diff options
| author | 2018-03-28 00:45:23 +0000 | |
|---|---|---|
| committer | 2018-03-28 00:45:23 +0000 | |
| commit | 1c73370df4c6b8399a264923e1f370b64019fca3 (patch) | |
| tree | 80ee61ec58811aa205c5404e80fff231a8604631 | |
| parent | 2ae75ba1abd6f02415f93e21ff805e9bdbc5372b (diff) | |
| parent | 2fd43ba63ef336f9e0edc9c742b85507c46b3bc9 (diff) | |
Merge "Sending MY_PACKAGE_SUSPENDED to suspended apps" into pi-dev
11 files changed, 328 insertions, 48 deletions
diff --git a/api/current.txt b/api/current.txt index d721936f4aa6..052b7f8e7101 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9905,6 +9905,8 @@ package android.content { field public static final java.lang.String ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE"; field public static final java.lang.String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED"; field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED"; + field public static final java.lang.String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED"; + field public static final java.lang.String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED"; field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL"; field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT"; field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; @@ -10065,6 +10067,7 @@ package android.content { field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME"; field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM"; field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT"; + field public static final java.lang.String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS"; field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE"; field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT"; field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE"; @@ -11178,7 +11181,7 @@ package android.content.pm { method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int); - method public android.os.PersistableBundle getSuspendedPackageAppExtras(); + method public android.os.Bundle getSuspendedPackageAppExtras(); method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); method public abstract java.lang.String[] getSystemSharedLibraryNames(); method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index f38c80cb89cd..a68136b531c4 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2167,15 +2167,16 @@ public class ApplicationPackageManager extends PackageManager { @Override public PersistableBundle getSuspendedPackageAppExtras(String packageName) { try { - return mPM.getPackageSuspendedAppExtras(packageName, mContext.getUserId()); + return mPM.getSuspendedPackageAppExtras(packageName, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override - public PersistableBundle getSuspendedPackageAppExtras() { - return getSuspendedPackageAppExtras(mContext.getOpPackageName()); + public Bundle getSuspendedPackageAppExtras() { + final PersistableBundle extras = getSuspendedPackageAppExtras(mContext.getOpPackageName()); + return extras != null ? new Bundle(extras.deepCopy()) : null; } @Override diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ce322785c229..02f0ded3fa92 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1814,6 +1814,17 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; /** + * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent with + * {@link #ACTION_MY_PACKAGE_SUSPENDED}. + * + * @see #ACTION_MY_PACKAGE_SUSPENDED + * @see #ACTION_MY_PACKAGE_UNSUSPENDED + * @see PackageManager#isPackageSuspended() + * @see PackageManager#getSuspendedPackageAppExtras() + */ + public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS"; + + /** * Intent extra: An app split name. * <p> * Type: String @@ -2237,6 +2248,43 @@ public class Intent implements Parcelable, Cloneable { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED"; + + /** + * Broadcast Action: Sent to a package that has been suspended by the system. This is sent + * whenever a package is put into a suspended state or any of it's app extras change while + * in the suspended state. + * <p> Optionally includes the following extras: + * <ul> + * <li> {@link #EXTRA_SUSPENDED_PACKAGE_EXTRAS} which is a {@link Bundle} which will contain + * useful information for the app being suspended. + * </ul> + * <p class="note">This is a protected intent that can only be sent + * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in + * the manifest.</em> + * + * @see #ACTION_MY_PACKAGE_UNSUSPENDED + * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS + * @see PackageManager#isPackageSuspended() + * @see PackageManager#getSuspendedPackageAppExtras() + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED"; + + /** + * Broadcast Action: Sent to a package that has been unsuspended. + * + * <p class="note">This is a protected intent that can only be sent + * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in + * the manifest.</em> + * + * @see #ACTION_MY_PACKAGE_SUSPENDED + * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS + * @see PackageManager#isPackageSuspended() + * @see PackageManager#getSuspendedPackageAppExtras() + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED"; + /** * Broadcast Action: A user ID has been removed from the system. The user * ID number is stored in the extra data under {@link #EXTRA_UID}. diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index d43d80f0f678..1352b5eaa1e3 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -278,7 +278,7 @@ interface IPackageManager { boolean isPackageSuspendedForUser(String packageName, int userId); - PersistableBundle getPackageSuspendedAppExtras(String pacakgeName, int userId); + PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId); void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras, int userId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 314eb985a3e1..491f0af2576d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5513,7 +5513,7 @@ public abstract class PackageManager { * Puts the package in a suspended state, where attempts at starting activities are denied. * * <p>It doesn't remove the data or the actual package file. The application's notifications - * will be hidden, any of the it's started activities will be stopped and it will not be able to + * will be hidden, any of its started activities will be stopped and it will not be able to * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a * system dialog with the given {@code dialogMessage} will be shown instead.</p> * @@ -5577,11 +5577,26 @@ public abstract class PackageManager { } /** - * Apps can query this to know if they have been suspended. + * Apps can query this to know if they have been suspended. A system app with the permission + * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state. + * + * <p>While in this state, the application's notifications will be hidden, any of its started + * activities will be stopped and it will not be able to show toasts or dialogs or ring the + * device. When the user tries to launch a suspended app, the system will, instead, show a + * dialog to the user informing them that they cannot use this app while it is suspended. + * + * <p>When an app is put into this state, the broadcast action + * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast + * receivers that included this action in their intent-filters, <em>including manifest + * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED} + * is delivered when a previously suspended app is taken out of this state. + * </p> * * @return {@code true} if the calling package has been suspended, {@code false} otherwise. * * @see #getSuspendedPackageAppExtras() + * @see Intent#ACTION_MY_PACKAGE_SUSPENDED + * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED */ public boolean isPackageSuspended() { throw new UnsupportedOperationException("isPackageSuspended not implemented"); @@ -5602,7 +5617,7 @@ public abstract class PackageManager { */ @SystemApi @RequiresPermission(Manifest.permission.SUSPEND_APPS) - public PersistableBundle getSuspendedPackageAppExtras(String packageName) { + public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) { throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented"); } @@ -5631,15 +5646,17 @@ public abstract class PackageManager { * Returns any extra information supplied as {@code appExtras} to the system when the calling * app was suspended. * - * <p> Note: This just returns whatever {@link PersistableBundle} was passed to the system via - * {@code setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, - * String)} when suspending the package, <em> which might be {@code null}. </em></p> + * <p>Note: If no extras were supplied to the system, this method will return {@code null}, even + * when the calling app has been suspended.</p> * - * @return A {@link PersistableBundle} containing the extras for the app, or {@code null} if the + * @return A {@link Bundle} containing the extras for the app, or {@code null} if the * package is not currently suspended. + * * @see #isPackageSuspended() + * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED + * @see Intent#ACTION_MY_PACKAGE_SUSPENDED */ - public @Nullable PersistableBundle getSuspendedPackageAppExtras() { + public @Nullable Bundle getSuspendedPackageAppExtras() { throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented"); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3fb52dc2a94e..1c3448bef8a8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -89,6 +89,8 @@ <protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" /> <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" /> <protected-broadcast android:name="android.intent.action.USER_ACTIVITY_NOTIFICATION" /> + <protected-broadcast android:name="android.intent.action.MY_PACKAGE_SUSPENDED" /> + <protected-broadcast android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" /> <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" /> <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" /> diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9b67bbd9eb29..e29a55b0cef2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -92,6 +92,7 @@ import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; +import static com.android.internal.util.ArrayUtils.appendElement; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; @@ -13987,18 +13988,15 @@ public class PackageManagerService extends IPackageManager.Stub return packageNames; } - // List of package names for whom the suspended state has changed. - final List<String> changedPackages = new ArrayList<>(packageNames.length); - // List of package names for whom the suspended state is not set as requested in this - // method. + final List<String> changedPackagesList = new ArrayList<>(packageNames.length); final List<String> unactionedPackages = new ArrayList<>(packageNames.length); final long callingId = Binder.clearCallingIdentity(); try { synchronized (mPackages) { for (int i = 0; i < packageNames.length; i++) { final String packageName = packageNames[i]; - if (packageName == callingPackage) { - Slog.w(TAG, "Calling package: " + callingPackage + "trying to " + if (callingPackage.equals(packageName)) { + Slog.w(TAG, "Calling package: " + callingPackage + " trying to " + (suspended ? "" : "un") + "suspend itself. Ignoring"); unactionedPackages.add(packageName); continue; @@ -14018,17 +14016,18 @@ public class PackageManagerService extends IPackageManager.Stub } pkgSetting.setSuspended(suspended, callingPackage, appExtras, launcherExtras, userId); - changedPackages.add(packageName); + changedPackagesList.add(packageName); } } } } finally { Binder.restoreCallingIdentity(callingId); } - // TODO (b/75036698): Also send each package a broadcast when suspended state changed - if (!changedPackages.isEmpty()) { - sendPackagesSuspendedForUser(changedPackages.toArray( - new String[changedPackages.size()]), userId, suspended); + if (!changedPackagesList.isEmpty()) { + final String[] changedPackages = changedPackagesList.toArray( + new String[changedPackagesList.size()]); + sendPackagesSuspendedForUser(changedPackages, userId, suspended); + sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId); synchronized (mPackages) { scheduleWritePackageRestrictionsLocked(userId); } @@ -14038,7 +14037,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public PersistableBundle getPackageSuspendedAppExtras(String packageName, int userId) { + public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); if (getPackageUid(packageName, 0, userId) != callingUid) { mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null); @@ -14049,7 +14048,10 @@ public class PackageManagerService extends IPackageManager.Stub throw new IllegalArgumentException("Unknown target package: " + packageName); } final PackageUserState packageUserState = ps.readUserState(userId); - return packageUserState.suspended ? packageUserState.suspendedAppExtras : null; + if (packageUserState.suspended) { + return packageUserState.suspendedAppExtras; + } + return null; } } @@ -14065,10 +14067,47 @@ public class PackageManagerService extends IPackageManager.Stub } final PackageUserState packageUserState = ps.readUserState(userId); if (packageUserState.suspended) { - // TODO (b/75036698): Also send this package a broadcast with the new app extras packageUserState.suspendedAppExtras = appExtras; + sendMyPackageSuspendedOrUnsuspended(new String[] {packageName}, true, appExtras, + userId); + } + } + } + + private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended, + PersistableBundle appExtras, int userId) { + final String action; + final Bundle intentExtras = new Bundle(); + if (suspended) { + action = Intent.ACTION_MY_PACKAGE_SUSPENDED; + if (appExtras != null) { + final Bundle bundledAppExtras = new Bundle(appExtras.deepCopy()); + intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, bundledAppExtras); } + } else { + action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED; } + mHandler.post(new Runnable() { + @Override + public void run() { + try { + final IActivityManager am = ActivityManager.getService(); + if (am == null) { + Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ " + + (suspended ? "" : "UN") + "SUSPENDED broadcasts"); + return; + } + final int[] targetUserIds = new int[] {userId}; + for (String packageName : affectedPackages) { + doSendBroadcast(am, action, null, intentExtras, + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null, + targetUserIds, false); + } + } catch (RemoteException ex) { + // Shouldn't happen as AMS is in the same process. + } + } + }); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java index d702318761d1..ee0ad39e9575 100644 --- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java @@ -16,55 +16,129 @@ package com.android.server.pm; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.app.AppGlobals; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.BaseBundle; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.PersistableBundle; +import android.os.RemoteException; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.MediumTest; +import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @RunWith(AndroidJUnit4.class) -@MediumTest +@LargeTest public class SuspendPackagesTest { private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME; private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME}; + public static final String INSTRUMENTATION_PACKAGE = "com.android.frameworks.servicestests"; + public static final String ACTION_REPORT_MY_PACKAGE_SUSPENDED = + INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED"; + public static final String ACTION_REPORT_MY_PACKAGE_UNSUSPENDED = + INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_UNSUSPENDED"; + private Context mContext; private PackageManager mPackageManager; private Handler mReceiverHandler; private ComponentName mTestReceiverComponent; + private AppCommunicationReceiver mAppCommsReceiver; + + private static final class AppCommunicationReceiver extends BroadcastReceiver { + private Context context; + private boolean registered; + private SynchronousQueue<Intent> intentQueue = new SynchronousQueue<>(); + + AppCommunicationReceiver(Context context) { + this.context = context; + } + + void register(Handler handler) { + registered = true; + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_SUSPENDED); + intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED); + context.registerReceiver(this, intentFilter, null, handler); + } + + void unregister() { + if (registered) { + context.unregisterReceiver(this); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + try { + intentQueue.offer(intent, 5, TimeUnit.SECONDS); + } catch (InterruptedException ie) { + throw new RuntimeException("Receiver thread interrupted", ie); + } + } + + Intent receiveIntentFromApp() { + if (!registered) { + throw new IllegalStateException("Receiver not registered"); + } + final Intent intent; + try { + intent = intentQueue.poll(5, TimeUnit.SECONDS); + } catch (InterruptedException ie) { + throw new RuntimeException("Interrupted while waiting for app broadcast", ie); + } + assertNotNull("No intent received from app within 5 seconds", intent); + return intent; + } + } @Before public void setUp() { mContext = InstrumentationRegistry.getTargetContext(); mPackageManager = mContext.getPackageManager(); - mPackageManager.setPackagesSuspended(PACKAGES_TO_SUSPEND, false, null, null, null); mReceiverHandler = new Handler(Looper.getMainLooper()); mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME, SuspendTestReceiver.class.getCanonicalName()); + IPackageManager ipm = AppGlobals.getPackageManager(); + try { + // Otherwise implicit broadcasts will not be delivered. + ipm.setPackageStoppedState(TEST_APP_PACKAGE_NAME, false, mContext.getUserId()); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + unsuspendTestPackage(); + mAppCommsReceiver = new AppCommunicationReceiver(mContext); } + /** + * Care should be taken when used with {@link #mAppCommsReceiver} in the same test as both use + * the same handler. + */ private Bundle requestAppAction(String action) throws InterruptedException { final AtomicReference<Bundle> result = new AtomicReference<>(); final CountDownLatch receiverLatch = new CountDownLatch(1); @@ -98,6 +172,24 @@ public class SuspendPackagesTest { assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0); } + private void unsuspendTestPackage() { + final String[] unchangedPackages = mPackageManager.setPackagesSuspended( + PACKAGES_TO_SUSPEND, false, null, null, null); + assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0); + } + + + private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) { + if (expected != null) { + expected.get(""); // hack to unparcel the bundles. + } + if (received != null) { + received.get(""); + } + assertTrue(message + ": [expected: " + expected + "; received: " + received + "]", + BaseBundle.kindofEquals(expected, received)); + } + @Test public void testIsPackageSuspended() { suspendTestPackage(null, null); @@ -109,19 +201,73 @@ public class SuspendPackagesTest { public void testSuspendedStateFromApp() throws Exception { Bundle resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE); assertFalse(resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED, true)); - assertNull(resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); + assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); - final PersistableBundle appExtras = getExtras("appExtras", 20, "20", 0.2); + final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2); suspendTestPackage(appExtras, null); resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE); assertTrue("resultFromApp:suspended is false", resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED)); - final PersistableBundle receivedAppExtras = - resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS); - receivedAppExtras.get(""); // hack to unparcel the bundles - appExtras.get(""); - assertTrue("Received app extras " + receivedAppExtras + " different to the ones supplied", - BaseBundle.kindofEquals(appExtras, receivedAppExtras)); + final Bundle receivedAppExtras = + resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS); + assertSameExtras("Received app extras different to the ones supplied", + appExtras, receivedAppExtras); + } + + @Test + public void testMyPackageSuspendedUnsuspended() { + mAppCommsReceiver.register(mReceiverHandler); + final PersistableBundle appExtras = getExtras("testMyPackageSuspendBroadcasts", 1, "1", .1); + suspendTestPackage(appExtras, null); + Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); + assertTrue("MY_PACKAGE_SUSPENDED delivery not reported", + ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction())); + assertSameExtras("Received app extras different to the ones supplied", appExtras, + intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); + unsuspendTestPackage(); + intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); + assertTrue("MY_PACKAGE_UNSUSPENDED delivery not reported", + ACTION_REPORT_MY_PACKAGE_UNSUSPENDED.equals(intentFromApp.getAction())); + } + + @Test + public void testUpdatingAppExtras() { + mAppCommsReceiver.register(mReceiverHandler); + final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1, + "1", 0.1); + suspendTestPackage(extras1, null); + Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); + assertTrue("MY_PACKAGE_SUSPENDED delivery not reported", + ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction())); + assertSameExtras("Received app extras different to the ones supplied", extras1, + intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); + final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2, + "2", 0.2); + mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2); + intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); + assertTrue("MY_PACKAGE_SUSPENDED delivery not reported", + ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction())); + assertSameExtras("Received app extras different to the updated extras", extras2, + intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); + } + + @Test + public void testCannotSuspendSelf() { + final String[] unchangedPkgs = mPackageManager.setPackagesSuspended( + new String[]{mContext.getOpPackageName()}, true, null, null, null); + assertTrue(unchangedPkgs.length == 1); + assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]); + } + + @After + public void tearDown() throws Exception { + mAppCommsReceiver.unregister(); + Thread.sleep(250); // To prevent any race with the next registerReceiver + } + + @FunctionalInterface + interface Condition { + boolean isTrue(); } } diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk index 40a34b945f44..afdde727b7ef 100644 --- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk +++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk @@ -17,14 +17,18 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test + LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java LOCAL_PACKAGE_NAME := SuspendTestApp LOCAL_DEX_PREOPT := false LOCAL_PROGUARD_ENABLED := disabled +LOCAL_PRIVATE_PLATFORM_APIS := true + include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml index 70a1fd0e6430..ce6a27aba040 100644 --- a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml +++ b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml @@ -21,7 +21,12 @@ <activity android:name=".SuspendTestActivity" android:exported="true" /> <receiver android:name=".SuspendTestReceiver" - android:exported="true" /> + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MY_PACKAGE_SUSPENDED" /> + <action android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" /> + </intent-filter> + </receiver> </application> </manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java index 6f353a008bea..90a9f01a12cb 100644 --- a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java +++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java @@ -16,12 +16,15 @@ package com.android.servicestests.apps.suspendtestapp; +import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_MY_PACKAGE_SUSPENDED; +import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_MY_PACKAGE_UNSUSPENDED; +import static com.android.server.pm.SuspendPackagesTest.INSTRUMENTATION_PACKAGE; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.os.PersistableBundle; import android.util.Log; public class SuspendTestReceiver extends BroadcastReceiver { @@ -34,21 +37,33 @@ public class SuspendTestReceiver extends BroadcastReceiver { public static final String EXTRA_SUSPENDED_APP_EXTRAS = PACKAGE_NAME + ".extra.SUSPENDED_APP_EXTRAS"; - private PackageManager mPm; - @Override public void onReceive(Context context, Intent intent) { - mPm = context.getPackageManager(); - Log.d(TAG, "Received request action " + intent.getAction()); + final PackageManager packageManager = context.getPackageManager(); + Log.d(TAG, "Received action " + intent.getAction()); + final Bundle appExtras; switch (intent.getAction()) { case ACTION_GET_SUSPENDED_STATE: final Bundle result = new Bundle(); - final boolean suspended = mPm.isPackageSuspended(); - final PersistableBundle appExtras = mPm.getSuspendedPackageAppExtras(); + final boolean suspended = packageManager.isPackageSuspended(); + appExtras = packageManager.getSuspendedPackageAppExtras(); result.putBoolean(EXTRA_SUSPENDED, suspended); - result.putParcelable(EXTRA_SUSPENDED_APP_EXTRAS, appExtras); + result.putBundle(EXTRA_SUSPENDED_APP_EXTRAS, appExtras); setResult(0, null, result); break; + case Intent.ACTION_MY_PACKAGE_SUSPENDED: + appExtras = intent.getBundleExtra(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS); + final Intent reportSuspendIntent = new Intent(ACTION_REPORT_MY_PACKAGE_SUSPENDED) + .putExtra(EXTRA_SUSPENDED_APP_EXTRAS, appExtras) + .setPackage(INSTRUMENTATION_PACKAGE); + context.sendBroadcast(reportSuspendIntent); + break; + case Intent.ACTION_MY_PACKAGE_UNSUSPENDED: + final Intent reportUnsuspendIntent = + new Intent(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED) + .setPackage(INSTRUMENTATION_PACKAGE); + context.sendBroadcast(reportUnsuspendIntent); + break; default: Log.e(TAG, "Unknown action: " + intent.getAction()); } |