| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.app.cts; |
| |
| import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; |
| import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT; |
| import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; |
| import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; |
| import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; |
| import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; |
| import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; |
| import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; |
| import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; |
| |
| import android.accessibilityservice.AccessibilityService; |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.AppOpsManager; |
| import android.app.Instrumentation; |
| import android.app.cts.android.app.cts.tools.ServiceConnectionHandler; |
| import android.app.cts.android.app.cts.tools.ServiceProcessController; |
| import android.app.cts.android.app.cts.tools.SyncOrderedBroadcast; |
| import android.app.cts.android.app.cts.tools.UidImportanceListener; |
| import android.app.cts.android.app.cts.tools.WaitForBroadcast; |
| import android.app.cts.android.app.cts.tools.WatchUidRunner; |
| import android.app.stubs.CommandReceiver; |
| import android.app.stubs.LocalForegroundServiceLocation; |
| import android.app.stubs.ScreenOnActivity; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ServiceInfo; |
| import android.content.res.Configuration; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.Parcel; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.permission.cts.PermissionUtils; |
| import android.server.wm.WindowManagerState; |
| import android.support.test.uiautomator.BySelector; |
| import android.support.test.uiautomator.UiDevice; |
| import android.support.test.uiautomator.UiSelector; |
| import android.test.InstrumentationTestCase; |
| import android.util.Log; |
| import android.view.accessibility.AccessibilityEvent; |
| |
| import com.android.compatibility.common.util.SystemUtil; |
| |
| public class ActivityManagerProcessStateTest extends InstrumentationTestCase { |
| private static final String TAG = ActivityManagerProcessStateTest.class.getName(); |
| |
| private static final String STUB_PACKAGE_NAME = "android.app.stubs"; |
| private static final String PACKAGE_NAME_APP1 = "com.android.app1"; |
| private static final String PACKAGE_NAME_APP2 = "com.android.app2"; |
| private static final String PACKAGE_NAME_APP3 = "com.android.app3"; |
| |
| private static final String[] PACKAGE_NAMES = { |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, PACKAGE_NAME_APP3 |
| }; |
| |
| private static final int WAIT_TIME = 10000; |
| private static final int WAITFOR_MSEC = 10000; |
| // A secondary test activity from another APK. |
| static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp"; |
| static final String SIMPLE_SERVICE = ".SimpleService"; |
| static final String SIMPLE_SERVICE2 = ".SimpleService2"; |
| static final String SIMPLE_SERVICE3 = ".SimpleService3"; |
| static final String SIMPLE_RECEIVER_START_SERVICE = ".SimpleReceiverStartService"; |
| static final String SIMPLE_ACTIVITY_START_SERVICE = ".SimpleActivityStartService"; |
| static final String SIMPLE_ACTIVITY_START_FG_SERVICE = ".SimpleActivityStartFgService"; |
| public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT = |
| "com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT"; |
| static final String ACTION_SIMPLE_ACTIVITY_START_FG = |
| "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.START_THEN_FG"; |
| public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT = |
| "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.NOW_FOREGROUND"; |
| public static String ACTION_FINISH_EVERYTHING = |
| "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.FINISH_ALL"; |
| |
| // APKs for testing heavy weight app interactions. |
| static final String CANT_SAVE_STATE_1_PACKAGE_NAME = "com.android.test.cantsavestate1"; |
| static final String CANT_SAVE_STATE_2_PACKAGE_NAME = "com.android.test.cantsavestate2"; |
| |
| // Actions |
| static final String ACTION_START_FOREGROUND = "com.android.test.action.START_FOREGROUND"; |
| static final String ACTION_STOP_FOREGROUND = "com.android.test.action.STOP_FOREGROUND"; |
| static final String ACTION_START_THEN_FG = "com.android.test.action.START_THEN_FG"; |
| static final String ACTION_STOP_SERVICE = "com.android.test.action.STOP"; |
| |
| private static final int TEMP_WHITELIST_DURATION_MS = 2000; |
| |
| private Context mContext; |
| private Context mTargetContext; |
| private Instrumentation mInstrumentation; |
| private Intent mServiceIntent; |
| private Intent mServiceStartForegroundIntent; |
| private Intent mServiceStopForegroundIntent; |
| private Intent mService2Intent; |
| private Intent mService3Intent; |
| private Intent mServiceStartForeground3Intent; |
| private Intent mMainProcess[]; |
| private Intent mAllProcesses[]; |
| |
| private int mAppCount; |
| private ApplicationInfo[] mAppInfo; |
| private WatchUidRunner[] mWatchers; |
| |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mInstrumentation = getInstrumentation(); |
| mContext = mInstrumentation.getContext(); |
| mTargetContext = mInstrumentation.getTargetContext(); |
| mServiceIntent = new Intent(); |
| mServiceIntent.setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE); |
| mServiceStartForegroundIntent = new Intent(mServiceIntent); |
| mServiceStartForegroundIntent.setAction(ACTION_START_FOREGROUND); |
| mServiceStopForegroundIntent = new Intent(mServiceIntent); |
| mServiceStopForegroundIntent.setAction(ACTION_STOP_FOREGROUND); |
| mService2Intent = new Intent() |
| .setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE2); |
| mService3Intent = new Intent() |
| .setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE3); |
| mMainProcess = new Intent[1]; |
| mMainProcess[0] = mServiceIntent; |
| mAllProcesses = new Intent[2]; |
| mAllProcesses[0] = mServiceIntent; |
| mAllProcesses[1] = mService2Intent; |
| mContext.stopService(mServiceIntent); |
| mContext.stopService(mService2Intent); |
| mContext.stopService(mService3Intent); |
| CtsAppTestUtils.turnScreenOn(mInstrumentation, mContext); |
| removeTestAppFromWhitelists(); |
| mAppCount = 0; |
| // Make sure we are in Home screen before starting the test |
| mInstrumentation.getUiAutomation().performGlobalAction( |
| AccessibilityService.GLOBAL_ACTION_HOME); |
| // Stop all the packages to avoid residual impact |
| final ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| for (int i = 0; i < PACKAGE_NAMES.length; i++) { |
| final String pkgName = PACKAGE_NAMES[i]; |
| SystemUtil.runWithShellPermissionIdentity(() -> { |
| am.forceStopPackage(pkgName); |
| }); |
| } |
| } |
| |
| /** |
| * Set up count app info objects and WatchUidRunners. |
| */ |
| private void setupWatchers(int count) throws Exception { |
| mAppCount = count; |
| mAppInfo = new ApplicationInfo[count]; |
| mWatchers = new WatchUidRunner[count]; |
| for (int i = 0; i < count; i++) { |
| mAppInfo[i] = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAMES[i], 0); |
| mWatchers[i] = new WatchUidRunner(mInstrumentation, mAppInfo[i].uid, |
| WAITFOR_MSEC); |
| } |
| } |
| |
| /** |
| * Finish all started WatchUidRunners. |
| */ |
| private void shutdownWatchers() throws Exception { |
| for (int i = 0; i < mAppCount; i++) { |
| mWatchers[i].finish(); |
| } |
| } |
| |
| private void removeTestAppFromWhitelists() throws Exception { |
| CtsAppTestUtils.executeShellCmd(mInstrumentation, |
| "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME); |
| CtsAppTestUtils.executeShellCmd(mInstrumentation, |
| "cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME); |
| } |
| |
| private void waitForAppFocus(String waitForApp, long waitTime) { |
| long waitUntil = SystemClock.elapsedRealtime() + waitTime; |
| while (true) { |
| WindowManagerState wms = new WindowManagerState(); |
| wms.computeState(); |
| String appName = wms.getFocusedApp(); |
| if (appName != null) { |
| ComponentName comp = ComponentName.unflattenFromString(appName); |
| if (waitForApp.equals(comp.getPackageName())) { |
| break; |
| } |
| } |
| if (SystemClock.elapsedRealtime() > waitUntil) { |
| throw new IllegalStateException("Timed out waiting for focus on app " |
| + waitForApp + ", last was " + appName); |
| } |
| Log.i(TAG, "Waiting for app focus, current: " + appName); |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| private void startActivityAndWaitForShow(final Intent intent) throws Exception { |
| mInstrumentation.getUiAutomation().executeAndWaitForEvent( |
| () -> { |
| try { |
| mTargetContext.startActivity(intent); |
| } catch (Exception e) { |
| fail("Cannot start activity: " + intent); |
| } |
| }, (AccessibilityEvent event) -> event.getEventType() |
| == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |
| , WAIT_TIME); |
| } |
| |
| private void maybeClick(UiDevice device, UiSelector sel) { |
| try { |
| device.findObject(sel).click(); |
| } catch (Throwable ignored) { |
| } |
| } |
| |
| private void maybeClick(UiDevice device, BySelector sel) { |
| try { |
| device.findObject(sel).click(); |
| } catch (Throwable ignored) { |
| } |
| } |
| |
| /** |
| * Test basic state changes as processes go up and down due to services running in them. |
| */ |
| public void testUidImportanceListener() throws Exception { |
| final Parcel data = Parcel.obtain(); |
| ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent, |
| WAIT_TIME); |
| ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent, |
| WAIT_TIME); |
| |
| ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| |
| ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( |
| SIMPLE_PACKAGE_NAME, 0); |
| UidImportanceListener uidForegroundListener = new UidImportanceListener(mContext, |
| appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, WAIT_TIME); |
| |
| PermissionUtils.revokePermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| boolean gotException = false; |
| try { |
| uidForegroundListener.register(); |
| } catch (SecurityException e) { |
| gotException = true; |
| } |
| assertTrue("Expected SecurityException thrown", gotException); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| /* |
| Log.d("XXXX", "Invoke: " + cmd); |
| Log.d("XXXX", "Result: " + result); |
| Log.d("XXXX", SystemUtil.runShellCommand(mInstrumentation, "dumpsys package " |
| + STUB_PACKAGE_NAME)); |
| */ |
| uidForegroundListener.register(); |
| |
| UidImportanceListener uidGoneListener = new UidImportanceListener(mContext, |
| appInfo.uid, IMPORTANCE_CACHED, WAIT_TIME); |
| uidGoneListener.register(); |
| |
| WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid, |
| WAIT_TIME); |
| |
| try { |
| // First kill the processes to start out in a stable state. |
| conn.bind(); |
| conn2.bind(); |
| IBinder service1 = conn.getServiceIBinder(); |
| IBinder service2 = conn2.getServiceIBinder(); |
| conn.unbind(); |
| conn2.unbind(); |
| try { |
| service1.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); |
| } catch (RemoteException e) { |
| } |
| try { |
| service2.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); |
| } catch (RemoteException e) { |
| } |
| service1 = service2 = null; |
| |
| // Wait for uid's processes to go away. |
| uidGoneListener.waitForValue(IMPORTANCE_GONE, IMPORTANCE_GONE); |
| assertEquals(IMPORTANCE_GONE, am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // And wait for the uid report to be gone. |
| uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null); |
| |
| // Now bind and see if we get told about the uid coming in to the foreground. |
| conn.bind(); |
| uidForegroundListener.waitForValue(IMPORTANCE_FOREGROUND, IMPORTANCE_VISIBLE); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // Also make sure the uid state reports are as expected. Wait for active because |
| // there may be some intermediate states as the process comes up. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Pull out the service IBinder for a kludy hack... |
| IBinder service = conn.getServiceIBinder(); |
| |
| // Now unbind and see if we get told about it going to the background. |
| conn.unbind(); |
| uidForegroundListener.waitForValue(IMPORTANCE_CACHED, IMPORTANCE_CACHED); |
| assertEquals(IMPORTANCE_CACHED, am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Now kill the process and see if we are told about it being gone. |
| try { |
| service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); |
| } catch (RemoteException e) { |
| // It is okay if it is already gone for some reason. |
| } |
| |
| uidGoneListener.waitForValue(IMPORTANCE_GONE, IMPORTANCE_GONE); |
| assertEquals(IMPORTANCE_GONE, am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| uidWatcher.expect(WatchUidRunner.CMD_GONE, null); |
| |
| // Now we are going to try different combinations of binding to two processes to |
| // see if they are correctly combined together for the app. |
| |
| // Bring up both services. |
| conn.bind(); |
| conn2.bind(); |
| uidForegroundListener.waitForValue(IMPORTANCE_FOREGROUND, IMPORTANCE_VISIBLE); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Bring down one service, app state should remain foreground. |
| conn2.unbind(); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // Bring down other service, app state should now be cached. (If the processes both |
| // actually get killed immediately, this is also not a correctly behaving system.) |
| conn.unbind(); |
| uidGoneListener.waitForValue(IMPORTANCE_CACHED, IMPORTANCE_CACHED); |
| assertEquals(IMPORTANCE_CACHED, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Bring up one service, this should be sufficient to become foreground. |
| conn2.bind(); |
| uidForegroundListener.waitForValue(IMPORTANCE_FOREGROUND, IMPORTANCE_VISIBLE); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Bring up other service, should remain foreground. |
| conn.bind(); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // Bring down one service, should remain foreground. |
| conn.unbind(); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // And bringing down other service should put us back to cached. |
| conn2.unbind(); |
| uidGoneListener.waitForValue(IMPORTANCE_CACHED, |
| IMPORTANCE_CACHED); |
| assertEquals(IMPORTANCE_CACHED, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| } finally { |
| data.recycle(); |
| uidWatcher.finish(); |
| uidForegroundListener.unregister(); |
| uidGoneListener.unregister(); |
| } |
| } |
| |
| /** |
| * Test that background check correctly prevents idle services from running but allows |
| * whitelisted apps to bypass the check. |
| */ |
| public void testBackgroundCheckService() throws Exception { |
| final Parcel data = Parcel.obtain(); |
| Intent serviceIntent = new Intent(); |
| serviceIntent.setClassName(SIMPLE_PACKAGE_NAME, |
| SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE); |
| ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, serviceIntent, |
| WAIT_TIME); |
| |
| ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| /* |
| Log.d("XXXX", "Invoke: " + cmd); |
| Log.d("XXXX", "Result: " + result); |
| Log.d("XXXX", SystemUtil.runShellCommand(mInstrumentation, "dumpsys package " |
| + STUB_PACKAGE_NAME)); |
| */ |
| |
| ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( |
| SIMPLE_PACKAGE_NAME, 0); |
| |
| UidImportanceListener uidForegroundListener = new UidImportanceListener(mContext, |
| appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, WAIT_TIME); |
| uidForegroundListener.register(); |
| UidImportanceListener uidGoneListener = new UidImportanceListener(mContext, |
| appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY, WAIT_TIME); |
| uidGoneListener.register(); |
| |
| WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid, |
| WAIT_TIME); |
| |
| // First kill the process to start out in a stable state. |
| mContext.stopService(serviceIntent); |
| conn.bind(); |
| IBinder service = conn.getServiceIBinder(); |
| conn.unbind(); |
| try { |
| service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); |
| } catch (RemoteException e) { |
| } |
| service = null; |
| |
| // Wait for uid's process to go away. |
| uidGoneListener.waitForValue(IMPORTANCE_GONE, IMPORTANCE_GONE); |
| assertEquals(IMPORTANCE_GONE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // And wait for the uid report to be gone. |
| uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null); |
| |
| String cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny"; |
| String result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // This is a side-effect of the app op command. |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "NONE"); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // Make sure app is not yet on whitelist |
| cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // We will use this to monitor when the service is running. |
| conn.startMonitoring(); |
| |
| try { |
| // Try starting the service. Should fail! |
| boolean failed = false; |
| try { |
| mContext.startService(serviceIntent); |
| } catch (IllegalStateException e) { |
| failed = true; |
| } |
| if (!failed) { |
| fail("Service was allowed to start while in the background"); |
| } |
| |
| // Put app on temporary whitelist to see if this allows the service start. |
| cmd = String.format("cmd deviceidle tempwhitelist -d %d %s", |
| TEMP_WHITELIST_DURATION_MS, SIMPLE_PACKAGE_NAME); |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // Try starting the service now that the app is whitelisted... should work! |
| mContext.startService(serviceIntent); |
| conn.waitForConnect(); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Good, now stop the service and give enough time to get off the temp whitelist. |
| mContext.stopService(serviceIntent); |
| conn.waitForDisconnect(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| CtsAppTestUtils.executeShellCmd(mInstrumentation, |
| "cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME); |
| |
| // Going off the temp whitelist causes a spurious proc state report... that's |
| // not ideal, but okay. |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Now that we should be off the temp whitelist, make sure we again can't start. |
| failed = false; |
| try { |
| mContext.startService(serviceIntent); |
| } catch (IllegalStateException e) { |
| failed = true; |
| } |
| if (!failed) { |
| fail("Service was allowed to start while in the background"); |
| } |
| |
| // Now put app on whitelist, should allow service to run. |
| cmd = "cmd deviceidle whitelist +" + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // Try starting the service now that the app is whitelisted... should work! |
| mContext.startService(serviceIntent); |
| conn.waitForConnect(); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Okay, bring down the service. |
| mContext.stopService(serviceIntent); |
| conn.waitForDisconnect(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| } finally { |
| mContext.stopService(serviceIntent); |
| conn.stopMonitoring(); |
| |
| uidWatcher.finish(); |
| |
| cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow"; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| uidGoneListener.unregister(); |
| uidForegroundListener.unregister(); |
| |
| data.recycle(); |
| } |
| } |
| |
| /** |
| * Test that background check behaves correctly after a process is no longer foreground: first |
| * allowing a service to be started, then stopped by the system when idle. |
| */ |
| public void testBackgroundCheckStopsService() throws Exception { |
| final Parcel data = Parcel.obtain(); |
| ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, mServiceIntent, |
| WAIT_TIME); |
| ServiceConnectionHandler conn2 = new ServiceConnectionHandler(mContext, mService2Intent, |
| WAIT_TIME); |
| |
| ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| /* |
| Log.d("XXXX", "Invoke: " + cmd); |
| Log.d("XXXX", "Result: " + result); |
| Log.d("XXXX", SystemUtil.runShellCommand(mInstrumentation, "dumpsys package " |
| + STUB_PACKAGE_NAME)); |
| */ |
| |
| ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( |
| SIMPLE_PACKAGE_NAME, 0); |
| |
| UidImportanceListener uidServiceListener = new UidImportanceListener(mContext, |
| appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, WAIT_TIME); |
| uidServiceListener.register(); |
| UidImportanceListener uidGoneListener = new UidImportanceListener(mContext, |
| appInfo.uid, IMPORTANCE_CACHED, WAIT_TIME); |
| uidGoneListener.register(); |
| |
| WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid, |
| WAIT_TIME); |
| |
| // First kill the process to start out in a stable state. |
| mContext.stopService(mServiceIntent); |
| mContext.stopService(mService2Intent); |
| conn.bind(); |
| conn2.bind(); |
| IBinder service = conn.getServiceIBinder(); |
| IBinder service2 = conn2.getServiceIBinder(); |
| conn.unbind(); |
| conn2.unbind(); |
| try { |
| service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); |
| } catch (RemoteException e) { |
| } |
| try { |
| service2.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); |
| } catch (RemoteException e) { |
| } |
| service = service2 = null; |
| |
| // Wait for uid's process to go away. |
| uidGoneListener.waitForValue(IMPORTANCE_GONE, |
| IMPORTANCE_GONE); |
| assertEquals(IMPORTANCE_GONE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // And wait for the uid report to be gone. |
| uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null, WAIT_TIME); |
| |
| String cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny"; |
| String result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // This is a side-effect of the app op command. |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_NONEXISTENT); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // Make sure app is not yet on whitelist |
| cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // We will use this to monitor when the service is running. |
| conn.startMonitoring(); |
| |
| try { |
| // Try starting the service. Should fail! |
| boolean failed = false; |
| try { |
| mContext.startService(mServiceIntent); |
| } catch (IllegalStateException e) { |
| failed = true; |
| } |
| if (!failed) { |
| fail("Service was allowed to start while in the background"); |
| } |
| |
| // First poke the process into the foreground, so we can avoid background check. |
| conn2.bind(); |
| conn2.waitForConnect(); |
| |
| // Wait for process state to reflect running service. |
| uidServiceListener.waitForValue( |
| IMPORTANCE_FOREGROUND_SERVICE, |
| ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE); |
| assertEquals(IMPORTANCE_FOREGROUND_SERVICE, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| conn2.unbind(); |
| |
| // Wait for process to recover back down to being cached. |
| uidServiceListener.waitForValue(IMPORTANCE_CACHED, |
| IMPORTANCE_GONE); |
| assertEquals(IMPORTANCE_CACHED, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Try starting the service now that the app is waiting to idle... should work! |
| mContext.startService(mServiceIntent); |
| conn.waitForConnect(); |
| |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // And also start the second service. |
| conn2.startMonitoring(); |
| mContext.startService(mService2Intent); |
| conn2.waitForConnect(); |
| |
| // Force app to go idle now |
| cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| // Wait for services to be stopped by system. |
| uidServiceListener.waitForValue(IMPORTANCE_CACHED, |
| IMPORTANCE_GONE); |
| assertEquals(IMPORTANCE_CACHED, |
| am.getPackageImportance(SIMPLE_PACKAGE_NAME)); |
| |
| // And service should be stopped by system, so just make sure it is disconnected. |
| conn.waitForDisconnect(); |
| conn2.waitForDisconnect(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| // There may be a transient 'SVC' proc state here. |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| } finally { |
| mContext.stopService(mServiceIntent); |
| mContext.stopService(mService2Intent); |
| conn.cleanup(); |
| conn2.cleanup(); |
| |
| uidWatcher.finish(); |
| |
| cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow"; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| cmd = "cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| uidGoneListener.unregister(); |
| uidServiceListener.unregister(); |
| |
| data.recycle(); |
| } |
| } |
| |
| /** |
| * Test the background check doesn't allow services to be started from broadcasts except when in |
| * the correct states. |
| */ |
| public void testBackgroundCheckBroadcastService() throws Exception { |
| final Intent broadcastIntent = new Intent(); |
| broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); |
| broadcastIntent.setClassName(SIMPLE_PACKAGE_NAME, |
| SIMPLE_PACKAGE_NAME + SIMPLE_RECEIVER_START_SERVICE); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| final ServiceProcessController controller = new ServiceProcessController(mContext, |
| mInstrumentation, STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME); |
| final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, |
| mServiceIntent, WAIT_TIME); |
| final WatchUidRunner uidWatcher = controller.getUidWatcher(); |
| |
| try { |
| // First kill the process to start out in a stable state. |
| controller.ensureProcessGone(); |
| |
| // Do initial setup. |
| controller.denyBackgroundOp(); |
| controller.makeUidIdle(); |
| controller.removeFromWhitelist(); |
| |
| // We will use this to monitor when the service is running. |
| conn.startMonitoring(); |
| |
| // Try sending broadcast to start the service. Should fail! |
| SyncOrderedBroadcast br = new SyncOrderedBroadcast(); |
| broadcastIntent.putExtra("service", mServiceIntent); |
| br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME); |
| int brCode = br.getReceivedCode(); |
| if (brCode != Activity.RESULT_CANCELED) { |
| fail("Didn't fail starting service, result=" + brCode); |
| } |
| |
| // Track the uid proc state changes from the broadcast (but not service execution) |
| uidWatcher.waitFor(WatchUidRunner.CMD_IDLE, null, WAIT_TIME); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_RECEIVER, |
| WAIT_TIME); |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, |
| WAIT_TIME); |
| |
| // Put app on temporary whitelist to see if this allows the service start. |
| controller.tempWhitelist(TEMP_WHITELIST_DURATION_MS); |
| |
| // Being on the whitelist means the uid is now active. |
| uidWatcher.expect(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, |
| WAIT_TIME); |
| |
| // Try starting the service now that the app is whitelisted... should work! |
| br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME); |
| brCode = br.getReceivedCode(); |
| if (brCode != Activity.RESULT_FIRST_USER) { |
| fail("Failed starting service, result=" + brCode); |
| } |
| conn.waitForConnect(); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| // We are going to wait until 'SVC', because we may see an intermediate 'RCVR' |
| // proc state depending on timing. |
| uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Good, now stop the service and give enough time to get off the temp whitelist. |
| mContext.stopService(mServiceIntent); |
| conn.waitForDisconnect(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| controller.removeFromTempWhitelist(); |
| |
| // Going off the temp whitelist causes a spurious proc state report... that's |
| // not ideal, but okay. |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| controller.makeUidIdle(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| |
| // Make sure the process is gone so we start over fresh. |
| controller.ensureProcessGone(); |
| |
| // Now that we should be off the temp whitelist, make sure we again can't start. |
| br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME); |
| brCode = br.getReceivedCode(); |
| if (brCode != Activity.RESULT_CANCELED) { |
| fail("Didn't fail starting service, result=" + brCode); |
| } |
| |
| // Track the uid proc state changes from the broadcast (but not service execution) |
| uidWatcher.waitFor(WatchUidRunner.CMD_IDLE, null); |
| // There could be a transient 'cached' state here before 'uncached' if uid state |
| // changes are dispatched before receiver is started. |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_RECEIVER); |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Now put app on whitelist, should allow service to run. |
| controller.addToWhitelist(); |
| |
| // Try starting the service now that the app is whitelisted... should work! |
| br.sendAndWait(mContext, broadcastIntent, Activity.RESULT_OK, null, null, WAIT_TIME); |
| brCode = br.getReceivedCode(); |
| if (brCode != Activity.RESULT_FIRST_USER) { |
| fail("Failed starting service, result=" + brCode); |
| } |
| conn.waitForConnect(); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Okay, bring down the service. |
| mContext.stopService(mServiceIntent); |
| conn.waitForDisconnect(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| } finally { |
| mContext.stopService(mServiceIntent); |
| conn.stopMonitoringIfNeeded(); |
| controller.cleanup(); |
| } |
| } |
| |
| /** |
| * Test that background check does allow services to be started from activities. |
| */ |
| public void testBackgroundCheckActivityService() throws Exception { |
| final Intent activityIntent = new Intent(); |
| activityIntent.setClassName(SIMPLE_PACKAGE_NAME, |
| SIMPLE_PACKAGE_NAME + SIMPLE_ACTIVITY_START_SERVICE); |
| activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| final ServiceProcessController controller = new ServiceProcessController(mContext, |
| mInstrumentation, STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME); |
| final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, |
| mServiceIntent, WAIT_TIME); |
| final WatchUidRunner uidWatcher = controller.getUidWatcher(); |
| |
| try { |
| // First kill the process to start out in a stable state. |
| controller.ensureProcessGone(); |
| |
| // Do initial setup. |
| controller.denyBackgroundOp(); |
| controller.makeUidIdle(); |
| controller.removeFromWhitelist(); |
| |
| // We will use this to monitor when the service is running. |
| conn.startMonitoring(); |
| |
| // Try starting activity that will start the service. This should be okay. |
| WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext()); |
| waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT); |
| activityIntent.putExtra("service", mServiceIntent); |
| mTargetContext.startActivity(activityIntent); |
| Intent resultIntent = waiter.doWait(WAIT_TIME * 2); |
| int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED); |
| if (brCode != Activity.RESULT_FIRST_USER) { |
| fail("Failed starting service, result=" + brCode); |
| } |
| conn.waitForConnect(); |
| |
| final String expectedActivityState = (CtsAppTestUtils.isScreenInteractive(mContext) |
| && !CtsAppTestUtils.isKeyguardLocked(mContext)) |
| ? WatchUidRunner.STATE_TOP : WatchUidRunner.STATE_TOP_SLEEPING; |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, expectedActivityState); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Okay, bring down the service. |
| mContext.stopService(mServiceIntent); |
| conn.waitForDisconnect(); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // App isn't yet idle, so we should be able to start the service again. |
| mContext.startService(mServiceIntent); |
| conn.waitForConnect(); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // And now fast-forward to the app going idle, service should be stopped. |
| controller.makeUidIdle(); |
| uidWatcher.waitFor(WatchUidRunner.CMD_IDLE, null); |
| |
| conn.waitForDisconnect(); |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // No longer should be able to start service. |
| boolean failed = false; |
| try { |
| mContext.startService(mServiceIntent); |
| } catch (IllegalStateException e) { |
| failed = true; |
| } |
| if (!failed) { |
| fail("Service was allowed to start while in the background"); |
| } |
| |
| } finally { |
| mContext.stopService(mServiceIntent); |
| conn.stopMonitoringIfNeeded(); |
| controller.cleanup(); |
| } |
| } |
| |
| /** |
| * Test that the foreground service app op does prevent the foreground state. |
| */ |
| public void testForegroundServiceAppOp() throws Exception { |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| // Use default timeout value 5000 |
| final ServiceProcessController controller = new ServiceProcessController(mContext, |
| mInstrumentation, STUB_PACKAGE_NAME, mAllProcesses); |
| // Use default timeout value 5000 |
| final ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, |
| mServiceIntent); |
| final WatchUidRunner uidWatcher = controller.getUidWatcher(); |
| |
| try { |
| // First kill the process to start out in a stable state. |
| controller.ensureProcessGone(); |
| |
| // Do initial setup. |
| controller.makeUidIdle(); |
| controller.removeFromWhitelist(); |
| controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "allow"); |
| |
| // Put app on whitelist, to allow service to run. |
| controller.addToWhitelist(); |
| |
| // We will use this to monitor when the service is running. |
| conn.startMonitoring(); |
| |
| // -------- START SERVICE AND THEN SUCCESSFULLY GO TO FOREGROUND |
| |
| // Now start the service and wait for it to come up. |
| mContext.startService(mServiceStartForegroundIntent); |
| conn.waitForConnect(); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Now take it out of foreground and confirm. |
| mContext.startService(mServiceStopForegroundIntent); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Good, now stop the service and wait for it to go away. |
| mContext.stopService(mServiceStartForegroundIntent); |
| conn.waitForDisconnect(); |
| |
| // There may be a transient STATE_SERVICE we don't care about, so waitFor. |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| controller.makeUidIdle(); |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| |
| // Make sure the process is gone so we start over fresh. |
| controller.ensureProcessGone(); |
| |
| // -------- START SERVICE AND BLOCK GOING TO FOREGROUND |
| |
| // Now we will deny the app op and ensure the service can't become foreground. |
| controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "ignore"); |
| |
| // Now start the service and wait for it to come up. |
| mContext.startService(mServiceStartForegroundIntent); |
| conn.waitForConnect(); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Good, now stop the service and wait for it to go away. |
| mContext.stopService(mServiceStartForegroundIntent); |
| conn.waitForDisconnect(); |
| |
| // THIS MUST BE AN EXPECT: we want to make sure we don't get in to STATE_FG_SERVICE. |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Make sure the uid is idle (it should be anyway, it never went active here). |
| controller.makeUidIdle(); |
| |
| // Make sure the process is gone so we start over fresh. |
| controller.ensureProcessGone(); |
| |
| // -------- DIRECT START FOREGROUND SERVICE SUCCESSFULLY |
| |
| controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "allow"); |
| |
| // Now start the service and wait for it to come up. |
| mContext.startForegroundService(mServiceStartForegroundIntent); |
| conn.waitForConnect(); |
| |
| // Make sure it becomes a foreground service. The process state changes here |
| // are weird looking because we first need to force the app out of idle to allow |
| // it to start the service. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| // Remove tempwhitelist avoid temp white list block idle command and app crash occur. |
| CtsAppTestUtils.executeShellCmd(mInstrumentation, |
| "cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME); |
| // Good, now stop the service and wait for it to go away. |
| mContext.stopService(mServiceStartForegroundIntent); |
| conn.waitForDisconnect(); |
| |
| // There may be a transient STATE_SERVICE we don't care about, so waitFor. |
| uidWatcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| controller.makeUidIdle(); |
| |
| // Make sure the process is gone so we start over fresh. |
| controller.ensureProcessGone(); |
| |
| // -------- DIRECT START FOREGROUND SERVICE BLOCKED |
| |
| // Now we will deny the app op and ensure the service can't become foreground. |
| controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "ignore"); |
| |
| // But we will put it on the whitelist so the service is still allowed to start. |
| controller.addToWhitelist(); |
| |
| // Now start the service and wait for it to come up. |
| mContext.startForegroundService(mServiceStartForegroundIntent); |
| conn.waitForConnect(); |
| |
| // In this case we only get to run it as a regular service. |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| |
| // Good, now stop the service and wait for it to go away. |
| mContext.stopService(mServiceStartForegroundIntent); |
| conn.waitForDisconnect(); |
| |
| // THIS MUST BE AN EXPECT: we want to make sure we don't get in to STATE_FG_SERVICE. |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| |
| // Make sure the uid is idle (it should be anyway, it never went active here). |
| controller.makeUidIdle(); |
| |
| // Make sure the process is gone so we start over fresh. |
| controller.ensureProcessGone(); |
| |
| // -------- XXX NEED TO TEST NON-WHITELIST CASE WHERE NOTHING HAPPENS |
| |
| } finally { |
| mContext.stopService(mServiceStartForegroundIntent); |
| conn.stopMonitoringIfNeeded(); |
| controller.cleanup(); |
| controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "allow"); |
| controller.removeFromWhitelist(); |
| } |
| } |
| |
| /** |
| * Verify that an app under background restrictions has its foreground services demoted to |
| * ordinary service state when it is no longer the top app. |
| */ |
| public void testBgRestrictedForegroundService() throws Exception { |
| final Intent activityIntent = new Intent() |
| .setClassName(SIMPLE_PACKAGE_NAME, |
| SIMPLE_PACKAGE_NAME + SIMPLE_ACTIVITY_START_FG_SERVICE) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| final ServiceProcessController controller = new ServiceProcessController(mContext, |
| getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME); |
| final WatchUidRunner uidWatcher = controller.getUidWatcher(); |
| |
| final Intent homeIntent = new Intent() |
| .setAction(Intent.ACTION_MAIN) |
| .addCategory(Intent.CATEGORY_HOME) |
| .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
| | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); |
| |
| final Intent serviceStartIntent = new Intent(mService3Intent) |
| .setAction(ACTION_START_THEN_FG); |
| activityIntent.putExtra("service", serviceStartIntent); |
| boolean activityStarted = false; |
| |
| try { |
| // First kill the process to start out in a stable state. |
| controller.ensureProcessGone(); |
| |
| // Do initial setup. |
| controller.denyAnyInBackgroundOp(); |
| controller.makeUidIdle(); |
| controller.removeFromWhitelist(); |
| controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "allow"); |
| |
| // Start the activity, which will start the fg service as well, and wait |
| // for the report that it's all up and running. |
| WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext()); |
| waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT); |
| |
| activityIntent.setAction(ACTION_SIMPLE_ACTIVITY_START_FG); |
| mTargetContext.startActivity(activityIntent); |
| activityStarted = true; |
| |
| Intent resultIntent = waiter.doWait(WAIT_TIME); |
| int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED); |
| if (brCode != Activity.RESULT_FIRST_USER) { |
| fail("Failed starting service, result=" + brCode); |
| } |
| |
| // activity is in front, fg service is running. make sure that we see |
| // the expected state at this point. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Switch to the home app; make sure the test app drops all the way |
| // down to SERVICE, not FG_SERVICE |
| mTargetContext.startActivity(homeIntent); |
| uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE); |
| } finally { |
| // tear down everything and we're done |
| if (activityStarted) { |
| activityIntent.setAction(ACTION_FINISH_EVERYTHING); |
| mTargetContext.startActivity(activityIntent); |
| } |
| |
| controller.cleanup(); |
| } |
| |
| } |
| |
| private boolean supportsCantSaveState() { |
| if (mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_CANT_SAVE_STATE)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Test that a single "can't save state" app has the proper process management semantics. |
| */ |
| public void testCantSaveStateLaunchAndBackground() throws Exception { |
| if (!supportsCantSaveState()) { |
| return; |
| } |
| |
| final Intent activityIntent = new Intent(); |
| activityIntent.setPackage(CANT_SAVE_STATE_1_PACKAGE_NAME); |
| activityIntent.setAction(Intent.ACTION_MAIN); |
| activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); |
| activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| final Intent homeIntent = new Intent(); |
| homeIntent.setAction(Intent.ACTION_MAIN); |
| homeIntent.addCategory(Intent.CATEGORY_HOME); |
| homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| String cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME; |
| String result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( |
| CANT_SAVE_STATE_1_PACKAGE_NAME, 0); |
| |
| // This test is also using UidImportanceListener to make sure the correct |
| // heavy-weight state is reported there. |
| UidImportanceListener uidForegroundListener = new UidImportanceListener(mContext, |
| appInfo.uid, IMPORTANCE_FOREGROUND, |
| WAIT_TIME); |
| uidForegroundListener.register(); |
| UidImportanceListener uidBackgroundListener = new UidImportanceListener(mContext, |
| appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE - 1, |
| WAIT_TIME); |
| uidBackgroundListener.register(); |
| UidImportanceListener uidCachedListener = new UidImportanceListener(mContext, |
| appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE + 1, |
| WAIT_TIME); |
| uidCachedListener.register(); |
| |
| WatchUidRunner uidWatcher = new WatchUidRunner(mInstrumentation, appInfo.uid, |
| WAIT_TIME); |
| |
| UiDevice device = UiDevice.getInstance(mInstrumentation); |
| |
| try { |
| // Start the heavy-weight app, should launch like a normal app. |
| mTargetContext.startActivity(activityIntent); |
| waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| |
| // Wait for process state to reflect running activity. |
| uidForegroundListener.waitForValue( |
| IMPORTANCE_FOREGROUND, |
| IMPORTANCE_FOREGROUND); |
| assertEquals(IMPORTANCE_FOREGROUND, |
| am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME)); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Now go to home, leaving the app. It should be put in the heavy weight state. |
| mTargetContext.startActivity(homeIntent); |
| |
| final int expectedImportance = |
| (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) |
| ? ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE |
| : ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE_PRE_26; |
| // Wait for process to go down to background heavy-weight. |
| uidBackgroundListener.waitForValue(expectedImportance, expectedImportance); |
| assertEquals(expectedImportance, |
| am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME)); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT); |
| |
| // While in background, should go in to normal idle state. |
| // Force app to go idle now |
| cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| |
| // Switch back to heavy-weight app to see if it correctly returns to foreground. |
| mTargetContext.startActivity(activityIntent); |
| |
| // Wait for process state to reflect running activity. |
| uidForegroundListener.waitForValue( |
| IMPORTANCE_FOREGROUND, |
| IMPORTANCE_FOREGROUND); |
| assertEquals(IMPORTANCE_FOREGROUND, |
| am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME)); |
| |
| // Also make sure the uid state reports are as expected. |
| uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| |
| // Exit activity, check to see if we are now cached. |
| mInstrumentation.getUiAutomation().performGlobalAction( |
| AccessibilityService.GLOBAL_ACTION_BACK); |
| // Hit back again in case the notification curtain is open |
| mInstrumentation.getUiAutomation().performGlobalAction( |
| AccessibilityService.GLOBAL_ACTION_BACK); |
| |
| // Wait for process to become cached |
| uidCachedListener.waitForValue( |
| IMPORTANCE_CACHED, |
| IMPORTANCE_CACHED); |
| assertEquals(IMPORTANCE_CACHED, |
| am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME)); |
| |
| uidWatcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT); |
| |
| // While in background, should go in to normal idle state. |
| // Force app to go idle now |
| cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| uidWatcher.expect(WatchUidRunner.CMD_IDLE, null); |
| |
| } finally { |
| uidWatcher.finish(); |
| uidForegroundListener.unregister(); |
| uidBackgroundListener.unregister(); |
| uidCachedListener.unregister(); |
| } |
| } |
| |
| /** |
| * Test that switching between two "can't save state" apps is handled properly. |
| */ |
| public void testCantSaveStateLaunchAndSwitch() throws Exception { |
| if (!supportsCantSaveState()) { |
| return; |
| } |
| |
| final Intent activity1Intent = new Intent(); |
| activity1Intent.setPackage(CANT_SAVE_STATE_1_PACKAGE_NAME); |
| activity1Intent.setAction(Intent.ACTION_MAIN); |
| activity1Intent.addCategory(Intent.CATEGORY_LAUNCHER); |
| activity1Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| final Intent activity2Intent = new Intent(); |
| activity2Intent.setPackage(CANT_SAVE_STATE_2_PACKAGE_NAME); |
| activity2Intent.setAction(Intent.ACTION_MAIN); |
| activity2Intent.addCategory(Intent.CATEGORY_LAUNCHER); |
| activity2Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| final Intent homeIntent = new Intent(); |
| homeIntent.setAction(Intent.ACTION_MAIN); |
| homeIntent.addCategory(Intent.CATEGORY_HOME); |
| homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| UiDevice device = UiDevice.getInstance(mInstrumentation); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| |
| // We don't want to wait for the uid to actually go idle, we can force it now. |
| String cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME; |
| String result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| cmd = "am make-uid-idle " + CANT_SAVE_STATE_2_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo( |
| CANT_SAVE_STATE_1_PACKAGE_NAME, 0); |
| WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid, |
| WAIT_TIME); |
| |
| ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo( |
| CANT_SAVE_STATE_2_PACKAGE_NAME, 0); |
| WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid, |
| WAIT_TIME); |
| |
| try { |
| // Start the first heavy-weight app, should launch like a normal app. |
| mTargetContext.startActivity(activity1Intent); |
| waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| |
| // Make sure the uid state reports are as expected. |
| uid1Watcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uid1Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Now go to home, leaving the app. It should be put in the heavy weight state. |
| mTargetContext.startActivity(homeIntent); |
| |
| // Wait for process to go down to background heavy-weight. |
| uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT); |
| |
| // Start the second heavy-weight app, should ask us what to do with the two apps |
| startActivityAndWaitForShow(activity2Intent); |
| |
| // First, let's try returning to the original app. |
| maybeClick(device, new UiSelector().resourceId("android:id/switch_old")); |
| waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| |
| // App should now be back in foreground. |
| uid1Watcher.expect(WatchUidRunner.CMD_UNCACHED, null); |
| uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Return to home. |
| mTargetContext.startActivity(homeIntent); |
| uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT); |
| |
| // Again try starting second heavy-weight app to get prompt. |
| startActivityAndWaitForShow(activity2Intent); |
| |
| // Now we'll switch to the new app. |
| maybeClick(device, new UiSelector().resourceId("android:id/switch_new")); |
| waitForAppFocus(CANT_SAVE_STATE_2_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| |
| // The original app should now become cached. |
| uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT); |
| |
| // And the new app should start. |
| uid2Watcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uid2Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Make sure the original app is idle for cleanliness |
| cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| uid1Watcher.expect(WatchUidRunner.CMD_IDLE, null); |
| |
| // Return to home. |
| mTargetContext.startActivity(homeIntent); |
| uid2Watcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT); |
| |
| // Try starting the first heavy weight app, but return to the existing second. |
| startActivityAndWaitForShow(activity1Intent); |
| maybeClick(device, new UiSelector().resourceId("android:id/switch_old")); |
| waitForAppFocus(CANT_SAVE_STATE_2_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| uid2Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Return to home. |
| mTargetContext.startActivity(homeIntent); |
| uid2Watcher.waitFor(WatchUidRunner.CMD_CACHED, null); |
| uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_HEAVY_WEIGHT); |
| |
| // Again start the first heavy weight app, this time actually switching to it |
| startActivityAndWaitForShow(activity1Intent); |
| maybeClick(device, new UiSelector().resourceId("android:id/switch_new")); |
| waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| |
| // The second app should now become cached. |
| uid2Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT); |
| |
| // And the first app should start. |
| uid1Watcher.waitFor(WatchUidRunner.CMD_ACTIVE, null); |
| uid1Watcher.waitFor(WatchUidRunner.CMD_UNCACHED, null); |
| uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); |
| |
| // Exit activity, check to see if we are now cached. |
| waitForAppFocus(CANT_SAVE_STATE_1_PACKAGE_NAME, WAIT_TIME); |
| device.waitForIdle(); |
| mInstrumentation.getUiAutomation().performGlobalAction( |
| AccessibilityService.GLOBAL_ACTION_BACK); |
| // Hit back again in case the notification curtain is open |
| mInstrumentation.getUiAutomation().performGlobalAction( |
| AccessibilityService.GLOBAL_ACTION_BACK); |
| uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null); |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT); |
| |
| // Make both apps idle for cleanliness. |
| cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| cmd = "am make-uid-idle " + CANT_SAVE_STATE_2_PACKAGE_NAME; |
| result = SystemUtil.runShellCommand(mInstrumentation, cmd); |
| |
| } finally { |
| uid2Watcher.finish(); |
| uid1Watcher.finish(); |
| } |
| } |
| |
| /** |
| * Test a service binding cycle between two apps, with one of them also running a foreground |
| * service. The other app should also get an FGS proc state. On stopping the foreground service, |
| * app should go back to cached state. |
| * |
| * @throws Exception |
| */ |
| public void testCycleFgs() throws Exception { |
| ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP1, 0); |
| ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP3, 0); |
| WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid, |
| WAITFOR_MSEC); |
| |
| try { |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null); |
| |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Create a cycle |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| try { |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_CACHED_EMPTY); |
| fail("App3 should not be demoted to cached"); |
| } catch (IllegalStateException ise) { |
| // Didn't go to cached in spite of cycle. Good! |
| } |
| |
| // Stop the foreground service |
| CommandReceiver.sendCommand(mContext, CommandReceiver |
| .COMMAND_STOP_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| |
| // Check that the app's proc state has fallen |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| } finally { |
| // Clean up: unbind services to avoid from interferences with other tests |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null); |
| |
| uid1Watcher.finish(); |
| uid3Watcher.finish(); |
| } |
| } |
| |
| /** |
| * Test a service binding cycle between three apps, with one of them also running a foreground |
| * service. The other apps should also get an FGS proc state. On stopping the foreground |
| * service, app should go back to cached state. |
| * |
| * @throws Exception |
| */ |
| public void testCycleFgsTriangle() throws Exception { |
| ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP1, 0); |
| ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP2, 0); |
| ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP3, 0); |
| WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid, |
| WAITFOR_MSEC); |
| |
| try { |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); |
| |
| uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Bind from 2 to 3 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null); |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Create a cycle |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| try { |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_CACHED_EMPTY); |
| fail("App3 should not be demoted to cached"); |
| } catch (IllegalStateException ise) { |
| // Didn't go to cached in spite of cycle. Good! |
| } |
| |
| try { |
| uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_CACHED_EMPTY); |
| fail("App2 should not be demoted to cached"); |
| } catch (IllegalStateException ise) { |
| // Didn't go to cached in spite of cycle. Good! |
| } |
| |
| try { |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_CACHED_EMPTY); |
| fail("App1 should not be demoted to cached"); |
| } catch (IllegalStateException ise) { |
| // Didn't go to cached in spite of cycle. Good! |
| } |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); |
| |
| uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| } finally { |
| // Clean up: unbind services to avoid from interferences with other tests |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| // Stop the foreground service |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| |
| uid1Watcher.finish(); |
| uid2Watcher.finish(); |
| uid3Watcher.finish(); |
| } |
| } |
| |
| /** |
| * Test a service binding cycle between three apps, with one of them also running a foreground |
| * service. The other apps should also get an FGS proc state. On stopping the foreground |
| * service, app should go back to cached state. |
| * |
| * @throws Exception |
| */ |
| public void testCycleFgsTriangleBiDi() throws Exception { |
| ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP1, 0); |
| ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP2, 0); |
| ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP3, 0); |
| WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid, |
| WAITFOR_MSEC); |
| |
| try { |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Bind from 1 to 2, 1 to 3 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null); |
| |
| uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE); |
| |
| // Bind from 2 to 3, 3 to 2, 3 to 1 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| try { |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_CACHED_EMPTY); |
| fail("App3 should not be demoted to cached"); |
| } catch (IllegalStateException ise) { |
| // Didn't go to cached in spite of cycle. Good! |
| } |
| |
| // Stop the foreground service |
| CommandReceiver.sendCommand(mContext, CommandReceiver |
| .COMMAND_STOP_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| |
| // Check that the apps' proc state has fallen |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| } finally { |
| // Clean up: unbind services to avoid from interferences with other tests |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| uid1Watcher.finish(); |
| uid2Watcher.finish(); |
| uid3Watcher.finish(); |
| } |
| } |
| |
| /** |
| * Test process states for foreground service binding to another app, with and without |
| * BIND_INCLUDE_CAPABILITIES. With BIND_INCLUDE_CAPABILITIES flag, |
| * PROCESS_CAPABILITY_FOREGROUND_LOCATION can be passed from client to service. Without |
| * BIND_INCLUDE_CAPABILITIES flag, PROCESS_CAPABILITY_FOREGROUND_LOCATION can not be passed from |
| * client to service. |
| * @throws Exception |
| */ |
| public void testFgsLocationBind() throws Exception { |
| setupWatchers(3); |
| |
| try { |
| // Put Package1 in TOP state, now it gets all capability (because the TOP process |
| // gets all while-in-use permission (not from FGSL). |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_ACTIVITY, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_TOP, |
| new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION |
| | PROCESS_CAPABILITY_ALL)); |
| |
| // Start a FGS |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, |
| mAppInfo[0].packageName, mAppInfo[0].packageName, 0, null); |
| |
| // Start a FGSL |
| Bundle bundle = new Bundle(); |
| bundle.putInt(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE, |
| ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_LOCATION, |
| mAppInfo[0].packageName, mAppInfo[0].packageName, 0, bundle); |
| |
| // Stop the activity. |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_STOP_ACTIVITY, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); |
| |
| mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_FG_SERVICE, |
| new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION |
| | PROCESS_CAPABILITY_ALL_IMPLICIT)); |
| |
| // Bind App 0 -> App 1, verify doesn't include capability. |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null); |
| // Verify app1 does NOT have capability. |
| mWatchers[1].waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_FG_SERVICE, |
| new Integer(PROCESS_CAPABILITY_ALL_IMPLICIT)); |
| |
| // Bind App 0 -> App 2, include capability. |
| bundle = new Bundle(); |
| bundle.putInt(CommandReceiver.EXTRA_FLAGS, Context.BIND_INCLUDE_CAPABILITIES); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| mAppInfo[0].packageName, mAppInfo[2].packageName, 0, bundle); |
| // Verify app2 has FOREGROUND_LOCATION capability. |
| mWatchers[2].waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_FG_SERVICE, |
| new Integer(PROCESS_CAPABILITY_FOREGROUND_LOCATION |
| | PROCESS_CAPABILITY_ALL_IMPLICIT)); |
| |
| // Back down to foreground service |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION, |
| mAppInfo[0].packageName, mAppInfo[0].packageName, 0, null); |
| // Verify app0 does NOT have FOREGROUND_LOCATION capability. |
| mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_FG_SERVICE, |
| new Integer(PROCESS_CAPABILITY_ALL_IMPLICIT)); |
| |
| // Remove foreground service as well |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE, |
| mAppInfo[0].packageName, mAppInfo[0].packageName, 0, null); |
| mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, |
| WatchUidRunner.STATE_CACHED_EMPTY, |
| new Integer(PROCESS_CAPABILITY_NONE)); |
| } finally { |
| // Clean up: unbind services to avoid from interferences with other tests |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| mAppInfo[0].packageName, mAppInfo[2].packageName, 0, null); |
| |
| shutdownWatchers(); |
| } |
| } |
| |
| /** |
| * Test process states for top app binding with and without BIND_INCLUDE_CAPABILITIES flag. |
| * Bound app should be TOP w/flag and BTOP without flag. |
| * @throws Exception |
| */ |
| public void testTopBind() throws Exception { |
| setupWatchers(2); |
| |
| Activity activity = null; |
| |
| try { |
| // This will start an activity in App0 |
| activity = startSubActivity(ScreenOnActivity.class); |
| |
| // Bind Stub -> App 0, verify doesn't include capability (only BTOP, not TOP) |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null); |
| mWatchers[0].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_BOUND_TOP, |
| new Integer(PROCESS_CAPABILITY_NONE)); |
| |
| // Bind Stub -> App 1, include capability (TOP) |
| Bundle bundle = new Bundle(); |
| bundle.putInt(CommandReceiver.EXTRA_FLAGS, Context.BIND_INCLUDE_CAPABILITIES); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, bundle); |
| mWatchers[1].waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_BOUND_TOP, |
| new Integer(PROCESS_CAPABILITY_ALL)); |
| } finally { |
| // Clean up: unbind services to avoid from interferences with other tests |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, null); |
| |
| shutdownWatchers(); |
| if (activity != null) { |
| activity.finish(); |
| } |
| } |
| } |
| |
| private final <T extends Activity> Activity startSubActivity(Class<T> activityClass) { |
| final Instrumentation.ActivityResult result = new Instrumentation.ActivityResult( |
| 0, new Intent()); |
| final Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor( |
| activityClass.getName(), result, false); |
| mInstrumentation.addMonitor(monitor); |
| launchActivity(STUB_PACKAGE_NAME, activityClass, null); |
| return monitor.waitForActivity(); |
| } |
| |
| public void testCycleTop() throws Exception { |
| ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP1, 0); |
| ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP2, 0); |
| ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP3, 0); |
| |
| PermissionUtils.grantPermission( |
| PACKAGE_NAME_APP1, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| PermissionUtils.grantPermission( |
| PACKAGE_NAME_APP2, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| PermissionUtils.grantPermission( |
| PACKAGE_NAME_APP3, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| |
| UidImportanceListener uid1Listener = new UidImportanceListener(mContext, |
| app1Info.uid, IMPORTANCE_VISIBLE, |
| WAITFOR_MSEC); |
| uid1Listener.register(); |
| |
| UidImportanceListener uid1ServiceListener = new UidImportanceListener(mContext, |
| app1Info.uid, IMPORTANCE_CACHED, |
| WAITFOR_MSEC); |
| uid1ServiceListener.register(); |
| |
| UidImportanceListener uid2Listener = new UidImportanceListener(mContext, |
| app2Info.uid, IMPORTANCE_VISIBLE, |
| WAITFOR_MSEC); |
| uid2Listener.register(); |
| |
| UidImportanceListener uid2ServiceListener = new UidImportanceListener(mContext, |
| app2Info.uid, IMPORTANCE_CACHED, |
| WAITFOR_MSEC); |
| uid2ServiceListener.register(); |
| |
| UidImportanceListener uid3Listener = new UidImportanceListener(mContext, |
| app3Info.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, |
| WAITFOR_MSEC); |
| uid3Listener.register(); |
| |
| UidImportanceListener uid3ServiceListener = new UidImportanceListener(mContext, |
| app3Info.uid, IMPORTANCE_CACHED, |
| WAITFOR_MSEC); |
| uid3ServiceListener.register(); |
| |
| Activity activity = null; |
| |
| try { |
| // Start an activity |
| activity = startSubActivity(ScreenOnActivity.class); |
| |
| // Start a FGS in app2 |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, PACKAGE_NAME_APP2, |
| PACKAGE_NAME_APP2, 0, null); |
| |
| uid2Listener.waitForValue( |
| IMPORTANCE_FOREGROUND_SERVICE, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| |
| // Bind from TOP to the service in app1 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null); |
| |
| uid1Listener.waitForValue(IMPORTANCE_FOREGROUND, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| |
| // Bind from app1 to a service in app2 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); |
| |
| // Bind from app2 to a service in app3 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null); |
| |
| uid3Listener.waitForValue(IMPORTANCE_FOREGROUND, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| |
| // Create a cycle |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| try { |
| uid3Listener.waitForValue(IMPORTANCE_CACHED, |
| IMPORTANCE_CACHED); |
| fail("App3 should not be demoted to cached, expecting FGS"); |
| } catch (IllegalStateException e) { |
| // Didn't go to cached in spite of cycle. Good! |
| } |
| |
| // Unbind from the TOP app |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null); |
| |
| // Check that the apps' proc state is FOREGROUND_SERVICE |
| uid2Listener.waitForValue( |
| IMPORTANCE_FOREGROUND_SERVICE, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| |
| // Stop the foreground service |
| CommandReceiver.sendCommand(mContext, CommandReceiver |
| .COMMAND_STOP_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null); |
| |
| // Check that the apps fall down to cached state |
| uid1ServiceListener.waitForValue( |
| IMPORTANCE_CACHED, |
| IMPORTANCE_CACHED); |
| |
| uid2ServiceListener.waitForValue( |
| IMPORTANCE_CACHED, |
| IMPORTANCE_CACHED); |
| |
| uid3ServiceListener.waitForValue( |
| IMPORTANCE_CACHED, |
| IMPORTANCE_CACHED); |
| } finally { |
| // Clean up: unbind services to avoid from interferences with other tests |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null); |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| uid1Listener.unregister(); |
| uid1ServiceListener.unregister(); |
| uid2Listener.unregister(); |
| uid2ServiceListener.unregister(); |
| uid3Listener.unregister(); |
| uid3ServiceListener.unregister(); |
| if (activity != null) { |
| activity.finish(); |
| } |
| } |
| } |
| |
| public void testCycleFgAppAndAlert() throws Exception { |
| ApplicationInfo stubInfo = mContext.getPackageManager().getApplicationInfo( |
| STUB_PACKAGE_NAME, 0); |
| ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP1, 0); |
| ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP2, 0); |
| ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo( |
| PACKAGE_NAME_APP3, 0); |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| PermissionUtils.grantPermission( |
| PACKAGE_NAME_APP1, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| PermissionUtils.grantPermission( |
| PACKAGE_NAME_APP2, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| PermissionUtils.grantPermission( |
| PACKAGE_NAME_APP3, android.Manifest.permission.PACKAGE_USAGE_STATS); |
| |
| UidImportanceListener stubListener = new UidImportanceListener(mContext, |
| stubInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE, |
| WAITFOR_MSEC); |
| stubListener.register(); |
| |
| UidImportanceListener uid1Listener = new UidImportanceListener(mContext, |
| app1Info.uid, IMPORTANCE_VISIBLE, |
| WAITFOR_MSEC); |
| uid1Listener.register(); |
| |
| UidImportanceListener uid2Listener = new UidImportanceListener(mContext, |
| app2Info.uid, IMPORTANCE_VISIBLE, |
| WAITFOR_MSEC); |
| uid2Listener.register(); |
| |
| UidImportanceListener uid3Listener = new UidImportanceListener(mContext, |
| app3Info.uid, IMPORTANCE_VISIBLE, |
| WAITFOR_MSEC); |
| uid3Listener.register(); |
| |
| WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid, |
| WAITFOR_MSEC); |
| WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid, |
| WAITFOR_MSEC); |
| |
| try { |
| // Stub app should have been in foreground since it's being instrumented. |
| |
| PermissionUtils.grantPermission( |
| STUB_PACKAGE_NAME, android.Manifest.permission.SYSTEM_ALERT_WINDOW); |
| // Show an alert on app0 |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_ALERT_SERVICE, STUB_PACKAGE_NAME, |
| STUB_PACKAGE_NAME, 0, null); |
| |
| // Start a FGS in app2 |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, PACKAGE_NAME_APP2, |
| PACKAGE_NAME_APP2, 0, null); |
| |
| uid2Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| |
| // Bind from app0 to a service in app1 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null); |
| |
| // Bind from app2 to a service in app1 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null); |
| |
| // Bind from app3 to a service in app1 |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| // Create a cycle |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null); |
| |
| uid1Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| uid3Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE, |
| IMPORTANCE_FOREGROUND_SERVICE); |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null); |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null); |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null); |
| |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE, |
| PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null); |
| |
| // Stop the foreground service |
| CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE, |
| PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null); |
| |
| // hide the alert |
| CommandReceiver.sendCommand(mContext, |
| CommandReceiver.COMMAND_STOP_ALERT_SERVICE, STUB_PACKAGE_NAME, |
| STUB_PACKAGE_NAME, 0, null); |
| |
| // Check that the apps' proc state has fallen |
| uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY); |
| } finally { |
| stubListener.unregister(); |
| uid1Listener.unregister(); |
| uid2Listener.unregister(); |
| uid3Listener.unregister(); |
| uid1Watcher.finish(); |
| uid2Watcher.finish(); |
| uid3Watcher.finish(); |
| } |
| } |
| } |