Merge "Adding LauncherApps apis for suspended packages" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-03-31 00:39:52 +00:00
committed by Android (Google) Code Review
10 changed files with 278 additions and 20 deletions

View File

@@ -10913,6 +10913,7 @@ package android.content.pm {
method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
method public android.os.Bundle getSuspendedPackageLauncherExtras(java.lang.String, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
@@ -10937,6 +10938,7 @@ package android.content.pm {
method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
method public void onPackagesSuspended(java.lang.String[], android.os.Bundle, android.os.UserHandle);
method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);

View File

@@ -1851,6 +1851,17 @@ public class Intent implements Parcelable, Cloneable {
@SystemApi
public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
/**
* Intent extra: A {@link Bundle} of extras supplied for the launcher when any packages on
* device are suspended. Will be sent with {@link #ACTION_PACKAGES_SUSPENDED}.
*
* @see PackageManager#isPackageSuspended()
* @see #ACTION_PACKAGES_SUSPENDED
*
* @hide
*/
public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS";
/**
* Activity action: Launch UI to manage which apps have a given permission.
* <p>
@@ -2251,8 +2262,8 @@ public class Intent implements Parcelable, Cloneable {
/**
* 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.
* whenever a package is put into a suspended state or any of its 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

View File

@@ -49,6 +49,7 @@ interface ILauncherApps {
String callingPackage, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user);
Bundle getSuspendedPackageLauncherExtras(String packageName, in UserHandle user);
boolean isActivityEnabled(
String callingPackage, in ComponentName component, in UserHandle user);
ApplicationInfo getApplicationInfo(

View File

@@ -17,6 +17,7 @@
package android.content.pm;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.UserHandle;
/**
@@ -28,7 +29,8 @@ oneway interface IOnAppsChangedListener {
void onPackageChanged(in UserHandle user, String packageName);
void onPackagesAvailable(in UserHandle user, in String[] packageNames, boolean replacing);
void onPackagesUnavailable(in UserHandle user, in String[] packageNames, boolean replacing);
void onPackagesSuspended(in UserHandle user, in String[] packageNames);
void onPackagesSuspended(in UserHandle user, in String[] packageNames,
in Bundle launcherExtras);
void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
}

View File

@@ -211,6 +211,10 @@ public class LauncherApps {
* example, this can happen when a Device Administrator suspends
* an applicaton.
*
* <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
* any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will
* not receive this callback.
*
* @param packageNames The names of the packages that have just been
* suspended.
* @param user The UserHandle of the profile that generated the change.
@@ -218,6 +222,22 @@ public class LauncherApps {
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
}
/**
* Indicates that one or more packages have been suspended. A device administrator or an app
* with {@code android.permission.SUSPEND_APPS} can do this.
*
* @param packageNames The names of the packages that have just been suspended.
* @param launcherExtras A {@link Bundle} of extras for the launcher.
* @param user the user for which the given packages were suspended.
*
* @see PackageManager#isPackageSuspended()
* @see #getSuspendedPackageLauncherExtras(String, UserHandle)
*/
public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras,
UserHandle user) {
onPackagesSuspended(packageNames, user);
}
/**
* Indicates that one or more packages have been unsuspended. For
* example, this can happen when a Device Administrator unsuspends
@@ -637,6 +657,31 @@ public class LauncherApps {
}
}
/**
* Gets the launcher extras supplied to the system when the given package was suspended via
* {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, String)}.
*
* <p>Note: This just returns whatever extras were provided to the system, <em>which might
* even be {@code null}.</em>
*
* @param packageName The package for which to fetch the launcher extras.
* @param user The {@link UserHandle} of the profile.
* @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
* suspended.
*
* @see Callback#onPackagesSuspended(String[], Bundle, UserHandle)
* @see PackageManager#isPackageSuspended()
*/
public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
return mService.getSuspendedPackageLauncherExtras(packageName, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
* Returns {@link ApplicationInfo} about an application installed for a specific user profile.
*
@@ -652,7 +697,7 @@ public class LauncherApps {
@ApplicationInfoFlags int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(packageName, "user");
Preconditions.checkNotNull(user, "user");
logErrorForInvalidProfileAccess(user);
try {
final ApplicationInfo ai = mService
@@ -1163,14 +1208,15 @@ public class LauncherApps {
}
@Override
public void onPackagesSuspended(UserHandle user, String[] packageNames)
public void onPackagesSuspended(UserHandle user, String[] packageNames,
Bundle launcherExtras)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackagesSuspended(packageNames, user);
callback.postOnPackagesSuspended(packageNames, launcherExtras, user);
}
}
}
@@ -1218,6 +1264,7 @@ public class LauncherApps {
private static class CallbackInfo {
String[] packageNames;
String packageName;
Bundle launcherExtras;
boolean replacing;
UserHandle user;
List<ShortcutInfo> shortcuts;
@@ -1251,7 +1298,8 @@ public class LauncherApps {
mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
break;
case MSG_SUSPENDED:
mCallback.onPackagesSuspended(info.packageNames, info.user);
mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras,
info.user);
break;
case MSG_UNSUSPENDED:
mCallback.onPackagesUnsuspended(info.packageNames, info.user);
@@ -1301,10 +1349,12 @@ public class LauncherApps {
obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
}
public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras,
UserHandle user) {
CallbackInfo info = new CallbackInfo();
info.packageNames = packageNames;
info.user = user;
info.launcherExtras = launcherExtras;
obtainMessage(MSG_SUSPENDED, info).sendToTarget();
}

View File

@@ -26,6 +26,7 @@ import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.SparseArray;
import java.lang.annotation.Retention;
@@ -187,6 +188,22 @@ public abstract class PackageManagerInternal {
public abstract PackageInfo getPackageInfo(String packageName,
@PackageInfoFlags int flags, int filterCallingUid, int userId);
/**
* Retrieve launcher extras for a suspended package provided to the system in
* {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, String)}
*
* @param packageName The package for which to return launcher extras.
* @param userId The user for which to check,
* @return The launcher extras.
*
* @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, String)
* @see PackageManager#isPackageSuspended()
*/
public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
int userId);
/**
* Do a straight uid lookup for the given package/application in the given user.
* @see PackageManager#getPackageUidAsUser(String, int, int)

View File

@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -196,6 +197,10 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
public void onPackagesSuspended(String[] packages) {
}
public void onPackagesSuspended(String[] packages, Bundle launcherExtras) {
onPackagesSuspended(packages);
}
public void onPackagesUnsuspended(String[] packages) {
}
@@ -433,8 +438,9 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
}
} else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) {
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
final Bundle launcherExtras = intent.getBundleExtra(Intent.EXTRA_LAUNCHER_EXTRAS);
mSomePackagesChanged = true;
onPackagesSuspended(pkgList);
onPackagesSuspended(pkgList, launcherExtras);
} else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) {
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
mSomePackagesChanged = true;

View File

@@ -363,6 +363,17 @@ public class LauncherAppsService extends SystemService {
}
}
@Override
public Bundle getSuspendedPackageLauncherExtras(String packageName,
UserHandle user) {
if (!canAccessProfile(user.getIdentifier(), "Cannot get launcher extras")) {
return null;
}
final PackageManagerInternal pmi =
LocalServices.getService(PackageManagerInternal.class);
return pmi.getSuspendedPackageLauncherExtras(packageName, user.getIdentifier());
}
@Override
public ApplicationInfo getApplicationInfo(
String callingPackage, String packageName, int flags, UserHandle user)
@@ -732,7 +743,7 @@ public class LauncherAppsService extends SystemService {
}
@Override
public void onPackagesSuspended(String[] packages) {
public void onPackagesSuspended(String[] packages, Bundle launcherExtras) {
UserHandle user = new UserHandle(getChangingUserId());
final int n = mListeners.beginBroadcast();
try {
@@ -741,7 +752,7 @@ public class LauncherAppsService extends SystemService {
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) continue;
try {
listener.onPackagesSuspended(user, packages);
listener.onPackagesSuspended(user, packages, launcherExtras);
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}
@@ -750,7 +761,7 @@ public class LauncherAppsService extends SystemService {
mListeners.finishBroadcast();
}
super.onPackagesSuspended(packages);
super.onPackagesSuspended(packages, launcherExtras);
}
@Override

View File

@@ -13818,11 +13818,15 @@ public class PackageManagerService extends IPackageManager.Stub
info.sendPackageRemovedBroadcasts(true /*killApp*/);
}
private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended) {
private void sendPackagesSuspendedForUser(String[] pkgList, int userId, boolean suspended,
PersistableBundle launcherExtras) {
if (pkgList.length > 0) {
Bundle extras = new Bundle(1);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
if (launcherExtras != null) {
extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
new Bundle(launcherExtras.deepCopy()));
}
sendPackageBroadcast(
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
@@ -14034,7 +14038,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
sendPackagesSuspendedForUser(changedPackages, userId, suspended);
sendPackagesSuspendedForUser(changedPackages, userId, suspended, launcherExtras);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId);
synchronized (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
@@ -23743,6 +23747,18 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
flags, filterCallingUid, userId);
}
@Override
public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
PersistableBundle launcherExtras = null;
if (ps != null) {
launcherExtras = ps.readUserState(userId).suspendedLauncherExtras;
}
return (launcherExtras != null) ? new Bundle(launcherExtras.deepCopy()) : null;
}
}
@Override
public int getPackageUid(String packageName, int flags, int userId) {
return PackageManagerService.this

View File

@@ -29,6 +29,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.BaseBundle;
import android.os.Bundle;
@@ -36,9 +37,11 @@ import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
@@ -47,6 +50,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
@@ -55,6 +59,7 @@ import java.util.concurrent.atomic.AtomicReference;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SuspendPackagesTest {
private static final String TAG = SuspendPackagesTest.class.getSimpleName();
private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME;
private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME};
@@ -66,9 +71,11 @@ public class SuspendPackagesTest {
private Context mContext;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private Handler mReceiverHandler;
private ComponentName mTestReceiverComponent;
private AppCommunicationReceiver mAppCommsReceiver;
private StubbedCallback mTestCallback;
private static final class AppCommunicationReceiver extends BroadcastReceiver {
private Context context;
@@ -95,6 +102,7 @@ public class SuspendPackagesTest {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "AppCommunicationReceiver#onReceive: " + intent.getAction());
try {
intentQueue.offer(intent, 5, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
@@ -121,6 +129,7 @@ public class SuspendPackagesTest {
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mPackageManager = mContext.getPackageManager();
mLauncherApps = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
mReceiverHandler = new Handler(Looper.getMainLooper());
mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
SuspendTestReceiver.class.getCanonicalName());
@@ -179,15 +188,19 @@ public class SuspendPackagesTest {
}
private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) {
private static boolean areSameExtras(BaseBundle expected, BaseBundle received) {
if (expected != null) {
expected.get(""); // hack to unparcel the bundles.
}
if (received != null) {
received.get("");
}
return BaseBundle.kindofEquals(expected, received);
}
private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) {
assertTrue(message + ": [expected: " + expected + "; received: " + received + "]",
BaseBundle.kindofEquals(expected, received));
areSameExtras(expected, received));
}
@Test
@@ -260,14 +273,143 @@ public class SuspendPackagesTest {
assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]);
}
@Test
public void testGetLauncherExtrasNonNull() {
final Bundle extrasWhenUnsuspended = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser());
assertNull("Non null extras when package unsuspended:", extrasWhenUnsuspended);
final PersistableBundle launcherExtras = getExtras("testGetLauncherExtras", 1, "1", 0.1);
suspendTestPackage(null, launcherExtras);
final Bundle receivedExtras = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser());
assertSameExtras("Received launcher extras different to the ones supplied", launcherExtras,
receivedExtras);
}
@Test
public void testGetLauncherExtrasNull() {
suspendTestPackage(null, null);
final Bundle extrasWhenNoneGiven = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser());
assertNull("Non null extras when null extras provided:", extrasWhenNoneGiven);
}
@Test
public void testGetLauncherExtrasInvalidPackage() {
final Bundle extrasForInvalidPackage = mLauncherApps.getSuspendedPackageLauncherExtras(
"test.nonexistent.packagename", mContext.getUser());
assertNull("Non null extras for an invalid package:", extrasForInvalidPackage);
}
@Test
public void testOnPackagesSuspendedNewAndOld() throws InterruptedException {
final PersistableBundle suppliedExtras = getExtras(
"testOnPackagesSuspendedNewAndOld", 2, "2", 0.2);
final AtomicReference<String> overridingBothCallbackResult = new AtomicReference<>("");
final CountDownLatch twoCallbackLatch = new CountDownLatch(2);
mTestCallback = new StubbedCallback() {
@Override
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
overridingBothCallbackResult.set(overridingBothCallbackResult.get()
+ "Old callback called even when the new one is overriden. ");
twoCallbackLatch.countDown();
}
@Override
public void onPackagesSuspended(String[] packageNames, Bundle launcherExtras,
UserHandle user) {
final StringBuilder errorString = new StringBuilder();
if (!Arrays.equals(packageNames, PACKAGES_TO_SUSPEND)) {
errorString.append("Received unexpected packageNames in onPackagesSuspended:");
for (String packageName : packageNames) {
errorString.append(" " + packageName);
}
errorString.append(". ");
}
if (user.getIdentifier() != mContext.getUserId()) {
errorString.append("Received wrong user " + user.getIdentifier() + ". ");
}
if (!areSameExtras(launcherExtras, suppliedExtras)) {
errorString.append("Unexpected launcherExtras, supplied: " + suppliedExtras
+ ", received: " + launcherExtras + ". ");
}
overridingBothCallbackResult.set(overridingBothCallbackResult.get()
+ errorString.toString());
twoCallbackLatch.countDown();
}
};
mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
suspendTestPackage(null, suppliedExtras);
assertFalse("Both callbacks were invoked", twoCallbackLatch.await(5, TimeUnit.SECONDS));
twoCallbackLatch.countDown();
assertTrue("No callback was invoked", twoCallbackLatch.await(2, TimeUnit.SECONDS));
final String result = overridingBothCallbackResult.get();
assertTrue("Callbacks did not complete as expected: " + result, result.isEmpty());
}
@Test
public void testOnPackagesSuspendedOld() throws InterruptedException {
final PersistableBundle suppliedExtras = getExtras(
"testOnPackagesSuspendedOld", 2, "2", 0.2);
final AtomicReference<String> overridingOneCallbackResult = new AtomicReference<>("");
final CountDownLatch oneCallbackLatch = new CountDownLatch(1);
mTestCallback = new StubbedCallback() {
@Override
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
final StringBuilder errorString = new StringBuilder();
if (!Arrays.equals(packageNames, PACKAGES_TO_SUSPEND)) {
errorString.append("Received unexpected packageNames in onPackagesSuspended:");
for (String packageName : packageNames) {
errorString.append(" " + packageName);
}
errorString.append(". ");
}
if (user.getIdentifier() != mContext.getUserId()) {
errorString.append("Received wrong user " + user.getIdentifier() + ". ");
}
overridingOneCallbackResult.set(overridingOneCallbackResult.get()
+ errorString.toString());
oneCallbackLatch.countDown();
}
};
mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
suspendTestPackage(null, suppliedExtras);
assertTrue("Callback not invoked", oneCallbackLatch.await(5, TimeUnit.SECONDS));
final String result = overridingOneCallbackResult.get();
assertTrue("Callback did not complete as expected: " + result, result.isEmpty());
}
@After
public void tearDown() throws Exception {
mAppCommsReceiver.unregister();
if (mTestCallback != null) {
mLauncherApps.unregisterCallback(mTestCallback);
}
Thread.sleep(250); // To prevent any race with the next registerReceiver
}
@FunctionalInterface
interface Condition {
boolean isTrue();
private static abstract class StubbedCallback extends LauncherApps.Callback {
@Override
public void onPackageRemoved(String packageName, UserHandle user) {
}
@Override
public void onPackageAdded(String packageName, UserHandle user) {
}
@Override
public void onPackageChanged(String packageName, UserHandle user) {
}
@Override
public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
}
@Override
public void onPackagesUnavailable(String[] packageNames, UserHandle user,
boolean replacing) {
}
}
}