1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.app.cts.backgroundrestrictions;
17 
18 import static junit.framework.Assert.assertFalse;
19 
20 import static org.junit.Assert.assertTrue;
21 
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.platform.test.annotations.AppModeFull;
27 import android.util.Log;
28 
29 import androidx.test.InstrumentationRegistry;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import com.android.compatibility.common.util.AmUtils;
33 import com.android.compatibility.common.util.CddTest;
34 import com.android.compatibility.common.util.SystemUtil;
35 
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 import java.util.ArrayList;
40 import java.util.concurrent.CountDownLatch;
41 import java.util.concurrent.TimeUnit;
42 import java.util.concurrent.atomic.AtomicReference;
43 import java.util.function.BiConsumer;
44 import java.util.function.Consumer;
45 
46 @RunWith(AndroidJUnit4.class)
47 public class BroadcastsTest {
48     private static final String TAG = "BroadcastsTest";
49 
50     private final int BROADCASTS_TIMEOUT_SECOND = 3 * 60;
51 
getContext()52     private static Context getContext() {
53         return InstrumentationRegistry.getContext();
54     }
55 
56     /**
57      * Make sure "com.android.launcher.action.INSTALL_SHORTCUT" won't be delivered to a runtime
58      * receiver.
59      */
60     @Test
61     @CddTest(requirement="3.5/C-0-6")
testNonSupportedBroadcastsNotDelivered_runtimeReceiver()62     public void testNonSupportedBroadcastsNotDelivered_runtimeReceiver() throws Exception {
63 
64         // Need a reference here to initialize it in a lambda.
65         final AtomicReference<BroadcastReceiver> receiverRef = new AtomicReference<>();
66 
67         testNonSupportedBroadcastsNotDelivered(
68                 (filter, callback) -> {
69                     final BroadcastReceiver receiver = new BroadcastReceiver() {
70                         @Override
71                         public void onReceive(Context context, Intent intent) {
72                             callback.accept(intent);
73                         }
74                     };
75                     receiverRef.set(receiver);
76 
77                     getContext().registerReceiver(receiver, filter);
78                 },
79                 (intent) -> {},
80                 () -> getContext().unregisterReceiver(receiverRef.get()));
81     }
82 
83     /**
84      * Make sure "com.android.launcher.action.INSTALL_SHORTCUT" won't be delivered to a manifest
85      * receiver, even if an intent is targeted to the component.
86      */
87     @AppModeFull(reason = "Instant apps don't get to run in the background.")
88     @Test
89     @CddTest(requirement="3.5/C-0-6")
testNonSupportedBroadcastsNotDelivered_manifestReceiver()90     public void testNonSupportedBroadcastsNotDelivered_manifestReceiver() throws Exception {
91         // Need a reference here to initialize it in a lambda.
92         final AtomicReference<BroadcastReceiver> receiverRef = new AtomicReference<>();
93 
94         testNonSupportedBroadcastsNotDelivered(
95                 (filter, callback) -> {
96                     MyReceiver.setCallback((intent) -> callback.accept(intent));
97                 },
98                 (intent) -> intent.setComponent(MyReceiver.getComponent()),
99                 () -> MyReceiver.clearCallback());
100     }
101 
testNonSupportedBroadcastsNotDelivered( BiConsumer<IntentFilter, Consumer<Intent>> receiverInitializer, Consumer<Intent> intentInitializer, Runnable receiverDeinitializer)102     private void testNonSupportedBroadcastsNotDelivered(
103             BiConsumer<IntentFilter, Consumer<Intent>> receiverInitializer,
104             Consumer<Intent> intentInitializer,
105             Runnable receiverDeinitializer) throws Exception {
106 
107         AmUtils.waitForBroadcastIdle();
108 
109         // This broadcast should not be delivered.
110         final String[] UNSUPPORTED_BROADCASTS = new String[]{
111                 "com.android.launcher.action.INSTALL_SHORTCUT",
112         };
113         // These broadcasts should be delivered.
114         final String[] SUPPORTED_BROADCASTS = new String[]{
115                 Intent.ACTION_VIEW,
116                 Intent.ACTION_SEND,
117         };
118         final String[][] ALL_BROADCASTS = new String[][]{
119                 UNSUPPORTED_BROADCASTS,
120                 SUPPORTED_BROADCASTS,
121         };
122 
123         // GuardedBy receivedBroadcasts
124         final ArrayList<String> receivedBroadcasts = new ArrayList<>();
125 
126         final CountDownLatch latch = new CountDownLatch(SUPPORTED_BROADCASTS.length);
127 
128         // Register a receiver for all the actions.
129         final IntentFilter filter = new IntentFilter();
130         for (String[] list : ALL_BROADCASTS) {
131             for (String action : list) {
132                 filter.addAction(action);
133             }
134         }
135 
136         // This is what's called when a receiver receives an intent.
137         final Consumer<Intent> callbackHandler = (intent) -> {
138             Log.i(TAG, "Intent " + intent + " received.");
139             synchronized (receivedBroadcasts) {
140                 receivedBroadcasts.add(intent.getAction());
141             }
142 
143             latch.countDown();
144         };
145 
146         receiverInitializer.accept(filter, callbackHandler);
147         try {
148             // Send all broadcasts one by one.
149             for (String[] list : ALL_BROADCASTS) {
150                 for (String action : list) {
151                     final Intent intent = new Intent(action)
152                         .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
153 
154                     intentInitializer.accept(intent);
155 
156                     getContext().sendBroadcast(intent);
157                 }
158             }
159             assertTrue(latch.await(BROADCASTS_TIMEOUT_SECOND, TimeUnit.SECONDS));
160 
161             final String history =
162                     SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
163                     "dumpsys activity broadcasts history");
164             Log.v(TAG, "Broadcast history:\n");
165             for (String line : history.split("\n")) {
166                 Log.v(TAG, line);
167             }
168 
169 
170             // Verify the received lists.
171             // The supported ones should be delivered, and show up in the history.
172             // The unsupported should not.
173             synchronized (receivedBroadcasts) {
174                 for (String action : SUPPORTED_BROADCASTS) {
175                     assertTrue(receivedBroadcasts.contains(action));
176                     assertTrue(history.contains(action));
177                 }
178 
179                 for (String action : UNSUPPORTED_BROADCASTS) {
180                     assertFalse(receivedBroadcasts.contains(action));
181                     assertFalse(history.contains(action));
182                 }
183             }
184 
185         } finally {
186             receiverDeinitializer.run();
187         }
188     }
189 }
190