Merge "ShortcutManager: Fix work profile" into nyc-dev

This commit is contained in:
Makoto Onuki
2016-03-29 15:35:49 +00:00
committed by Android (Google) Code Review
8 changed files with 1452 additions and 313 deletions

View File

@@ -40,28 +40,37 @@ public abstract class ShortcutServiceInternal {
}
public abstract List<ShortcutInfo>
getShortcuts(@NonNull String callingPackage, long changedSince,
getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags,
int userId);
public abstract List<ShortcutInfo>
getShortcutInfo(@NonNull String callingPackage,
getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId);
public abstract void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String id, int userId);
public abstract void pinShortcuts(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId);
public abstract Intent createShortcutIntent(@NonNull String callingPackage,
public abstract Intent createShortcutIntent(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract void addListener(@NonNull ShortcutChangeListener listener);
public abstract int getShortcutIconResId(@NonNull String callingPackage,
public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
public abstract ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
public abstract boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId);
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage);
}

View File

@@ -335,40 +335,40 @@ public class LauncherAppsService extends SystemService {
verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
if (!mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
user.getIdentifier())) {
if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
callingPackage)) {
throw new SecurityException("Caller can't access shortcut information");
}
}
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
String packageName, ComponentName componentName, int flags, UserHandle user)
throws RemoteException {
String packageName, ComponentName componentName, int flags, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
componentName, flags, user.getIdentifier()));
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
callingPackage, changedSince, packageName,
componentName, flags, user.getIdentifier()));
}
@Override
public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
List<String> ids, UserHandle user) throws RemoteException {
List<String> ids, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
ids, user.getIdentifier()));
mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
callingPackage, packageName, ids, user.getIdentifier()));
}
@Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle user) throws RemoteException {
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
ids, user.getIdentifier());
mShortcutServiceInternal.pinShortcuts(getCallingUserId(),
callingPackage, packageName, ids, user.getIdentifier());
}
@Override
@@ -376,8 +376,8 @@ public class LauncherAppsService extends SystemService {
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
user.getIdentifier());
return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
callingPackage, shortcut, user.getIdentifier());
}
@Override
@@ -385,25 +385,31 @@ public class LauncherAppsService extends SystemService {
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
user.getIdentifier());
return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
callingPackage, shortcut, user.getIdentifier());
}
@Override
public boolean hasShortcutHostPermission(String callingPackage) throws RemoteException {
public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage);
return mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
getCallingUserId());
return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
callingPackage);
}
@Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
throws RemoteException {
ensureShortcutPermission(callingPackage, user);
Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
packageName, shortcutId, user.getIdentifier());
// Even without the permission, pinned shortcuts are always launchable.
if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
callingPackage, packageName, shortcutId, user.getIdentifier())) {
ensureShortcutPermission(callingPackage, user);
}
final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
callingPackage, packageName, shortcutId, user.getIdentifier());
if (intent == null) {
return false;
}
@@ -713,9 +719,11 @@ public class LauncherAppsService extends SystemService {
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
final int launcherUserId = cookie.user.getIdentifier();
// Make sure the caller has the permission.
if (!mShortcutServiceInternal.hasShortcutHostPermission(cookie.packageName,
cookie.user.getIdentifier())) {
if (!mShortcutServiceInternal.hasShortcutHostPermission(
launcherUserId, cookie.packageName)) {
continue;
}
// Each launcher has a different set of pinned shortcuts, so we need to do a
@@ -723,8 +731,9 @@ public class LauncherAppsService extends SystemService {
// (As of now, only one launcher has the permission at a time, so it's bit
// moot, but we may change the permission model eventually.)
final List<ShortcutInfo> list =
mShortcutServiceInternal.getShortcuts(cookie.packageName,
/* changedSince= */ 0, packageName, /* component= */ null,
mShortcutServiceInternal.getShortcuts(launcherUserId,
cookie.packageName,
/* changedSince= */ 0, packageName, /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC

View File

@@ -40,6 +40,7 @@ class ShortcutLauncher implements ShortcutPackageItem {
private static final String TAG_PACKAGE = "package";
private static final String TAG_PIN = "pin";
private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
private static final String ATTR_VALUE = "value";
private static final String ATTR_PACKAGE_NAME = "package-name";
@@ -49,14 +50,19 @@ class ShortcutLauncher implements ShortcutPackageItem {
@NonNull
private final String mPackageName;
@UserIdInt
private final int mLauncherUserId;
/**
* Package name -> IDs.
*/
final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
ShortcutLauncher(@UserIdInt int userId, @NonNull String packageName) {
ShortcutLauncher(@UserIdInt int userId, @NonNull String packageName,
@UserIdInt int launcherUserId) {
mUserId = userId;
mPackageName = packageName;
mLauncherUserId = launcherUserId;
}
@UserIdInt
@@ -64,6 +70,11 @@ class ShortcutLauncher implements ShortcutPackageItem {
return mUserId;
}
@UserIdInt
public int getLauncherUserId() {
return mLauncherUserId;
}
@NonNull
public String getPackageName() {
return mPackageName;
@@ -120,8 +131,8 @@ class ShortcutLauncher implements ShortcutPackageItem {
}
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
mPackageName);
ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, mPackageName);
ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, mLauncherUserId);
for (int i = 0; i < size; i++) {
out.startTag(null, TAG_PACKAGE);
@@ -142,12 +153,15 @@ class ShortcutLauncher implements ShortcutPackageItem {
/**
* Load.
*/
public static ShortcutLauncher loadFromXml(XmlPullParser parser, int userId)
public static ShortcutLauncher loadFromXml(XmlPullParser parser, int ownerUserId)
throws IOException, XmlPullParserException {
final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
ATTR_PACKAGE_NAME);
final int launcherUserId = ShortcutService.parseIntAttribute(parser,
ATTR_LAUNCHER_USER_ID, ownerUserId);
final ShortcutLauncher ret = new ShortcutLauncher(userId, launcherPackageName);
final ShortcutLauncher ret = new ShortcutLauncher(launcherUserId, launcherPackageName,
launcherUserId);
ArraySet<String> ids = null;
final int outerDepth = parser.getDepth();
@@ -184,6 +198,8 @@ class ShortcutLauncher implements ShortcutPackageItem {
pw.print(prefix);
pw.print("Launcher: ");
pw.print(mPackageName);
pw.print(" UserId: ");
pw.print(mLauncherUserId);
pw.println();
final int size = mPinnedShortcuts.size();

View File

@@ -104,6 +104,9 @@ class ShortcutPackage implements ShortcutPackageItem {
return mPackageName;
}
/**
* Note this does *not* provide a correct view to the calling launcher.
*/
@Nullable
public ShortcutInfo findShortcutById(String id) {
return mShortcuts.get(id);
@@ -229,8 +232,8 @@ class ShortcutPackage implements ShortcutPackageItem {
}
// Then, for the pinned set for each launcher, set the pin flag one by one.
final ArrayMap<String, ShortcutLauncher> launchers =
s.getUserShortcutsLocked(mUserId).getLaunchers();
final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
s.getUserShortcutsLocked(mUserId).getAllLaunchers();
for (int l = launchers.size() - 1; l >= 0; l--) {
final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
@@ -300,13 +303,25 @@ class ShortcutPackage implements ShortcutPackageItem {
/**
* Find all shortcuts that match {@code query}.
*/
public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
findAll(s, result, query, cloneFlag, null, 0);
}
/**
* Find all shortcuts that match {@code query}.
*
* This will also provide a "view" for each launcher -- a non-dynamic shortcut that's not pinned
* by the calling launcher will not be included in the result, and also "isPinned" will be
* adjusted for the caller too.
*/
public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag,
@Nullable String callingLauncher) {
@Nullable String callingLauncher, int launcherUserId) {
// Set of pinned shortcuts by the calling launcher.
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcuts(callingLauncher, mUserId)
: s.getLauncherShortcuts(callingLauncher, mUserId, launcherUserId)
.getPinnedShortcutIds(mPackageName);
for (int i = 0; i < mShortcuts.size(); i++) {

View File

@@ -48,6 +48,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package-info";
private static final String ATTR_USER_ID = "user";
private static final String ATTR_NAME = "name";
private static final String ATTR_VERSION = "version";
private static final String ATTR_SHADOW = "shadow";
@@ -55,11 +56,8 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
private static final String TAG_SIGNATURE = "signature";
private static final String ATTR_SIGNATURE_HASH = "hash";
public interface ShortcutPackageInfoHolder {
ShortcutPackageInfo getShortcutPackageInfo();
}
private final String mPackageName;
private final int mUserId;
/**
* When true, this package information was restored from the previous device, and the app hasn't
@@ -69,12 +67,13 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
private int mVersionCode;
private ArrayList<byte[]> mSigHashes;
private ShortcutPackageInfo(String packageName, int versionCode, ArrayList<byte[]> sigHashes,
boolean isShadow) {
private ShortcutPackageInfo(String packageName, int userId,
int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
mPackageName = Preconditions.checkNotNull(packageName);
mUserId = userId;
mVersionCode = versionCode;
mIsShadow = isShadow;
mSigHashes = sigHashes;
mPackageName = Preconditions.checkNotNull(packageName);
}
@NonNull
@@ -82,6 +81,10 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
return mPackageName;
}
public int getUserId() {
return mUserId;
}
public boolean isShadow() {
return mIsShadow;
}
@@ -197,7 +200,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null;
}
final ShortcutPackageInfo ret = new ShortcutPackageInfo(packageName, pi.versionCode,
final ShortcutPackageInfo ret = new ShortcutPackageInfo(packageName, userId, pi.versionCode,
hashSignatureArray(pi.signatures), /* shadow=*/ false);
return ret;
@@ -221,6 +224,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
ShortcutService.writeAttr(out, ATTR_USER_ID, mUserId);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
@@ -232,10 +236,11 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
out.endTag(null, TAG_ROOT);
}
public static ShortcutPackageInfo loadFromXml(XmlPullParser parser)
public static ShortcutPackageInfo loadFromXml(XmlPullParser parser, int ownerUserId)
throws IOException, XmlPullParserException {
final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_NAME);
final int userId = ShortcutService.parseIntAttribute(parser, ATTR_USER_ID, ownerUserId);
final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
@@ -261,7 +266,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
}
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutPackageInfo(packageName, versionCode, hashes, shadow);
return new ShortcutPackageInfo(packageName, userId, versionCode, hashes, shadow);
}
public void dump(ShortcutService s, PrintWriter pw, String prefix) {
@@ -272,6 +277,11 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
pw.print(mPackageName);
pw.println();
pw.print(prefix);
pw.print(" User: ");
pw.print(mUserId);
pw.println();
pw.print(prefix);
pw.print(" IsShadow: ");
pw.print(mIsShadow);

View File

@@ -31,7 +31,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -78,6 +77,7 @@ import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
import libcore.io.IoUtils;
@@ -104,14 +104,9 @@ import java.util.function.Predicate;
*
* - Default launcher check does take a few ms. Worth caching.
*
* - Allow non-default launcher to start pinned shortcuts. (but not dynamic.)
* - Don't backup launcher from different profile.
*
* - Extract the user/package/launcher classes to their own files. Maybe rename so they all have
* the same "Shortcut" prefix.
*
* - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
* -> Need to scan all packages when a user starts too.
* -> Clear data -> remove all dynamic? but not the pinned?
* - Clear data -> remove all dynamic? but not the pinned?
*
* - Scan and remove orphan bitmaps (just in case).
*
@@ -455,16 +450,24 @@ public class ShortcutService extends IShortcutService.Stub {
return (int) parseLongAttribute(parser, attribute);
}
static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
return (int) parseLongAttribute(parser, attribute, def);
}
static long parseLongAttribute(XmlPullParser parser, String attribute) {
return parseLongAttribute(parser, attribute, 0);
}
static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
final String value = parseStringAttribute(parser, attribute);
if (TextUtils.isEmpty(value)) {
return 0;
return def;
}
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing long " + value);
return 0;
return def;
}
}
@@ -825,8 +828,8 @@ public class ShortcutService extends IShortcutService.Stub {
@GuardedBy("mLock")
@NonNull
ShortcutLauncher getLauncherShortcuts(
@NonNull String packageName, @UserIdInt int userId) {
return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName);
@NonNull String packageName, @UserIdInt int userId, @UserIdInt int launcherUserId) {
return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName, launcherUserId);
}
// === Caller validation ===
@@ -1324,8 +1327,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags,
/* callingLauncher= */ null);
getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags);
return new ParceledListSlice<>(ret);
}
@@ -1463,22 +1465,26 @@ public class ShortcutService extends IShortcutService.Stub {
// === House keeping ===
@VisibleForTesting
void cleanUpPackageLocked(String packageName, int userId) {
final boolean wasUserLoaded = isUserLoadedLocked(userId);
void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
final ShortcutUser mUser = getUserShortcutsLocked(userId);
final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
boolean doNotify = false;
// First, remove the package from the package list (if the package is a publisher).
if (mUser.getPackages().remove(packageName) != null) {
doNotify = true;
if (packageUserId == owningUserId) {
if (mUser.getPackages().remove(packageName) != null) {
doNotify = true;
}
}
// Also remove from the launcher list (if the package is a launcher).
mUser.getLaunchers().remove(packageName);
mUser.removeLauncher(packageUserId, packageName);
// Then remove pinned shortcuts from all launchers.
for (int i = mUser.getLaunchers().size() - 1; i >= 0; i--) {
mUser.getLaunchers().valueAt(i).cleanUpPackage(packageName);
final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
for (int i = launchers.size() - 1; i >= 0; i--) {
launchers.valueAt(i).cleanUpPackage(packageName);
}
// Now there may be orphan shortcuts because we removed pinned shortucts at the previous
// step. Remove them too.
@@ -1487,17 +1493,17 @@ public class ShortcutService extends IShortcutService.Stub {
}
// Remove the package info too.
mUser.getPackageInfos().remove(packageName);
mUser.removePackageInfo(packageUserId, packageName);
scheduleSaveUser(userId);
scheduleSaveUser(owningUserId);
if (doNotify) {
notifyListeners(packageName, userId);
notifyListeners(packageName, owningUserId);
}
if (!wasUserLoaded) {
// Note this will execute the scheduled save.
unloadUserLocked(userId);
unloadUserLocked(owningUserId);
}
}
@@ -1506,7 +1512,7 @@ public class ShortcutService extends IShortcutService.Stub {
*/
private class LocalService extends ShortcutServiceInternal {
@Override
public List<ShortcutInfo> getShortcuts(
public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
int queryFlags, int userId) {
@@ -1518,14 +1524,14 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) {
if (packageName != null) {
getShortcutsInnerLocked(
getShortcutsInnerLocked(launcherUserId,
callingPackage, packageName, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, ShortcutPackage> packages =
getUserShortcutsLocked(userId).getPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked(
getShortcutsInnerLocked(launcherUserId,
callingPackage, packages.keyAt(i), changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
}
@@ -1534,7 +1540,7 @@ public class ShortcutService extends IShortcutService.Stub {
return ret;
}
private void getShortcutsInnerLocked(@NonNull String callingPackage,
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName,long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
@@ -1554,11 +1560,11 @@ public class ShortcutService extends IShortcutService.Stub {
((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
&& si.isPinned();
return matchDynamic || matchPinned;
}, cloneFlag, callingPackage);
}, cloneFlag, callingPackage, launcherUserId);
}
@Override
public List<ShortcutInfo> getShortcutInfo(
public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
@@ -1570,13 +1576,41 @@ public class ShortcutService extends IShortcutService.Stub {
getPackageShortcutsLocked(packageName, userId).findAll(
ShortcutService.this, ret,
(ShortcutInfo si) -> idSet.contains(si.getId()),
ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage);
ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
}
return ret;
}
@Override
public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
synchronized (mLock) {
final ShortcutInfo si = getShortcutInfoLocked(
launcherUserId, callingPackage, packageName, shortcutId, userId);
return si != null && si.isPinned();
}
}
public ShortcutInfo getShortcutInfoLocked(
int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
getPackageShortcutsLocked(packageName, userId).findAll(
ShortcutService.this, list,
(ShortcutInfo si) -> shortcutId.equals(si.getId()),
/* clone flags=*/ 0, callingPackage, launcherUserId);
return list.size() == 0 ? null : list.get(0);
}
@Override
public void pinShortcuts(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1584,26 +1618,31 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) {
getUserShortcutsLocked(userId).ensurePackageInfo(
ShortcutService.this, callingPackage, userId);
ShortcutService.this, callingPackage, launcherUserId);
getLauncherShortcuts(callingPackage, userId).pinShortcuts(
getLauncherShortcuts(callingPackage, userId, launcherUserId).pinShortcuts(
ShortcutService.this, packageName, shortcutIds);
}
userPackageChanged(packageName, userId);
}
@Override
public Intent createShortcutIntent(@NonNull String callingPackage,
public Intent createShortcutIntent(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
synchronized (mLock) {
final ShortcutInfo fullShortcut =
getPackageShortcutsLocked(packageName, userId)
.findShortcutById(shortcutId);
return fullShortcut == null ? null : fullShortcut.getIntent();
// Make sure the shortcut is actually visible to the launcher.
final ShortcutInfo si = getShortcutInfoLocked(
launcherUserId, callingPackage, packageName, shortcutId, userId);
// "si == null" should suffice here, but check the flags too just to make sure.
if (si == null || !(si.isDynamic() || si.isPinned())) {
return null;
}
return si.getIntent();
}
}
@@ -1615,7 +1654,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
public int getShortcutIconResId(@NonNull String callingPackage,
public int getShortcutIconResId(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId) {
Preconditions.checkNotNull(shortcut, "shortcut");
@@ -1628,7 +1668,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcutIn, int userId) {
Preconditions.checkNotNull(shortcutIn, "shortcut");
@@ -1654,8 +1695,9 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage) {
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
}
@@ -1684,28 +1726,31 @@ public class ShortcutService extends IShortcutService.Stub {
* Called when a user is unlocked. Check all known packages still exist, and otherwise
* perform cleanup.
*/
private void cleanupGonePackages(@UserIdInt int userId) {
@VisibleForTesting
void cleanupGonePackages(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "cleanupGonePackages() userId=" + userId);
}
ArrayList<String> gonePackages = null;
ArrayList<PackageWithUser> gonePackages = null;
final ShortcutUser user = getUserShortcutsLocked(userId);
final ArrayMap<String, ShortcutPackageInfo> infos = user.getPackageInfos();
for (int i = infos.size() -1; i >= 0; i--) {
final ShortcutPackageInfo info = infos.valueAt(i);
if (info.isShadow()) {
continue;
synchronized (mLock) {
final ShortcutUser user = getUserShortcutsLocked(userId);
final ArrayMap<PackageWithUser, ShortcutPackageInfo> infos = user.getAllPackageInfos();
for (int i = infos.size() -1; i >= 0; i--) {
final ShortcutPackageInfo info = infos.valueAt(i);
if (info.isShadow()) {
continue;
}
if (isPackageInstalled(info.getPackageName(), info.getUserId())) {
continue;
}
gonePackages = ArrayUtils.add(gonePackages,
PackageWithUser.of(info.getUserId(), info.getPackageName()));
}
if (isPackageInstalled(info.getPackageName(), userId)) {
continue;
}
gonePackages = ArrayUtils.add(gonePackages, info.getPackageName());
}
if (gonePackages != null) {
synchronized (mLock) {
if (gonePackages != null) {
for (int i = gonePackages.size() - 1; i >= 0; i--) {
cleanUpPackageLocked(gonePackages.get(i), userId);
final PackageWithUser pu = gonePackages.get(i);
cleanUpPackageLocked(pu.packageName, userId, pu.userId);
}
}
}
@@ -1716,9 +1761,8 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
final ArrayMap<String, ShortcutPackageInfo> infos =
getUserShortcutsLocked(userId).getPackageInfos();
final ShortcutPackageInfo existing = infos.get(packageName);
final ShortcutPackageInfo existing = getUserShortcutsLocked(userId)
.getPackageInfo(userId, packageName);
if (existing != null && existing.isShadow()) {
Slog.w(TAG, "handlePackageAdded: TODO Restore not implemented");
@@ -1732,7 +1776,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
synchronized (mLock) {
final ShortcutPackageInfo spi =
getUserShortcutsLocked(userId).getPackageInfos().get(packageName);
getUserShortcutsLocked(userId).getPackageInfo(userId, packageName);
if (spi != null) {
spi.refreshAndSave(this, userId);
}
@@ -1744,7 +1788,7 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId));
}
synchronized (mLock) {
cleanUpPackageLocked(packageName, userId);
cleanUpPackageLocked(packageName, userId, userId);
}
}
@@ -2161,11 +2205,16 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId) {
return getPackageInfoForTest(packageName, userId, userId);
}
@VisibleForTesting
ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId, int packageUserId) {
synchronized (mLock) {
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
return user.getPackageInfos().get(packageName);
return user.getPackageInfo(packageUserId, packageName);
}
}
}

View File

@@ -21,6 +21,8 @@ import android.content.ComponentName;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.util.Preconditions;
import libcore.util.Objects;
import org.xmlpull.v1.XmlPullParser;
@@ -41,14 +43,48 @@ class ShortcutUser {
private static final String ATTR_VALUE = "value";
static final class PackageWithUser {
final int userId;
final String packageName;
private PackageWithUser(int userId, String packageName) {
this.userId = userId;
this.packageName = Preconditions.checkNotNull(packageName);
}
public static PackageWithUser of(int launcherUserId, String packageName) {
return new PackageWithUser(launcherUserId, packageName);
}
@Override
public int hashCode() {
return packageName.hashCode() ^ userId;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PackageWithUser)) {
return false;
}
final PackageWithUser that = (PackageWithUser) obj;
return userId == that.userId && packageName.equals(that.packageName);
}
@Override
public String toString() {
return String.format("{Launcher: %d, %s}", userId, packageName);
}
}
@UserIdInt
final int mUserId;
private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
private final ArrayMap<String, ShortcutLauncher> mLaunchers = new ArrayMap<>();
private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
private final ArrayMap<String, ShortcutPackageInfo> mPackageInfos = new ArrayMap<>();
private final ArrayMap<PackageWithUser, ShortcutPackageInfo> mPackageInfos = new ArrayMap<>();
private ComponentName mLauncherComponent;
@@ -60,14 +96,41 @@ class ShortcutUser {
return mPackages;
}
public ArrayMap<String, ShortcutLauncher> getLaunchers() {
public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
return mLaunchers;
}
public ArrayMap<String, ShortcutPackageInfo> getPackageInfos() {
public ShortcutLauncher getLauncher(@UserIdInt int userId, @NonNull String packageName) {
return mLaunchers.get(PackageWithUser.of(userId, packageName));
}
public void addLauncher(ShortcutLauncher launcher) {
mLaunchers.put(PackageWithUser.of(launcher.getUserId(), launcher.getPackageName()),
launcher);
}
public ShortcutLauncher removeLauncher(
@UserIdInt int userId, @NonNull String packageName) {
return mLaunchers.remove(PackageWithUser.of(userId, packageName));
}
public ArrayMap<PackageWithUser, ShortcutPackageInfo> getAllPackageInfos() {
return mPackageInfos;
}
public ShortcutPackageInfo getPackageInfo(@UserIdInt int userId, @NonNull String packageName) {
return mPackageInfos.get(PackageWithUser.of(userId, packageName));
}
public void addPackageInfo(ShortcutPackageInfo spi) {
mPackageInfos.put(PackageWithUser.of(spi.getUserId(), spi.getPackageName()), spi);
}
public ShortcutPackageInfo removePackageInfo(
@UserIdInt int userId, @NonNull String packageName) {
return mPackageInfos.remove(PackageWithUser.of(userId, packageName));
}
public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
ShortcutPackage ret = mPackages.get(packageName);
if (ret == null) {
@@ -77,17 +140,20 @@ class ShortcutUser {
return ret;
}
public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName) {
ShortcutLauncher ret = mLaunchers.get(packageName);
public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName,
@UserIdInt int launcherUserId) {
final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
ShortcutLauncher ret = mLaunchers.get(key);
if (ret == null) {
ret = new ShortcutLauncher(mUserId, packageName);
mLaunchers.put(packageName, ret);
ret = new ShortcutLauncher(mUserId, packageName, launcherUserId);
mLaunchers.put(key, ret);
}
return ret;
}
public void ensurePackageInfo(ShortcutService s, String packageName, @UserIdInt int userId) {
final ShortcutPackageInfo existing = mPackageInfos.get(packageName);
final PackageWithUser key = PackageWithUser.of(userId, packageName);
final ShortcutPackageInfo existing = mPackageInfos.get(key);
if (existing != null) {
return;
@@ -97,7 +163,7 @@ class ShortcutUser {
}
final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage(
s, packageName, userId);
mPackageInfos.put(packageName, newSpi);
mPackageInfos.put(key, newSpi);
s.scheduleSaveUser(mUserId);
}
@@ -166,18 +232,12 @@ class ShortcutUser {
}
case ShortcutLauncher.TAG_ROOT: {
final ShortcutLauncher shortcuts =
ShortcutLauncher.loadFromXml(parser, userId);
ret.getLaunchers().put(shortcuts.getPackageName(), shortcuts);
ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId));
continue;
}
case ShortcutPackageInfo.TAG_ROOT: {
final ShortcutPackageInfo pi =
ShortcutPackageInfo.loadFromXml(parser);
ret.getPackageInfos().put(pi.getPackageName(), pi);
ret.addPackageInfo(ShortcutPackageInfo.loadFromXml(parser, userId));
continue;
}
}