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