1 /* 2 * Copyright (C) 2018 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.pm; 18 19 import static android.app.AppOpsManager.MODE_ALLOWED; 20 import static android.app.AppOpsManager.MODE_IGNORED; 21 import static android.app.AppOpsManager.OP_CAMERA; 22 import static android.app.AppOpsManager.OP_PLAY_AUDIO; 23 import static android.app.AppOpsManager.OP_RECORD_AUDIO; 24 import static android.app.AppOpsManager.opToName; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assert.assertTrue; 31 import static org.junit.Assert.fail; 32 import static org.junit.Assume.assumeTrue; 33 34 import android.app.AppGlobals; 35 import android.content.BroadcastReceiver; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.IntentFilter; 40 import android.content.pm.IPackageManager; 41 import android.content.pm.LauncherApps; 42 import android.content.pm.PackageManager; 43 import android.content.pm.SuspendDialogInfo; 44 import android.content.res.Resources; 45 import android.os.BaseBundle; 46 import android.os.Bundle; 47 import android.os.Handler; 48 import android.os.Looper; 49 import android.os.PersistableBundle; 50 import android.os.RemoteException; 51 import android.os.ServiceManager; 52 import android.os.UserHandle; 53 import android.support.test.uiautomator.By; 54 import android.support.test.uiautomator.UiDevice; 55 import android.support.test.uiautomator.UiObject2; 56 import android.support.test.uiautomator.Until; 57 import android.util.Log; 58 import android.view.IWindowManager; 59 import android.view.WindowManagerGlobal; 60 61 import androidx.test.InstrumentationRegistry; 62 import androidx.test.filters.FlakyTest; 63 import androidx.test.filters.LargeTest; 64 import androidx.test.runner.AndroidJUnit4; 65 66 import com.android.internal.app.IAppOpsCallback; 67 import com.android.internal.app.IAppOpsService; 68 import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity; 69 import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver; 70 71 import org.junit.After; 72 import org.junit.Before; 73 import org.junit.Test; 74 import org.junit.runner.RunWith; 75 76 import java.io.IOException; 77 import java.util.Arrays; 78 import java.util.concurrent.CountDownLatch; 79 import java.util.concurrent.SynchronousQueue; 80 import java.util.concurrent.TimeUnit; 81 import java.util.concurrent.atomic.AtomicReference; 82 83 @RunWith(AndroidJUnit4.class) 84 @LargeTest 85 @FlakyTest 86 public class SuspendPackagesTest { 87 private static final String TAG = SuspendPackagesTest.class.getSimpleName(); 88 private static final String TEST_APP_LABEL = "Suspend Test App"; 89 private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME; 90 private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME}; 91 92 public static final String INSTRUMENTATION_PACKAGE = "com.android.frameworks.servicestests"; 93 public static final String ACTION_REPORT_MY_PACKAGE_SUSPENDED = 94 INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED"; 95 public static final String ACTION_REPORT_MY_PACKAGE_UNSUSPENDED = 96 INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_UNSUSPENDED"; 97 public static final String ACTION_REPORT_TEST_ACTIVITY_STARTED = 98 INSTRUMENTATION_PACKAGE + ".action.REPORT_TEST_ACTIVITY_STARTED"; 99 public static final String ACTION_REPORT_TEST_ACTIVITY_STOPPED = 100 INSTRUMENTATION_PACKAGE + ".action.REPORT_TEST_ACTIVITY_STOPPED"; 101 public static final String ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED = 102 INSTRUMENTATION_PACKAGE + ".action.REPORT_MORE_DETAILS_ACTIVITY_STARTED"; 103 public static final String ACTION_FINISH_TEST_ACTIVITY = 104 INSTRUMENTATION_PACKAGE + ".action.FINISH_TEST_ACTIVITY"; 105 public static final String EXTRA_RECEIVED_PACKAGE_NAME = 106 SuspendPackagesTest.INSTRUMENTATION_PACKAGE + ".extra.RECEIVED_PACKAGE_NAME"; 107 108 109 private Context mContext; 110 private PackageManager mPackageManager; 111 private LauncherApps mLauncherApps; 112 private Handler mReceiverHandler; 113 private AppCommunicationReceiver mAppCommsReceiver; 114 private StubbedCallback mTestCallback; 115 private UiDevice mUiDevice; 116 private ComponentName mDeviceAdminComponent; 117 private boolean mPoSet; 118 private boolean mDoSet; 119 120 private static final class AppCommunicationReceiver extends BroadcastReceiver { 121 private Context context; 122 private boolean registered; 123 private SynchronousQueue<Intent> intentQueue = new SynchronousQueue<>(); 124 AppCommunicationReceiver(Context context)125 AppCommunicationReceiver(Context context) { 126 this.context = context; 127 } 128 register(Handler handler, String... actions)129 void register(Handler handler, String... actions) { 130 registered = true; 131 final IntentFilter intentFilter = new IntentFilter(); 132 for (String action : actions) { 133 intentFilter.addAction(action); 134 } 135 context.registerReceiver(this, intentFilter, null, handler); 136 } 137 unregister()138 void unregister() { 139 if (registered) { 140 context.unregisterReceiver(this); 141 } 142 } 143 144 @Override onReceive(Context context, Intent intent)145 public void onReceive(Context context, Intent intent) { 146 Log.d(TAG, "AppCommunicationReceiver#onReceive: " + intent.getAction()); 147 try { 148 intentQueue.offer(intent, 5, TimeUnit.SECONDS); 149 } catch (InterruptedException ie) { 150 throw new RuntimeException("Receiver thread interrupted", ie); 151 } 152 } 153 pollForIntent(long secondsToWait)154 Intent pollForIntent(long secondsToWait) { 155 if (!registered) { 156 throw new IllegalStateException("Receiver not registered"); 157 } 158 final Intent intent; 159 try { 160 intent = intentQueue.poll(secondsToWait, TimeUnit.SECONDS); 161 } catch (InterruptedException ie) { 162 throw new RuntimeException("Interrupted while waiting for app broadcast", ie); 163 } 164 return intent; 165 } 166 drainPendingBroadcasts()167 void drainPendingBroadcasts() { 168 while (pollForIntent(5) != null) ; 169 } 170 receiveIntentFromApp()171 Intent receiveIntentFromApp() { 172 final Intent intentReceived = pollForIntent(5); 173 assertNotNull("No intent received from app within 5 seconds", intentReceived); 174 return intentReceived; 175 } 176 } 177 178 @Before setUp()179 public void setUp() { 180 mContext = InstrumentationRegistry.getTargetContext(); 181 mPackageManager = mContext.getPackageManager(); 182 mLauncherApps = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE); 183 mReceiverHandler = new Handler(Looper.getMainLooper()); 184 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 185 mDeviceAdminComponent = new ComponentName(mContext, 186 "com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"); 187 IPackageManager ipm = AppGlobals.getPackageManager(); 188 try { 189 // Otherwise implicit broadcasts will not be delivered. 190 ipm.setPackageStoppedState(TEST_APP_PACKAGE_NAME, false, mContext.getUserId()); 191 } catch (RemoteException e) { 192 e.rethrowAsRuntimeException(); 193 } 194 unsuspendTestPackage(); 195 mAppCommsReceiver = new AppCommunicationReceiver(mContext); 196 } 197 198 /** 199 * Care should be taken when used with {@link #mAppCommsReceiver} in the same test as both use 200 * the same handler. 201 */ requestAppAction(String action)202 private Bundle requestAppAction(String action) throws InterruptedException { 203 final AtomicReference<Bundle> result = new AtomicReference<>(); 204 final CountDownLatch receiverLatch = new CountDownLatch(1); 205 final ComponentName testReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME, 206 SuspendTestReceiver.class.getCanonicalName()); 207 final Intent broadcastIntent = new Intent(action) 208 .setComponent(testReceiverComponent) 209 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 210 mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() { 211 @Override 212 public void onReceive(Context context, Intent intent) { 213 result.set(getResultExtras(true)); 214 receiverLatch.countDown(); 215 } 216 }, mReceiverHandler, 0, null, null); 217 218 assertTrue("Test receiver timed out ", receiverLatch.await(5, TimeUnit.SECONDS)); 219 return result.get(); 220 } 221 getExtras(String keyPrefix, long lval, String sval, double dval)222 private PersistableBundle getExtras(String keyPrefix, long lval, String sval, double dval) { 223 final PersistableBundle extras = new PersistableBundle(3); 224 extras.putLong(keyPrefix + ".LONG_VALUE", lval); 225 extras.putDouble(keyPrefix + ".DOUBLE_VALUE", dval); 226 extras.putString(keyPrefix + ".STRING_VALUE", sval); 227 return extras; 228 } 229 suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo)230 private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras, 231 SuspendDialogInfo dialogInfo) { 232 final String[] unchangedPackages = mPackageManager.setPackagesSuspended( 233 PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogInfo); 234 assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0); 235 } 236 unsuspendTestPackage()237 private void unsuspendTestPackage() { 238 final String[] unchangedPackages = mPackageManager.setPackagesSuspended( 239 PACKAGES_TO_SUSPEND, false, null, null, (SuspendDialogInfo) null); 240 assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0); 241 } 242 startTestAppActivity()243 private void startTestAppActivity() { 244 final Intent testActivity = new Intent() 245 .setComponent(new ComponentName(TEST_APP_PACKAGE_NAME, 246 SuspendTestActivity.class.getCanonicalName())) 247 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 248 mContext.startActivity(testActivity); 249 } 250 areSameExtras(BaseBundle expected, BaseBundle received)251 private static boolean areSameExtras(BaseBundle expected, BaseBundle received) { 252 if (expected != null) { 253 expected.get(""); // hack to unparcel the bundles. 254 } 255 if (received != null) { 256 received.get(""); 257 } 258 return BaseBundle.kindofEquals(expected, received); 259 } 260 assertSameExtras(String message, BaseBundle expected, BaseBundle received)261 private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) { 262 if (!areSameExtras(expected, received)) { 263 fail(message + ": [expected: " + expected + "; received: " + received + "]"); 264 } 265 } 266 267 @Test testIsPackageSuspended()268 public void testIsPackageSuspended() throws Exception { 269 suspendTestPackage(null, null, null); 270 assertTrue("isPackageSuspended is false", 271 mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME)); 272 } 273 274 @Test testSuspendedStateFromApp()275 public void testSuspendedStateFromApp() throws Exception { 276 Bundle resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE); 277 assertFalse(resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED, true)); 278 assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); 279 280 final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2); 281 suspendTestPackage(appExtras, null, null); 282 283 resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE); 284 assertTrue("resultFromApp:suspended is false", 285 resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED)); 286 final Bundle receivedAppExtras = 287 resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS); 288 assertSameExtras("Received app extras different to the ones supplied", 289 appExtras, receivedAppExtras); 290 } 291 292 @Test testMyPackageSuspendedUnsuspended()293 public void testMyPackageSuspendedUnsuspended() { 294 mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED, 295 ACTION_REPORT_MY_PACKAGE_UNSUSPENDED); 296 mAppCommsReceiver.drainPendingBroadcasts(); 297 final PersistableBundle appExtras = getExtras("testMyPackageSuspendBroadcasts", 1, "1", .1); 298 suspendTestPackage(appExtras, null, null); 299 Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 300 assertEquals("MY_PACKAGE_SUSPENDED delivery not reported", 301 ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction()); 302 assertSameExtras("Received app extras different to the ones supplied", appExtras, 303 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); 304 unsuspendTestPackage(); 305 intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 306 assertEquals("MY_PACKAGE_UNSUSPENDED delivery not reported", 307 ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction()); 308 } 309 310 @Test testUpdatingAppExtras()311 public void testUpdatingAppExtras() { 312 mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED); 313 final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1, 314 "1", 0.1); 315 suspendTestPackage(extras1, null, null); 316 Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 317 assertEquals("MY_PACKAGE_SUSPENDED delivery not reported", 318 ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction()); 319 assertSameExtras("Received app extras different to the ones supplied", extras1, 320 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); 321 final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2, 322 "2", 0.2); 323 suspendTestPackage(extras2, null, null); 324 intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 325 assertEquals("MY_PACKAGE_SUSPENDED delivery not reported", 326 ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction()); 327 assertSameExtras("Received app extras different to the updated extras", extras2, 328 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); 329 } 330 331 @Test testCannotSuspendSelf()332 public void testCannotSuspendSelf() { 333 final String[] unchangedPkgs = mPackageManager.setPackagesSuspended( 334 new String[]{mContext.getOpPackageName()}, true, null, null, 335 (SuspendDialogInfo) null); 336 assertTrue(unchangedPkgs.length == 1); 337 assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]); 338 } 339 340 @Test testActivityStoppedOnSuspend()341 public void testActivityStoppedOnSuspend() { 342 mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_TEST_ACTIVITY_STARTED, 343 ACTION_REPORT_TEST_ACTIVITY_STOPPED); 344 startTestAppActivity(); 345 Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 346 assertEquals("Test activity start not reported", 347 ACTION_REPORT_TEST_ACTIVITY_STARTED, intentFromApp.getAction()); 348 suspendTestPackage(null, null, null); 349 intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 350 assertEquals("Test activity stop not reported on suspending the test app", 351 ACTION_REPORT_TEST_ACTIVITY_STOPPED, intentFromApp.getAction()); 352 } 353 354 @Test testGetLauncherExtrasNonNull()355 public void testGetLauncherExtrasNonNull() { 356 final Bundle extrasWhenUnsuspended = mLauncherApps.getSuspendedPackageLauncherExtras( 357 TEST_APP_PACKAGE_NAME, mContext.getUser()); 358 assertNull("Non null extras when package unsuspended:", extrasWhenUnsuspended); 359 final PersistableBundle launcherExtras = getExtras("testGetLauncherExtras", 1, "1", 0.1); 360 suspendTestPackage(null, launcherExtras, null); 361 final Bundle receivedExtras = mLauncherApps.getSuspendedPackageLauncherExtras( 362 TEST_APP_PACKAGE_NAME, mContext.getUser()); 363 assertSameExtras("Received launcher extras different to the ones supplied", launcherExtras, 364 receivedExtras); 365 } 366 367 @Test testGetLauncherExtrasNull()368 public void testGetLauncherExtrasNull() { 369 suspendTestPackage(null, null, null); 370 final Bundle extrasWhenNoneGiven = mLauncherApps.getSuspendedPackageLauncherExtras( 371 TEST_APP_PACKAGE_NAME, mContext.getUser()); 372 assertNull("Non null extras when null extras provided:", extrasWhenNoneGiven); 373 } 374 375 @Test testGetLauncherExtrasInvalidPackage()376 public void testGetLauncherExtrasInvalidPackage() { 377 final Bundle extrasForInvalidPackage = mLauncherApps.getSuspendedPackageLauncherExtras( 378 "test.nonexistent.packagename", mContext.getUser()); 379 assertNull("Non null extras for an invalid package:", extrasForInvalidPackage); 380 } 381 382 @Test testOnPackagesSuspendedNewAndOld()383 public void testOnPackagesSuspendedNewAndOld() throws InterruptedException { 384 final PersistableBundle suppliedExtras = getExtras( 385 "testOnPackagesSuspendedNewAndOld", 2, "2", 0.2); 386 final AtomicReference<String> overridingBothCallbackResult = new AtomicReference<>(""); 387 final CountDownLatch twoCallbackLatch = new CountDownLatch(2); 388 mTestCallback = new StubbedCallback() { 389 @Override 390 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 391 overridingBothCallbackResult.set(overridingBothCallbackResult.get() 392 + "Old callback called even when the new one is overriden. "); 393 twoCallbackLatch.countDown(); 394 } 395 396 @Override 397 public void onPackagesSuspended(String[] packageNames, UserHandle user, 398 Bundle launcherExtras) { 399 final StringBuilder errorString = new StringBuilder(); 400 if (!Arrays.equals(packageNames, PACKAGES_TO_SUSPEND)) { 401 errorString.append("Received unexpected packageNames in onPackagesSuspended:"); 402 for (String packageName : packageNames) { 403 errorString.append(" " + packageName); 404 } 405 errorString.append(". "); 406 } 407 if (user.getIdentifier() != mContext.getUserId()) { 408 errorString.append("Received wrong user " + user.getIdentifier() + ". "); 409 } 410 if (!areSameExtras(launcherExtras, suppliedExtras)) { 411 errorString.append("Unexpected launcherExtras, supplied: " + suppliedExtras 412 + ", received: " + launcherExtras + ". "); 413 } 414 overridingBothCallbackResult.set(overridingBothCallbackResult.get() 415 + errorString.toString()); 416 twoCallbackLatch.countDown(); 417 } 418 }; 419 mLauncherApps.registerCallback(mTestCallback, mReceiverHandler); 420 suspendTestPackage(null, suppliedExtras, null); 421 assertFalse("Both callbacks were invoked", twoCallbackLatch.await(5, TimeUnit.SECONDS)); 422 twoCallbackLatch.countDown(); 423 assertTrue("No callback was invoked", twoCallbackLatch.await(2, TimeUnit.SECONDS)); 424 final String result = overridingBothCallbackResult.get(); 425 assertTrue("Callbacks did not complete as expected: " + result, result.isEmpty()); 426 } 427 428 @Test testOnPackagesSuspendedOld()429 public void testOnPackagesSuspendedOld() throws InterruptedException { 430 final PersistableBundle suppliedExtras = getExtras( 431 "testOnPackagesSuspendedOld", 2, "2", 0.2); 432 final AtomicReference<String> overridingOneCallbackResult = new AtomicReference<>(""); 433 final CountDownLatch oneCallbackLatch = new CountDownLatch(1); 434 mTestCallback = new StubbedCallback() { 435 @Override 436 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 437 final StringBuilder errorString = new StringBuilder(); 438 if (!Arrays.equals(packageNames, PACKAGES_TO_SUSPEND)) { 439 errorString.append("Received unexpected packageNames in onPackagesSuspended:"); 440 for (String packageName : packageNames) { 441 errorString.append(" " + packageName); 442 } 443 errorString.append(". "); 444 } 445 if (user.getIdentifier() != mContext.getUserId()) { 446 errorString.append("Received wrong user " + user.getIdentifier() + ". "); 447 } 448 overridingOneCallbackResult.set(overridingOneCallbackResult.get() 449 + errorString.toString()); 450 oneCallbackLatch.countDown(); 451 } 452 }; 453 mLauncherApps.registerCallback(mTestCallback, mReceiverHandler); 454 suspendTestPackage(null, suppliedExtras, null); 455 assertTrue("Callback not invoked", oneCallbackLatch.await(5, TimeUnit.SECONDS)); 456 final String result = overridingOneCallbackResult.get(); 457 assertTrue("Callback did not complete as expected: " + result, result.isEmpty()); 458 } 459 turnScreenOn()460 private void turnScreenOn() throws Exception { 461 if (!mUiDevice.isScreenOn()) { 462 mUiDevice.wakeUp(); 463 } 464 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 465 wm.dismissKeyguard(null, null); 466 } 467 468 @Test testInterceptorActivity()469 public void testInterceptorActivity() throws Exception { 470 turnScreenOn(); 471 mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED, 472 ACTION_REPORT_TEST_ACTIVITY_STARTED); 473 final String testMessage = "This is a test message to report suspension of %1$s"; 474 suspendTestPackage(null, null, 475 new SuspendDialogInfo.Builder().setMessage(testMessage).build()); 476 startTestAppActivity(); 477 assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2)); 478 assertNotNull("Given dialog message not shown", mUiDevice.wait( 479 Until.findObject(By.text(String.format(testMessage, TEST_APP_LABEL))), 5000)); 480 final String buttonText = mContext.getResources().getString(Resources.getSystem() 481 .getIdentifier("app_suspended_more_details", "string", "android")); 482 final UiObject2 moreDetailsButton = mUiDevice.findObject( 483 By.clickable(true).text(buttonText)); 484 assertNotNull(buttonText + " button not shown", moreDetailsButton); 485 moreDetailsButton.click(); 486 final Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 487 assertEquals(buttonText + " activity start not reported", 488 ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED, intentFromApp.getAction()); 489 final String receivedPackageName = intentFromApp.getStringExtra( 490 EXTRA_RECEIVED_PACKAGE_NAME); 491 assertEquals("Wrong package name received by " + buttonText + " activity", 492 TEST_APP_PACKAGE_NAME, receivedPackageName); 493 } 494 setProfileOwner()495 private boolean setProfileOwner() throws IOException { 496 final String result = mUiDevice.executeShellCommand("dpm set-profile-owner --user cur " 497 + mDeviceAdminComponent.flattenToString()); 498 return mPoSet = result.trim().startsWith("Success"); 499 } 500 setDeviceOwner()501 private boolean setDeviceOwner() throws IOException { 502 final String result = mUiDevice.executeShellCommand("dpm set-device-owner --user cur " 503 + mDeviceAdminComponent.flattenToString()); 504 return mDoSet = result.trim().startsWith("Success"); 505 } 506 removeProfileOrDeviceOwner()507 private void removeProfileOrDeviceOwner() throws IOException { 508 if (mPoSet || mDoSet) { 509 mUiDevice.executeShellCommand("dpm remove-active-admin --user cur " 510 + mDeviceAdminComponent.flattenToString()); 511 mPoSet = mDoSet = false; 512 } 513 } 514 515 @Test testCanSuspendWhenProfileOwner()516 public void testCanSuspendWhenProfileOwner() throws IOException { 517 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)); 518 assertTrue("Profile-owner could not be set", setProfileOwner()); 519 suspendTestPackage(null, null, null); 520 } 521 522 @Test testCanSuspendWhenDeviceOwner()523 public void testCanSuspendWhenDeviceOwner() throws IOException { 524 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)); 525 assertTrue("Device-owner could not be set", setDeviceOwner()); 526 suspendTestPackage(null, null, null); 527 } 528 529 @Test testPackageUnsuspendedOnAddingDeviceOwner()530 public void testPackageUnsuspendedOnAddingDeviceOwner() throws IOException { 531 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)); 532 mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, 533 ACTION_REPORT_MY_PACKAGE_SUSPENDED); 534 mAppCommsReceiver.drainPendingBroadcasts(); 535 suspendTestPackage(null, null, null); 536 Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 537 assertEquals(ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction()); 538 assertTrue("Device-owner could not be set", setDeviceOwner()); 539 intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 540 assertEquals(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction()); 541 } 542 543 @Test testPackageUnsuspendedOnAddingProfileOwner()544 public void testPackageUnsuspendedOnAddingProfileOwner() throws IOException { 545 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)); 546 mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, 547 ACTION_REPORT_MY_PACKAGE_SUSPENDED); 548 mAppCommsReceiver.drainPendingBroadcasts(); 549 suspendTestPackage(null, null, null); 550 Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 551 assertEquals(ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction()); 552 assertTrue("Profile-owner could not be set", setProfileOwner()); 553 intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); 554 assertEquals(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction()); 555 } 556 557 @Test testCameraBlockedOnSuspend()558 public void testCameraBlockedOnSuspend() throws Exception { 559 assertOpBlockedOnSuspend(OP_CAMERA); 560 } 561 562 @Test testPlayAudioBlockedOnSuspend()563 public void testPlayAudioBlockedOnSuspend() throws Exception { 564 assertOpBlockedOnSuspend(OP_PLAY_AUDIO); 565 } 566 567 @Test testRecordAudioBlockedOnSuspend()568 public void testRecordAudioBlockedOnSuspend() throws Exception { 569 assertOpBlockedOnSuspend(OP_RECORD_AUDIO); 570 } 571 assertOpBlockedOnSuspend(int code)572 private void assertOpBlockedOnSuspend(int code) throws Exception { 573 final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface( 574 ServiceManager.getService(Context.APP_OPS_SERVICE)); 575 final CountDownLatch latch = new CountDownLatch(1); 576 final IAppOpsCallback watcher = new IAppOpsCallback.Stub() { 577 @Override 578 public void opChanged(int op, int uid, String packageName) { 579 if (op == code && packageName.equals(TEST_APP_PACKAGE_NAME)) { 580 latch.countDown(); 581 } 582 } 583 }; 584 iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher); 585 final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0); 586 int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME); 587 assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED, 588 opMode); 589 suspendTestPackage(null, null, null); 590 assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS)); 591 opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME); 592 assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED, 593 opMode); 594 iAppOps.stopWatchingMode(watcher); 595 } 596 597 @After tearDown()598 public void tearDown() throws IOException { 599 mAppCommsReceiver.unregister(); 600 if (mTestCallback != null) { 601 mLauncherApps.unregisterCallback(mTestCallback); 602 } 603 removeProfileOrDeviceOwner(); 604 mContext.sendBroadcast(new Intent(ACTION_FINISH_TEST_ACTIVITY) 605 .setPackage(TEST_APP_PACKAGE_NAME)); 606 } 607 608 private static abstract class StubbedCallback extends LauncherApps.Callback { 609 610 @Override onPackageRemoved(String packageName, UserHandle user)611 public void onPackageRemoved(String packageName, UserHandle user) { 612 } 613 614 @Override onPackageAdded(String packageName, UserHandle user)615 public void onPackageAdded(String packageName, UserHandle user) { 616 } 617 618 @Override onPackageChanged(String packageName, UserHandle user)619 public void onPackageChanged(String packageName, UserHandle user) { 620 } 621 622 @Override onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)623 public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) { 624 625 } 626 627 @Override onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)628 public void onPackagesUnavailable(String[] packageNames, UserHandle user, 629 boolean replacing) { 630 } 631 } 632 } 633