• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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