Merge "Sending MY_PACKAGE_SUSPENDED to suspended apps" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1c73370df4
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1813,6 +1813,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>
|
||||
@@ -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}.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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,12 +14067,49 @@ 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
|
||||
public boolean isPackageSuspendedForUser(String packageName, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user