1 /*
2  * Copyright (C) 2016 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 
17 package com.android.server.am;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import android.app.ActivityManager;
27 import android.app.ActivityManager.OnUidImportanceListener;
28 import android.app.ActivityManager.RecentTaskInfo;
29 import android.app.ActivityManager.RunningAppProcessInfo;
30 import android.app.IActivityManager;
31 import android.content.BroadcastReceiver;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.ServiceConnection;
37 import android.content.pm.PackageManager;
38 import android.os.Binder;
39 import android.os.Bundle;
40 import android.os.DropBoxManager;
41 import android.os.Handler;
42 import android.os.IBinder;
43 import android.os.IRemoteCallback;
44 import android.os.Looper;
45 import android.os.Message;
46 import android.os.Messenger;
47 import android.os.Parcel;
48 import android.os.RemoteException;
49 import android.os.SystemClock;
50 import android.os.UserHandle;
51 import android.platform.test.annotations.Presubmit;
52 import android.provider.DeviceConfig;
53 import android.provider.Settings;
54 import android.server.wm.settings.SettingsSession;
55 import android.support.test.uiautomator.UiDevice;
56 import android.test.suitebuilder.annotation.LargeTest;
57 import android.text.TextUtils;
58 import android.util.Log;
59 import android.util.Pair;
60 
61 import androidx.test.InstrumentationRegistry;
62 import androidx.test.filters.FlakyTest;
63 
64 import org.junit.Before;
65 import org.junit.Ignore;
66 import org.junit.Test;
67 
68 import java.io.IOException;
69 import java.util.ArrayList;
70 import java.util.List;
71 import java.util.concurrent.CountDownLatch;
72 import java.util.concurrent.TimeUnit;
73 import java.util.regex.Matcher;
74 import java.util.regex.Pattern;
75 import java.util.stream.Collectors;
76 
77 /**
78  * Tests for {@link ActivityManager}.
79  *
80  * Build/Install/Run:
81  *  atest FrameworksServicesTests:ActivityManagerTest
82  */
83 @FlakyTest(detail = "Promote to presubmit if stable")
84 @Presubmit
85 public class ActivityManagerTest {
86     private static final String TAG = "ActivityManagerTest";
87 
88     private static final String TEST_APP1 = "com.android.servicestests.apps.simpleservicetestapp1";
89     private static final String TEST_APP2 = "com.android.servicestests.apps.simpleservicetestapp2";
90     private static final String TEST_APP3 = "com.android.servicestests.apps.simpleservicetestapp3";
91     private static final String TEST_CLASS =
92             "com.android.servicestests.apps.simpleservicetestapp.SimpleService";
93     private static final int TEST_LOOPS = 100;
94     private static final long AWAIT_TIMEOUT = 2000;
95     private static final long CHECK_INTERVAL = 100;
96 
97     private static final String TEST_FGS_CLASS =
98             "com.android.servicestests.apps.simpleservicetestapp.SimpleFgService";
99     private static final String ACTION_FGS_STATS_TEST =
100             "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
101     private static final String EXTRA_MESSENGER = "extra_messenger";
102 
103     private static final String EXTRA_CALLBACK = "callback";
104     private static final String EXTRA_COMMAND = "command";
105     private static final String EXTRA_FLAGS = "flags";
106     private static final String EXTRA_TARGET_PACKAGE = "target_package";
107 
108     private static final int COMMAND_INVALID = 0;
109     private static final int COMMAND_EMPTY = 1;
110     private static final int COMMAND_BIND_SERVICE = 2;
111     private static final int COMMAND_UNBIND_SERVICE = 3;
112     private static final int COMMAND_STOP_SELF = 4;
113 
114     private static final String TEST_ISOLATED_CLASS =
115             "com.android.servicestests.apps.simpleservicetestapp.SimpleIsolatedService";
116 
117     private IActivityManager mService;
118     private IRemoteCallback mCallback;
119     private Context mContext;
120 
121     @Before
setUp()122     public void setUp() throws Exception {
123         mService = ActivityManager.getService();
124         mContext = InstrumentationRegistry.getTargetContext();
125     }
126 
127     @Test
testTaskIdsForRunningUsers()128     public void testTaskIdsForRunningUsers() throws RemoteException {
129         int[] runningUserIds = mService.getRunningUserIds();
130         assertThat(runningUserIds).isNotEmpty();
131         for (int userId : runningUserIds) {
132             testTaskIdsForUser(userId);
133         }
134     }
135 
testTaskIdsForUser(int userId)136     private void testTaskIdsForUser(int userId) throws RemoteException {
137         List<?> recentTasks = mService.getRecentTasks(100, 0, userId).getList();
138         if (recentTasks != null) {
139             for (Object elem : recentTasks) {
140                 assertThat(elem).isInstanceOf(RecentTaskInfo.class);
141                 RecentTaskInfo recentTask = (RecentTaskInfo) elem;
142                 int taskId = recentTask.taskId;
143                 assertEquals("The task id " + taskId + " should not belong to user " + userId,
144                              taskId / UserHandle.PER_USER_RANGE, userId);
145             }
146         }
147     }
148 
149     @Test
testServiceUnbindAndKilling()150     public void testServiceUnbindAndKilling() {
151         for (int i = TEST_LOOPS; i > 0; i--) {
152             runOnce(i);
153         }
154     }
155 
runOnce(long yieldDuration)156     private void runOnce(long yieldDuration) {
157         final PackageManager pm = mContext.getPackageManager();
158         int uid = 0;
159         try {
160             uid = pm.getPackageUid(TEST_APP1, 0);
161         } catch (PackageManager.NameNotFoundException e) {
162             throw new RuntimeException(e);
163         }
164 
165         Intent intent = new Intent();
166         intent.setClassName(TEST_APP1, TEST_CLASS);
167 
168         // Create a service connection with auto creation.
169         CountDownLatch latch = new CountDownLatch(1);
170         final MyServiceConnection autoConnection = new MyServiceConnection(latch);
171         mContext.bindService(intent, autoConnection, Context.BIND_AUTO_CREATE);
172         try {
173             assertTrue("Timeout to bind to service " + intent.getComponent(),
174                     latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
175         } catch (InterruptedException e) {
176             fail("Unable to bind to service " + intent.getComponent());
177         }
178 
179         // Create a service connection without any flags.
180         intent = new Intent();
181         intent.setClassName(TEST_APP1, TEST_CLASS);
182         latch = new CountDownLatch(1);
183         MyServiceConnection otherConnection = new MyServiceConnection(latch);
184         mContext.bindService(intent, otherConnection, 0);
185         try {
186             assertTrue("Timeout to bind to service " + intent.getComponent(),
187                     latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
188         } catch (InterruptedException e) {
189             fail("Unable to bind to service " + intent.getComponent());
190         }
191 
192         // Inform the remote process to kill itself
193         try {
194             mCallback.sendResult(null);
195             // It's basically a test for race condition, we expect the bringDownServiceLocked()
196             // would find out the hosting process is dead - to do this, technically we should
197             // do killing and unbinding simultaneously; but in reality, the killing would take
198             // a little while, before the signal really kills it; so we do it in the same thread,
199             // and even wait a while after sending killing signal.
200             Thread.sleep(yieldDuration);
201         } catch (RemoteException | InterruptedException e) {
202             fail("Unable to kill the process");
203         }
204         // Now unbind that auto connection, this should be equivalent to stopService
205         mContext.unbindService(autoConnection);
206 
207         // Now we don't expect the system_server crashes.
208 
209         // Wait for the target process dies
210         long total = 0;
211         for (; total < AWAIT_TIMEOUT; total += CHECK_INTERVAL) {
212             try {
213                 if (!targetPackageIsRunning(mContext, uid)) {
214                     break;
215                 }
216                 Thread.sleep(CHECK_INTERVAL);
217             } catch (InterruptedException e) {
218             }
219         }
220         assertTrue("Timeout to wait for the target package dies", total < AWAIT_TIMEOUT);
221         mCallback = null;
222     }
223 
targetPackageIsRunning(Context context, int uid)224     private boolean targetPackageIsRunning(Context context, int uid) {
225         final String result = runShellCommand(
226                 String.format("cmd activity get-uid-state %d", uid));
227         return !result.contains("(NONEXISTENT)");
228     }
229 
runShellCommand(String cmd)230     private static String runShellCommand(String cmd) {
231         try {
232             return UiDevice.getInstance(
233                     InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
234         } catch (IOException e) {
235             throw new RuntimeException(e);
236         }
237     }
238 
239     private class MyServiceConnection implements ServiceConnection {
240         private CountDownLatch mLatch;
241 
MyServiceConnection(CountDownLatch latch)242         MyServiceConnection(CountDownLatch latch) {
243             this.mLatch = latch;
244         }
245 
246         @Override
onServiceConnected(ComponentName name, IBinder service)247         public void onServiceConnected(ComponentName name, IBinder service) {
248             mCallback = IRemoteCallback.Stub.asInterface(service);
249             mLatch.countDown();
250         }
251 
252         @Override
onServiceDisconnected(ComponentName name)253         public void onServiceDisconnected(ComponentName name) {
254         }
255     }
256 
257     /**
258      * Note: This test actually only works in eng build. It'll always pass
259      * in user and userdebug build, because the expected exception won't be
260      * thrown in those builds.
261      */
262     @LargeTest
263     @Test
testFgsProcStatsTracker()264     public void testFgsProcStatsTracker() throws Exception {
265         final PackageManager pm = mContext.getPackageManager();
266         final long timeout = 5000;
267         int uid = pm.getPackageUid(TEST_APP1, 0);
268         final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
269         final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
270         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
271         final CountDownLatch[] latchHolder = new CountDownLatch[1];
272         final H handler = new H(Looper.getMainLooper(), latchHolder);
273         final Messenger messenger = new Messenger(handler);
274         final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
275         final CountDownLatch dboxLatch = new CountDownLatch(1);
276         final BroadcastReceiver receiver = new BroadcastReceiver() {
277             @Override
278             public void onReceive(Context context, Intent intent) {
279                 final String tag_wtf = "system_server_wtf";
280                 if (tag_wtf.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
281                     final DropBoxManager.Entry e = dbox.getNextEntry(tag_wtf, intent.getLongExtra(
282                             DropBoxManager.EXTRA_TIME, 0) - 1);
283                     final String text = e.getText(8192);
284                     if (TextUtils.isEmpty(text)) {
285                         return;
286                     }
287                     if (text.indexOf("can't store negative values") == -1) {
288                         return;
289                     }
290                     dboxLatch.countDown();
291                 }
292             }
293         };
294         try {
295             mContext.registerReceiver(receiver,
296                     new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
297             am.addOnUidImportanceListener(uidListener1,
298                     RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
299             am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
300             runShellCommand("cmd deviceidle whitelist +" + TEST_APP1);
301             toggleScreenOn(true);
302 
303             final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
304             final ComponentName cn = ComponentName.unflattenFromString(
305                     TEST_APP1 + "/" + TEST_FGS_CLASS);
306             final Bundle bundle = new Bundle();
307             intent.setComponent(cn);
308             bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
309             intent.putExtras(bundle);
310 
311             latchHolder[0] = new CountDownLatch(1);
312             mContext.startForegroundService(intent);
313             assertTrue("Timed out to start fg service", uidListener1.waitFor(
314                     RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, timeout));
315             assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
316                     timeout, TimeUnit.MILLISECONDS));
317 
318             Thread.sleep(timeout);
319             latchHolder[0] = new CountDownLatch(1);
320             handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
321             assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
322                     timeout, TimeUnit.MILLISECONDS));
323 
324             Thread.sleep(timeout);
325             latchHolder[0] = new CountDownLatch(1);
326             handler.sendRemoteMessage(H.MSG_START_FOREGROUND, 0, 0, null);
327             assertTrue("Timed out to wait for start fg", latchHolder[0].await(
328                     timeout, TimeUnit.MILLISECONDS));
329 
330             toggleScreenOn(false);
331             latchHolder[0] = new CountDownLatch(1);
332             handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
333             assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
334                     timeout, TimeUnit.MILLISECONDS));
335             assertFalse("There shouldn't be negative values", dboxLatch.await(
336                     timeout * 2, TimeUnit.MILLISECONDS));
337         } finally {
338             toggleScreenOn(true);
339             runShellCommand("cmd deviceidle whitelist -" + TEST_APP1);
340             am.removeOnUidImportanceListener(uidListener1);
341             am.removeOnUidImportanceListener(uidListener2);
342             am.forceStopPackage(TEST_APP1);
343             mContext.unregisterReceiver(receiver);
344         }
345     }
346 
347     @LargeTest
348     @Test
testAppFreezerWithAllowOomAdj()349     public void testAppFreezerWithAllowOomAdj() throws Exception {
350         final long waitFor = 5000;
351         boolean freezerWasEnabled = isFreezerEnabled();
352         SettingsSession<String> freezerEnabled = null;
353         SettingsSession<String> amConstantsSettings = null;
354         DeviceConfigSession<Long> freezerDebounceTimeout = null;
355         MyServiceConnection autoConnection = null;
356         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
357         final PackageManager pm = mContext.getPackageManager();
358         final int uid1 = pm.getPackageUid(TEST_APP1, 0);
359         final int uid2 = pm.getPackageUid(TEST_APP2, 0);
360         final MyUidImportanceListener uid1Listener = new MyUidImportanceListener(uid1);
361         final MyUidImportanceListener uid2Listener = new MyUidImportanceListener(uid2);
362         try {
363             if (!freezerWasEnabled) {
364                 freezerEnabled = new SettingsSession<>(
365                         Settings.Global.getUriFor(Settings.Global.CACHED_APPS_FREEZER_ENABLED),
366                         Settings.Global::getString, Settings.Global::putString);
367                 freezerEnabled.set("enabled");
368                 Thread.sleep(waitFor);
369                 if (!isFreezerEnabled()) {
370                     // Still not enabled? Probably because the device doesn't support it.
371                     return;
372                 }
373             }
374             freezerDebounceTimeout = new DeviceConfigSession<>(
375                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
376                     CachedAppOptimizer.KEY_FREEZER_DEBOUNCE_TIMEOUT,
377                     DeviceConfig::getLong, CachedAppOptimizer.DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
378             freezerDebounceTimeout.set(waitFor);
379 
380             final String activityManagerConstants = Settings.Global.ACTIVITY_MANAGER_CONSTANTS;
381             amConstantsSettings = new SettingsSession<>(
382                 Settings.Global.getUriFor(activityManagerConstants),
383                 Settings.Global::getString, Settings.Global::putString);
384 
385             amConstantsSettings.set(
386                     ActivityManagerConstants.KEY_MAX_SERVICE_INACTIVITY + "=" + waitFor);
387 
388             runShellCommand("cmd deviceidle whitelist +" + TEST_APP1);
389             runShellCommand("cmd deviceidle whitelist +" + TEST_APP2);
390 
391             am.addOnUidImportanceListener(uid1Listener,
392                     RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
393             am.addOnUidImportanceListener(uid2Listener, RunningAppProcessInfo.IMPORTANCE_CACHED);
394 
395             final Intent intent = new Intent();
396             intent.setClassName(TEST_APP1, TEST_CLASS);
397 
398             CountDownLatch latch = new CountDownLatch(1);
399             autoConnection = new MyServiceConnection(latch);
400             mContext.bindService(intent, autoConnection,
401                     Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
402             try {
403                 assertTrue("Timeout to bind to service " + intent.getComponent(),
404                         latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
405             } catch (InterruptedException e) {
406                 fail("Unable to bind to service " + intent.getComponent());
407             }
408             assertFalse(TEST_APP1 + " shouldn't be frozen now.", isAppFrozen(TEST_APP1));
409 
410             // Trigger oomAdjUpdate/
411             toggleScreenOn(false);
412             toggleScreenOn(true);
413 
414             // Wait for the freezer kick in if there is any.
415             Thread.sleep(waitFor * 4);
416 
417             // It still shouldn't be frozen, although it's been in cached state.
418             assertFalse(TEST_APP1 + " shouldn't be frozen now.", isAppFrozen(TEST_APP1));
419 
420             final CountDownLatch[] latchHolder = new CountDownLatch[1];
421             final IRemoteCallback callback = new IRemoteCallback.Stub() {
422                 @Override
423                 public void sendResult(Bundle bundle) {
424                     if (bundle != null) {
425                         latchHolder[0].countDown();
426                     }
427                 }
428             };
429 
430             // Bind from app1 to app2 without BIND_WAIVE_PRIORITY.
431             final Bundle extras = new Bundle();
432             extras.putBinder(EXTRA_CALLBACK, callback.asBinder());
433             latchHolder[0] = new CountDownLatch(1);
434             sendCommand(COMMAND_BIND_SERVICE, TEST_APP1, TEST_APP2, extras);
435             assertTrue("Timed out to bind to " + TEST_APP2, latchHolder[0].await(
436                     waitFor, TimeUnit.MILLISECONDS));
437 
438             // Stop service in app1
439             extras.clear();
440             sendCommand(COMMAND_STOP_SELF, TEST_APP1, TEST_APP1, extras);
441 
442             assertTrue(TEST_APP2 + " should be in cached", uid2Listener.waitFor(
443                     RunningAppProcessInfo.IMPORTANCE_CACHED, waitFor));
444 
445             // Wait for the freezer kick in if there is any.
446             Thread.sleep(waitFor * 4);
447 
448             // It still shouldn't be frozen, although it's been in cached state.
449             assertFalse(TEST_APP2 + " shouldn't be frozen now.", isAppFrozen(TEST_APP2));
450         } finally {
451             toggleScreenOn(true);
452             if (amConstantsSettings != null) {
453                 amConstantsSettings.close();
454             }
455             if (freezerEnabled != null) {
456                 freezerEnabled.close();
457             }
458             if (freezerDebounceTimeout != null) {
459                 freezerDebounceTimeout.close();
460             }
461             if (autoConnection != null) {
462                 mContext.unbindService(autoConnection);
463             }
464             am.removeOnUidImportanceListener(uid1Listener);
465             am.removeOnUidImportanceListener(uid2Listener);
466             sendCommand(COMMAND_UNBIND_SERVICE, TEST_APP1, TEST_APP2, null);
467             sendCommand(COMMAND_UNBIND_SERVICE, TEST_APP2, TEST_APP1, null);
468             runShellCommand("cmd deviceidle whitelist -" + TEST_APP1);
469             runShellCommand("cmd deviceidle whitelist -" + TEST_APP2);
470         }
471     }
472 
sendCommand(int command, String sourcePkg, String targetPkg, Bundle extras)473     private void sendCommand(int command, String sourcePkg, String targetPkg, Bundle extras) {
474         final Intent intent = new Intent();
475         intent.setClassName(sourcePkg, TEST_CLASS);
476         intent.putExtra(EXTRA_COMMAND, command);
477         intent.putExtra(EXTRA_TARGET_PACKAGE, targetPkg);
478         if (extras != null) {
479             intent.putExtras(extras);
480         }
481         mContext.startService(intent);
482     }
483 
isFreezerEnabled()484     private boolean isFreezerEnabled() throws Exception {
485         final String output = runShellCommand("dumpsys activity settings");
486         final Matcher matcher = Pattern.compile("\\b" + CachedAppOptimizer.KEY_USE_FREEZER
487                 + "\\b=\\b(true|false)\\b").matcher(output);
488         if (matcher.find()) {
489             return Boolean.parseBoolean(matcher.group(1));
490         }
491         return false;
492     }
493 
isAppFrozen(String packageName)494     private boolean isAppFrozen(String packageName) throws Exception {
495         final String output = runShellCommand("dumpsys activity p " + packageName);
496         final Matcher matcher = Pattern.compile("\\b" + ProcessCachedOptimizerRecord.IS_FROZEN
497                 + "\\b=\\b(true|false)\\b").matcher(output);
498         if (matcher.find()) {
499             return Boolean.parseBoolean(matcher.group(1));
500         }
501         return false;
502     }
503 
504     @Ignore("Need to disable calling uid check in ActivityManagerService.killPids before this test")
505     @Test
testKillPids()506     public void testKillPids() throws Exception {
507         final long timeout = 5000;
508         final long shortTimeout = 2000;
509         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
510         final PackageManager pm = mContext.getPackageManager();
511         final int uid1 = pm.getPackageUid(TEST_APP1, 0);
512         final int uid2 = pm.getPackageUid(TEST_APP2, 0);
513         final int uid3 = pm.getPackageUid(TEST_APP3, 0);
514         final MyUidImportanceListener uid1Listener1 = new MyUidImportanceListener(uid1);
515         final MyUidImportanceListener uid1Listener2 = new MyUidImportanceListener(uid1);
516         final MyUidImportanceListener uid2Listener1 = new MyUidImportanceListener(uid2);
517         final MyUidImportanceListener uid2Listener2 = new MyUidImportanceListener(uid2);
518         final MyUidImportanceListener uid3Listener1 = new MyUidImportanceListener(uid3);
519         final MyUidImportanceListener uid3Listener2 = new MyUidImportanceListener(uid3);
520         try {
521             am.addOnUidImportanceListener(uid1Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
522             am.addOnUidImportanceListener(uid1Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
523             am.addOnUidImportanceListener(uid2Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
524             am.addOnUidImportanceListener(uid2Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
525             am.addOnUidImportanceListener(uid3Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
526             am.addOnUidImportanceListener(uid3Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
527             runShellCommand("cmd deviceidle whitelist +" + TEST_APP1);
528             runShellCommand("cmd deviceidle whitelist +" + TEST_APP2);
529             runShellCommand("cmd deviceidle whitelist +" + TEST_APP3);
530             final int[] pids = new int[3];
531             // Test sync kills
532             pids[0] = startTargetService(am, TEST_APP1, TEST_CLASS, uid1, TEST_APP1,
533                     uid1Listener1, timeout);
534             pids[1] = startTargetService(am, TEST_APP2, TEST_CLASS, uid2, TEST_APP2,
535                     uid2Listener1, timeout);
536             pids[2] = startTargetService(am, TEST_APP3, TEST_CLASS, uid3, TEST_APP3,
537                     uid3Listener1, timeout);
538             Thread.sleep(shortTimeout);
539             mService.killPids(pids, "testKillPids", false);
540             assertTrue("Timed out to kill process", uid1Listener2.waitFor(
541                     RunningAppProcessInfo.IMPORTANCE_GONE, timeout));
542             assertTrue("Timed out to kill process", uid2Listener2.waitFor(
543                     RunningAppProcessInfo.IMPORTANCE_GONE, timeout));
544             assertTrue("Timed out to kill process", uid3Listener2.waitFor(
545                     RunningAppProcessInfo.IMPORTANCE_GONE, timeout));
546         } finally {
547             runShellCommand("cmd deviceidle whitelist -" + TEST_APP1);
548             runShellCommand("cmd deviceidle whitelist -" + TEST_APP2);
549             runShellCommand("cmd deviceidle whitelist -" + TEST_APP3);
550             am.removeOnUidImportanceListener(uid1Listener1);
551             am.removeOnUidImportanceListener(uid1Listener2);
552             am.removeOnUidImportanceListener(uid2Listener1);
553             am.removeOnUidImportanceListener(uid2Listener2);
554             am.removeOnUidImportanceListener(uid3Listener1);
555             am.removeOnUidImportanceListener(uid3Listener2);
556             am.forceStopPackage(TEST_APP1);
557             am.forceStopPackage(TEST_APP2);
558             am.forceStopPackage(TEST_APP3);
559         }
560     }
561 
startTargetService(ActivityManager am, String targetPakage, String targetService, int targetUid, String targetProcessName, MyUidImportanceListener uidListener, long timeout)562     private int startTargetService(ActivityManager am, String targetPakage, String targetService,
563             int targetUid, String targetProcessName, MyUidImportanceListener uidListener,
564             long timeout) throws Exception {
565         final Intent intent = new Intent();
566         intent.setComponent(ComponentName.unflattenFromString(targetPakage + "/" + targetService));
567         mContext.startService(intent);
568         assertTrue("Timed out to start service", uidListener.waitFor(
569                 RunningAppProcessInfo.IMPORTANCE_SERVICE, timeout));
570         final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
571         for (int i = processes.size() - 1; i >= 0; i--) {
572             final RunningAppProcessInfo info = processes.get(i);
573             if (info.uid == targetUid && targetProcessName.equals(info.processName)) {
574                 return info.pid;
575             }
576         }
577         return -1;
578     }
579 
580     @Test
testGetIsolatedProcesses()581     public void testGetIsolatedProcesses() throws Exception {
582         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
583         final PackageManager pm = mContext.getPackageManager();
584         final int uid1 = pm.getPackageUid(TEST_APP1, 0);
585         final int uid2 = pm.getPackageUid(TEST_APP2, 0);
586         final int uid3 = pm.getPackageUid(TEST_APP3, 0);
587         final List<Pair<Integer, ServiceConnection>> uid1Processes = new ArrayList<>();
588         final List<Pair<Integer, ServiceConnection>> uid2Processes = new ArrayList<>();
589         try {
590             assertTrue("There shouldn't be any isolated process for " + TEST_APP1,
591                     getIsolatedProcesses(uid1).isEmpty());
592             assertTrue("There shouldn't be any isolated process for " + TEST_APP2,
593                     getIsolatedProcesses(uid2).isEmpty());
594             assertTrue("There shouldn't be any isolated process for " + TEST_APP3,
595                     getIsolatedProcesses(uid3).isEmpty());
596 
597             // Verify uid1
598             uid1Processes.add(createIsolatedProcessAndVerify(TEST_APP1, uid1));
599             uid1Processes.add(createIsolatedProcessAndVerify(TEST_APP1, uid1));
600             uid1Processes.add(createIsolatedProcessAndVerify(TEST_APP1, uid1));
601             verifyIsolatedProcesses(uid1Processes, getIsolatedProcesses(uid1));
602 
603             // Let one of the processes go
604             final Pair<Integer, ServiceConnection> uid1P2 = uid1Processes.remove(2);
605             mContext.unbindService(uid1P2.second);
606             Thread.sleep(5_000); // Wait for the process gone.
607             verifyIsolatedProcesses(uid1Processes, getIsolatedProcesses(uid1));
608 
609             // Verify uid2
610             uid2Processes.add(createIsolatedProcessAndVerify(TEST_APP2, uid2));
611             verifyIsolatedProcesses(uid2Processes, getIsolatedProcesses(uid2));
612 
613             // Verify uid1 again
614             verifyIsolatedProcesses(uid1Processes, getIsolatedProcesses(uid1));
615 
616             // Verify uid3
617             assertTrue("There shouldn't be any isolated process for " + TEST_APP3,
618                     getIsolatedProcesses(uid3).isEmpty());
619         } finally {
620             for (Pair<Integer, ServiceConnection> p: uid1Processes) {
621                 mContext.unbindService(p.second);
622             }
623             for (Pair<Integer, ServiceConnection> p: uid2Processes) {
624                 mContext.unbindService(p.second);
625             }
626             am.forceStopPackage(TEST_APP1);
627             am.forceStopPackage(TEST_APP2);
628             am.forceStopPackage(TEST_APP3);
629         }
630     }
631 
getIsolatedProcesses(int uid)632     private static List<Integer> getIsolatedProcesses(int uid) throws Exception {
633         final String output = runShellCommand("am get-isolated-pids " + uid);
634         final Matcher matcher = Pattern.compile("(\\d+)").matcher(output);
635         final List<Integer> pids = new ArrayList<>();
636         while (matcher.find()) {
637             pids.add(Integer.parseInt(output.substring(matcher.start(), matcher.end())));
638         }
639         return pids;
640     }
641 
verifyIsolatedProcesses(List<Pair<Integer, ServiceConnection>> processes, List<Integer> pids)642     private void verifyIsolatedProcesses(List<Pair<Integer, ServiceConnection>> processes,
643             List<Integer> pids) {
644         final List<Integer> l = processes.stream().map(p -> p.first).collect(Collectors.toList());
645         assertTrue("Isolated processes don't match", l.containsAll(pids));
646         assertTrue("Isolated processes don't match", pids.containsAll(l));
647     }
648 
createIsolatedProcessAndVerify(String pkgName, int uid)649     private Pair<Integer, ServiceConnection> createIsolatedProcessAndVerify(String pkgName, int uid)
650             throws Exception {
651         final Pair<Integer, ServiceConnection> p = createIsolatedProcess(pkgName);
652         final List<Integer> pids = getIsolatedProcesses(uid);
653         assertTrue("Can't find the isolated pid " + p.first + " for " + pkgName,
654                 pids.contains(p.first));
655         return p;
656     }
657 
createIsolatedProcess(String pkgName)658     private Pair<Integer, ServiceConnection> createIsolatedProcess(String pkgName)
659             throws Exception {
660         final int[] pid = new int[1];
661         final CountDownLatch[] latch = new CountDownLatch[1];
662         final ServiceConnection conn = new ServiceConnection() {
663             @Override
664             public void onServiceConnected(ComponentName name, IBinder service) {
665                 final IRemoteCallback s = IRemoteCallback.Stub.asInterface(service);
666                 final IBinder callback = new Binder() {
667                     @Override
668                     protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
669                             throws RemoteException {
670                         if (code == Binder.FIRST_CALL_TRANSACTION) {
671                             pid[0] = data.readInt();
672                             latch[0].countDown();
673                             return true;
674                         }
675                         return super.onTransact(code, data, reply, flags);
676                     }
677                 };
678                 try {
679                     final Bundle extra = new Bundle();
680                     extra.putBinder(EXTRA_CALLBACK, callback);
681                     s.sendResult(extra);
682                 } catch (RemoteException e) {
683                     fail("Unable to call into isolated process");
684                 }
685             }
686             @Override
687             public void onServiceDisconnected(ComponentName name) {
688             }
689         };
690         final Intent intent = new Intent();
691         intent.setClassName(pkgName, TEST_ISOLATED_CLASS);
692         latch[0] = new CountDownLatch(1);
693         assertTrue("Unable to create isolated process in " + pkgName,
694                 mContext.bindIsolatedService(intent, Context.BIND_AUTO_CREATE,
695                 Long.toString(SystemClock.uptimeMillis()), mContext.getMainExecutor(), conn));
696         assertTrue("Timeout to bind to service " + intent.getComponent(),
697                 latch[0].await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
698         return Pair.create(pid[0], conn);
699     }
700 
701     /**
702      * Make sure the screen state.
703      */
toggleScreenOn(final boolean screenon)704     private void toggleScreenOn(final boolean screenon) throws Exception {
705         if (screenon) {
706             runShellCommand("input keyevent KEYCODE_WAKEUP");
707             runShellCommand("wm dismiss-keyguard");
708         } else {
709             runShellCommand("input keyevent KEYCODE_SLEEP");
710         }
711         // Since the screen on/off intent is ordered, they will not be sent right now.
712         Thread.sleep(2_000);
713     }
714 
715     private class H extends Handler {
716         static final int MSG_INIT = 0;
717         static final int MSG_DONE = 1;
718         static final int MSG_START_FOREGROUND = 2;
719         static final int MSG_STOP_FOREGROUND = 3;
720 
721         private Messenger mRemoteMessenger;
722         private CountDownLatch[] mLatchHolder;
723 
H(Looper looper, CountDownLatch[] latchHolder)724         H(Looper looper, CountDownLatch[] latchHolder) {
725             super(looper);
726             mLatchHolder = latchHolder;
727         }
728 
729         @Override
handleMessage(Message msg)730         public void handleMessage(Message msg) {
731             switch (msg.what) {
732                 case MSG_INIT:
733                     mRemoteMessenger = (Messenger) msg.obj;
734                     mLatchHolder[0].countDown();
735                     break;
736                 case MSG_DONE:
737                     mLatchHolder[0].countDown();
738                     break;
739             }
740         }
741 
sendRemoteMessage(int what, int arg1, int arg2, Object obj)742         void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
743             Message msg = Message.obtain();
744             msg.what = what;
745             msg.arg1 = arg1;
746             msg.arg2 = arg2;
747             msg.obj = obj;
748             try {
749                 mRemoteMessenger.send(msg);
750             } catch (RemoteException e) {
751             }
752             msg.recycle();
753         }
754     }
755 
756     private static class MyUidImportanceListener implements OnUidImportanceListener {
757         final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
758         private final int mExpectedUid;
759         private int mExpectedImportance;
760         private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
761 
MyUidImportanceListener(int uid)762         MyUidImportanceListener(int uid) {
763             mExpectedUid = uid;
764         }
765 
766         @Override
onUidImportance(int uid, int importance)767         public void onUidImportance(int uid, int importance) {
768             if (uid == mExpectedUid) {
769                 synchronized (this) {
770                     if (importance == mExpectedImportance && mLatchHolder[0] != null) {
771                         mLatchHolder[0].countDown();
772                     }
773                     mCurrentImportance = importance;
774                 }
775                 Log.i(TAG, "uid " + uid + " importance: " + importance);
776             }
777         }
778 
waitFor(int expectedImportance, long timeout)779         boolean waitFor(int expectedImportance, long timeout) throws Exception {
780             synchronized (this) {
781                 mExpectedImportance = expectedImportance;
782                 if (mCurrentImportance == expectedImportance) {
783                     return true;
784                 }
785                 mLatchHolder[0] = new CountDownLatch(1);
786             }
787             return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
788         }
789     }
790 }
791