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