1 /*
2  * Copyright (C) 2023 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.getbindinguidimportance;
17 
18 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
20 import static android.app.stubs.shared.Shared_getBindingUidImportance.ACTION_TEST_PROVIDER;
21 import static android.app.stubs.shared.Shared_getBindingUidImportance.ACTION_TEST_SERVICE_BINDING;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotEquals;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 
28 import android.app.ActivityManager;
29 import android.app.Flags;
30 import android.app.Service;
31 import android.content.BroadcastReceiver;
32 import android.content.ComponentName;
33 import android.content.ContentProvider;
34 import android.content.ContentValues;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.database.Cursor;
38 import android.database.MatrixCursor;
39 import android.net.Uri;
40 import android.os.Binder;
41 import android.os.Handler;
42 import android.os.IBinder;
43 import android.os.Looper;
44 import android.os.Process;
45 import android.platform.test.annotations.RequiresFlagsEnabled;
46 import android.platform.test.flag.junit.CheckFlagsRule;
47 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
48 import android.util.Log;
49 
50 import androidx.test.ext.junit.runners.AndroidJUnit4;
51 import androidx.test.platform.app.InstrumentationRegistry;
52 
53 import com.android.compatibility.common.util.ShellIdentityUtils;
54 import com.android.compatibility.common.util.ShellUtils;
55 import com.android.compatibility.common.util.TestUtils;
56 
57 import org.junit.Before;
58 import org.junit.BeforeClass;
59 import org.junit.Rule;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 
63 import java.util.concurrent.CountDownLatch;
64 import java.util.concurrent.TimeUnit;
65 
66 @RunWith(AndroidJUnit4.class)
67 public class ActivityManager_getBindingUidImportanceTest {
68     private static final String TAG = "ActivityManager_getBindingUidImportanceTest";
69 
70     @Rule
71     public final CheckFlagsRule mCheckFlagsRule =
72             DeviceFlagsValueProvider.createCheckFlagsRule();
73 
74     private static final String HELPER_PACKAGE = "android.app.stubs";
75     private static final ComponentName HELPER_RECEIVER = new ComponentName(
76             HELPER_PACKAGE, "android.app.stubs.Receiver_getBindingUidImportance");
77 
78     private static final int RECEIVER_TIMEOUT_SEC = 60 * 3;
79 
80     private static final int MY_UID = Process.myUid();
81     private static final int NONEXISTENT_UID = -1;
82     private static int sHelperUid;
83 
84     private static Context sContext;
85 
86     private static int sLastUidImportance;
87 
resetLastImportance()88     private static void resetLastImportance() {
89         sLastUidImportance = -1;
90     }
91 
92     @BeforeClass
beforeClass()93     public static void beforeClass() throws Exception {
94         sContext = InstrumentationRegistry.getInstrumentation().getContext();
95 
96         sHelperUid = sContext.getPackageManager().getPackageUid(HELPER_PACKAGE, 0);
97 
98         assertNotEquals("Helper UID must be different from mine", sHelperUid, MY_UID);
99         Log.d(TAG, "Helper UID: " + sHelperUid);
100     }
101 
102     @Before
setUp()103     public void setUp() {
104         // Force-stop the helper app to make sure there's no leftover binding, etc.
105         ShellUtils.runShellCommand("am force-stop %s", HELPER_PACKAGE);
106 
107         resetLastImportance();
108     }
109 
110     /**
111      * Test: get the importance of an UID that's binding to this process.
112      *
113      * The API should return the actual importance.
114      */
115     // Add the flag annotation
116     @Test
117     @RequiresFlagsEnabled(Flags.FLAG_GET_BINDING_UID_IMPORTANCE)
testWithServiceBinding()118     public void testWithServiceBinding() throws Exception {
119         sendBroadcastAndWait(ACTION_TEST_SERVICE_BINDING);
120 
121         // Because we call getBindingUidImportance() when the UID in running a receiver,
122         // the importance shouldn't be cached.
123         assertImportanceNotCached(sLastUidImportance);
124 
125         // Now the binding is disconnected, it should return GONE.
126         resetLastImportance();
127         assertImportanceGone(getBindingUidImportance(sContext, sHelperUid));
128     }
129 
130     /**
131      * Test: get the importance of an UID that's accessing a provider in this process.
132      *
133      * The API should return the actual importance.
134      */
135     // Add the flag annotation
136     @Test
137     @RequiresFlagsEnabled(Flags.FLAG_GET_BINDING_UID_IMPORTANCE)
testWithProviderAccess()138     public void testWithProviderAccess() throws Exception {
139         sendBroadcastAndWait(ACTION_TEST_PROVIDER);
140 
141         // Because we call getBindingUidImportance() when the UID in running a receiver,
142         // the importance shouldn't be cached.
143         assertImportanceNotCached(sLastUidImportance);
144 
145         // Now the provider client is closed, it should return GONE.
146         // But the provider connection isn't disconnected right away; there's some timeout,
147         // so we account for that with waitUntil.
148         resetLastImportance();
149         TestUtils.waitUntil("UID importance should become GONE", 60, () -> {
150             return getBindingUidImportance(sContext, sHelperUid) == IMPORTANCE_GONE;
151         });
152     }
153 
154     /**
155      * Test: try to get the importance of an UID that's not binding and not accessing the provider.
156      * It should return GONE.
157      */
158     // Add the flag annotation
159     @Test
160     @RequiresFlagsEnabled(Flags.FLAG_GET_BINDING_UID_IMPORTANCE)
testNoBindingNoProviderAccess()161     public void testNoBindingNoProviderAccess() {
162         assertImportanceGone(getBindingUidImportance(sContext, sHelperUid));
163     }
164 
165     /**
166      * Test: try to get the importance of an UID that doesn't exist.
167      * It should return GONE.
168      */
169     // Add the flag annotation
170     @Test
171     @RequiresFlagsEnabled(Flags.FLAG_GET_BINDING_UID_IMPORTANCE)
testNonExistingUid()172     public void testNonExistingUid() {
173         assertImportanceGone(getBindingUidImportance(sContext, NONEXISTENT_UID));
174     }
175 
176     /**
177      * Getting the importance of own UID is always allowed.
178      */
179     // Add the flag annotation
180     @Test
181     @RequiresFlagsEnabled(Flags.FLAG_GET_BINDING_UID_IMPORTANCE)
testSelfImportance()182     public void testSelfImportance() {
183         assertImportanceNotCached(getBindingUidImportance(sContext, MY_UID));
184     }
185 
assertImportanceNotCached(int importance)186     private static void assertImportanceNotCached(int importance) {
187         if (importance < 0) {
188             fail("importance is negative. getBindingUidImportance() wasn't called?");
189         }
190         if (importance > IMPORTANCE_SERVICE) {
191             fail("Importance should be non cached, but was " + importance);
192         }
193     }
194 
assertImportanceGone(int importance)195     public static void assertImportanceGone(int importance) {
196         assertEquals("Importance should seem to be GONE", importance, IMPORTANCE_GONE);
197     }
198 
199     /**
200      * Call ActivityManager.getBindingUidImportance() with the required permission, and
201      * return the result. It also updates {@link #sLastUidImportance}.
202      * @return the importance
203      */
getBindingUidImportance(Context context, int uid)204     private static int getBindingUidImportance(Context context, int uid) {
205         sLastUidImportance = ShellIdentityUtils.invokeMethodWithShellPermissions(
206                 context.getSystemService(ActivityManager.class),
207                 (am) -> am.getBindingUidImportance(uid),
208                 android.Manifest.permission.GET_BINDING_UID_IMPORTANCE);
209         Log.d(TAG, "Importance of UID " + uid + " is " + sLastUidImportance);
210         return sLastUidImportance;
211     }
212 
sendBroadcastAndWait(String action)213     private void sendBroadcastAndWait(String action) throws Exception {
214         final CountDownLatch latch = new CountDownLatch(1);
215 
216         final BroadcastReceiver resultReceiver = new BroadcastReceiver() {
217             @Override
218             public void onReceive(Context context, Intent intent) {
219                 Log.d(TAG, "Result received");
220                 latch.countDown();
221             }
222         };
223 
224         sContext.sendOrderedBroadcast(
225                 new Intent(action).setComponent(HELPER_RECEIVER),
226                 null, // receiverPermission
227                 resultReceiver,
228                 new Handler(Looper.getMainLooper()), // scheduler
229                 0, // initialCode
230                 null, // initialData
231                 null); // initialExtras
232 
233         assertTrue("Receiver didn't finish in time",
234                 latch.await(RECEIVER_TIMEOUT_SEC, TimeUnit.SECONDS));
235     }
236 
237     public static class MyService extends Service {
238         @Override
onBind(Intent intent)239         public IBinder onBind(Intent intent) {
240             Log.d(TAG, "MyService.onBind");
241 
242             // Update sLastUidImportance.
243             getBindingUidImportance(sContext, sHelperUid);
244             return new Binder();
245         }
246     }
247 
248     public static class MyProvider extends ContentProvider {
249         @Override
onCreate()250         public boolean onCreate() {
251             return true;
252         }
253 
254         @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)255         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
256                 String sortOrder) {
257             Log.d(TAG, "MyProvider.query");
258 
259             // Update sLastUidImportance.
260             getBindingUidImportance(sContext, sHelperUid);
261 
262             // Return a non-null cursor.
263             return new MatrixCursor(new String[]{"xxx"}, 0);
264         }
265 
266         @Override
getType(Uri uri)267         public String getType(Uri uri) {
268             return null;
269         }
270 
271         @Override
insert(Uri uri, ContentValues values)272         public Uri insert(Uri uri, ContentValues values) {
273             return null;
274         }
275 
276         @Override
delete(Uri uri, String selection, String[] selectionArgs)277         public int delete(Uri uri, String selection, String[] selectionArgs) {
278             return 0;
279         }
280 
281         @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)282         public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
283             return 0;
284         }
285     }
286 }
287