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> public abstract List<ShortcutInfo>
getShortcuts(@NonNull String callingPackage, long changedSince, getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName, @Nullable String packageName, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags, @ShortcutQuery.QueryFlags int flags,
int userId); int userId);
public abstract List<ShortcutInfo> public abstract List<ShortcutInfo>
getShortcutInfo(@NonNull String callingPackage, getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId); @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); @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); @NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract void addListener(@NonNull ShortcutChangeListener listener); 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); @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); @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); verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
if (!mShortcutServiceInternal.hasShortcutHostPermission(callingPackage, if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
user.getIdentifier())) { callingPackage)) {
throw new SecurityException("Caller can't access shortcut information"); throw new SecurityException("Caller can't access shortcut information");
} }
} }
@Override @Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince, public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
String packageName, ComponentName componentName, int flags, UserHandle user) String packageName, ComponentName componentName, int flags, UserHandle user) {
throws RemoteException {
ensureShortcutPermission(callingPackage, user); ensureShortcutPermission(callingPackage, user);
return new ParceledListSlice<>( return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName, mShortcutServiceInternal.getShortcuts(getCallingUserId(),
componentName, flags, user.getIdentifier())); callingPackage, changedSince, packageName,
componentName, flags, user.getIdentifier()));
} }
@Override @Override
public ParceledListSlice getShortcutInfo(String callingPackage, String packageName, public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
List<String> ids, UserHandle user) throws RemoteException { List<String> ids, UserHandle user) {
ensureShortcutPermission(callingPackage, user); ensureShortcutPermission(callingPackage, user);
return new ParceledListSlice<>( return new ParceledListSlice<>(
mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName, mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
ids, user.getIdentifier())); callingPackage, packageName, ids, user.getIdentifier()));
} }
@Override @Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids, public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle user) throws RemoteException { UserHandle user) {
ensureShortcutPermission(callingPackage, user); ensureShortcutPermission(callingPackage, user);
mShortcutServiceInternal.pinShortcuts(callingPackage, packageName, mShortcutServiceInternal.pinShortcuts(getCallingUserId(),
ids, user.getIdentifier()); callingPackage, packageName, ids, user.getIdentifier());
} }
@Override @Override
@@ -376,8 +376,8 @@ public class LauncherAppsService extends SystemService {
UserHandle user) { UserHandle user) {
ensureShortcutPermission(callingPackage, user); ensureShortcutPermission(callingPackage, user);
return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut, return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
user.getIdentifier()); callingPackage, shortcut, user.getIdentifier());
} }
@Override @Override
@@ -385,25 +385,31 @@ public class LauncherAppsService extends SystemService {
UserHandle user) { UserHandle user) {
ensureShortcutPermission(callingPackage, user); ensureShortcutPermission(callingPackage, user);
return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut, return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
user.getIdentifier()); callingPackage, shortcut, user.getIdentifier());
} }
@Override @Override
public boolean hasShortcutHostPermission(String callingPackage) throws RemoteException { public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage); verifyCallingPackage(callingPackage);
return mShortcutServiceInternal.hasShortcutHostPermission(callingPackage, return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
getCallingUserId()); callingPackage);
} }
@Override @Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId, public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
Rect sourceBounds, Bundle startActivityOptions, UserHandle user) Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
throws RemoteException { verifyCallingPackage(callingPackage);
ensureShortcutPermission(callingPackage, user); ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage, // Even without the permission, pinned shortcuts are always launchable.
packageName, shortcutId, user.getIdentifier()); 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) { if (intent == null) {
return false; return false;
} }
@@ -713,9 +719,11 @@ public class LauncherAppsService extends SystemService {
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue; if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
final int launcherUserId = cookie.user.getIdentifier();
// Make sure the caller has the permission. // Make sure the caller has the permission.
if (!mShortcutServiceInternal.hasShortcutHostPermission(cookie.packageName, if (!mShortcutServiceInternal.hasShortcutHostPermission(
cookie.user.getIdentifier())) { launcherUserId, cookie.packageName)) {
continue; continue;
} }
// Each launcher has a different set of pinned shortcuts, so we need to do a // 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 // (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.) // moot, but we may change the permission model eventually.)
final List<ShortcutInfo> list = final List<ShortcutInfo> list =
mShortcutServiceInternal.getShortcuts(cookie.packageName, mShortcutServiceInternal.getShortcuts(launcherUserId,
/* changedSince= */ 0, packageName, /* component= */ null, cookie.packageName,
/* changedSince= */ 0, packageName, /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC | 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_PACKAGE = "package";
private static final String TAG_PIN = "pin"; 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_VALUE = "value";
private static final String ATTR_PACKAGE_NAME = "package-name"; private static final String ATTR_PACKAGE_NAME = "package-name";
@@ -49,14 +50,19 @@ class ShortcutLauncher implements ShortcutPackageItem {
@NonNull @NonNull
private final String mPackageName; private final String mPackageName;
@UserIdInt
private final int mLauncherUserId;
/** /**
* Package name -> IDs. * Package name -> IDs.
*/ */
final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>(); 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; mUserId = userId;
mPackageName = packageName; mPackageName = packageName;
mLauncherUserId = launcherUserId;
} }
@UserIdInt @UserIdInt
@@ -64,6 +70,11 @@ class ShortcutLauncher implements ShortcutPackageItem {
return mUserId; return mUserId;
} }
@UserIdInt
public int getLauncherUserId() {
return mLauncherUserId;
}
@NonNull @NonNull
public String getPackageName() { public String getPackageName() {
return mPackageName; return mPackageName;
@@ -120,8 +131,8 @@ class ShortcutLauncher implements ShortcutPackageItem {
} }
out.startTag(null, TAG_ROOT); out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, mPackageName);
mPackageName); ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, mLauncherUserId);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
out.startTag(null, TAG_PACKAGE); out.startTag(null, TAG_PACKAGE);
@@ -142,12 +153,15 @@ class ShortcutLauncher implements ShortcutPackageItem {
/** /**
* Load. * Load.
*/ */
public static ShortcutLauncher loadFromXml(XmlPullParser parser, int userId) public static ShortcutLauncher loadFromXml(XmlPullParser parser, int ownerUserId)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
final String launcherPackageName = ShortcutService.parseStringAttribute(parser, final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
ATTR_PACKAGE_NAME); 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; ArraySet<String> ids = null;
final int outerDepth = parser.getDepth(); final int outerDepth = parser.getDepth();
@@ -184,6 +198,8 @@ class ShortcutLauncher implements ShortcutPackageItem {
pw.print(prefix); pw.print(prefix);
pw.print("Launcher: "); pw.print("Launcher: ");
pw.print(mPackageName); pw.print(mPackageName);
pw.print(" UserId: ");
pw.print(mLauncherUserId);
pw.println(); pw.println();
final int size = mPinnedShortcuts.size(); final int size = mPinnedShortcuts.size();

View File

@@ -104,6 +104,9 @@ class ShortcutPackage implements ShortcutPackageItem {
return mPackageName; return mPackageName;
} }
/**
* Note this does *not* provide a correct view to the calling launcher.
*/
@Nullable @Nullable
public ShortcutInfo findShortcutById(String id) { public ShortcutInfo findShortcutById(String id) {
return mShortcuts.get(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. // Then, for the pinned set for each launcher, set the pin flag one by one.
final ArrayMap<String, ShortcutLauncher> launchers = final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
s.getUserShortcutsLocked(mUserId).getLaunchers(); s.getUserShortcutsLocked(mUserId).getAllLaunchers();
for (int l = launchers.size() - 1; l >= 0; l--) { for (int l = launchers.size() - 1; l >= 0; l--) {
final ShortcutLauncher launcherShortcuts = launchers.valueAt(l); final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
@@ -300,13 +303,25 @@ class ShortcutPackage implements ShortcutPackageItem {
/** /**
* Find all shortcuts that match {@code query}. * 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, public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag, @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
@Nullable String callingLauncher) { @Nullable String callingLauncher, int launcherUserId) {
// Set of pinned shortcuts by the calling launcher. // Set of pinned shortcuts by the calling launcher.
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcuts(callingLauncher, mUserId) : s.getLauncherShortcuts(callingLauncher, mUserId, launcherUserId)
.getPinnedShortcutIds(mPackageName); .getPinnedShortcutIds(mPackageName);
for (int i = 0; i < mShortcuts.size(); i++) { 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; private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package-info"; 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_NAME = "name";
private static final String ATTR_VERSION = "version"; private static final String ATTR_VERSION = "version";
private static final String ATTR_SHADOW = "shadow"; 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 TAG_SIGNATURE = "signature";
private static final String ATTR_SIGNATURE_HASH = "hash"; private static final String ATTR_SIGNATURE_HASH = "hash";
public interface ShortcutPackageInfoHolder {
ShortcutPackageInfo getShortcutPackageInfo();
}
private final String mPackageName; private final String mPackageName;
private final int mUserId;
/** /**
* When true, this package information was restored from the previous device, and the app hasn't * 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 int mVersionCode;
private ArrayList<byte[]> mSigHashes; private ArrayList<byte[]> mSigHashes;
private ShortcutPackageInfo(String packageName, int versionCode, ArrayList<byte[]> sigHashes, private ShortcutPackageInfo(String packageName, int userId,
boolean isShadow) { int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
mPackageName = Preconditions.checkNotNull(packageName);
mUserId = userId;
mVersionCode = versionCode; mVersionCode = versionCode;
mIsShadow = isShadow; mIsShadow = isShadow;
mSigHashes = sigHashes; mSigHashes = sigHashes;
mPackageName = Preconditions.checkNotNull(packageName);
} }
@NonNull @NonNull
@@ -82,6 +81,10 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
return mPackageName; return mPackageName;
} }
public int getUserId() {
return mUserId;
}
public boolean isShadow() { public boolean isShadow() {
return mIsShadow; return mIsShadow;
} }
@@ -197,7 +200,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
Slog.e(TAG, "Can't get signatures: package=" + packageName); Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null; return null;
} }
final ShortcutPackageInfo ret = new ShortcutPackageInfo(packageName, pi.versionCode, final ShortcutPackageInfo ret = new ShortcutPackageInfo(packageName, userId, pi.versionCode,
hashSignatureArray(pi.signatures), /* shadow=*/ false); hashSignatureArray(pi.signatures), /* shadow=*/ false);
return ret; return ret;
@@ -221,6 +224,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
out.startTag(null, TAG_ROOT); out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_NAME, mPackageName); ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
ShortcutService.writeAttr(out, ATTR_USER_ID, mUserId);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode); ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow); ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
@@ -232,10 +236,11 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
out.endTag(null, TAG_ROOT); out.endTag(null, TAG_ROOT);
} }
public static ShortcutPackageInfo loadFromXml(XmlPullParser parser) public static ShortcutPackageInfo loadFromXml(XmlPullParser parser, int ownerUserId)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_NAME); 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 int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW); final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
@@ -261,7 +266,7 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
} }
throw ShortcutService.throwForInvalidTag(depth, tag); 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) { public void dump(ShortcutService s, PrintWriter pw, String prefix) {
@@ -272,6 +277,11 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
pw.print(mPackageName); pw.print(mPackageName);
pw.println(); pw.println();
pw.print(prefix);
pw.print(" User: ");
pw.print(mUserId);
pw.println();
pw.print(prefix); pw.print(prefix);
pw.print(" IsShadow: "); pw.print(" IsShadow: ");
pw.print(mIsShadow); pw.print(mIsShadow);

View File

@@ -31,7 +31,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
@@ -78,6 +77,7 @@ import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.SystemService; import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
import libcore.io.IoUtils; import libcore.io.IoUtils;
@@ -104,14 +104,9 @@ import java.util.function.Predicate;
* *
* - Default launcher check does take a few ms. Worth caching. * - 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 * - Clear data -> remove all dynamic? but not the pinned?
* 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?
* *
* - Scan and remove orphan bitmaps (just in case). * - Scan and remove orphan bitmaps (just in case).
* *
@@ -455,16 +450,24 @@ public class ShortcutService extends IShortcutService.Stub {
return (int) parseLongAttribute(parser, attribute); 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) { 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); final String value = parseStringAttribute(parser, attribute);
if (TextUtils.isEmpty(value)) { if (TextUtils.isEmpty(value)) {
return 0; return def;
} }
try { try {
return Long.parseLong(value); return Long.parseLong(value);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing long " + value); Slog.e(TAG, "Error parsing long " + value);
return 0; return def;
} }
} }
@@ -825,8 +828,8 @@ public class ShortcutService extends IShortcutService.Stub {
@GuardedBy("mLock") @GuardedBy("mLock")
@NonNull @NonNull
ShortcutLauncher getLauncherShortcuts( ShortcutLauncher getLauncherShortcuts(
@NonNull String packageName, @UserIdInt int userId) { @NonNull String packageName, @UserIdInt int userId, @UserIdInt int launcherUserId) {
return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName); return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName, launcherUserId);
} }
// === Caller validation === // === Caller validation ===
@@ -1324,8 +1327,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ArrayList<ShortcutInfo> ret = new ArrayList<>(); final ArrayList<ShortcutInfo> ret = new ArrayList<>();
getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags, getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags);
/* callingLauncher= */ null);
return new ParceledListSlice<>(ret); return new ParceledListSlice<>(ret);
} }
@@ -1463,22 +1465,26 @@ public class ShortcutService extends IShortcutService.Stub {
// === House keeping === // === House keeping ===
@VisibleForTesting @VisibleForTesting
void cleanUpPackageLocked(String packageName, int userId) { void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
final boolean wasUserLoaded = isUserLoadedLocked(userId); final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
final ShortcutUser mUser = getUserShortcutsLocked(userId); final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
boolean doNotify = false; boolean doNotify = false;
// First, remove the package from the package list (if the package is a publisher). // First, remove the package from the package list (if the package is a publisher).
if (mUser.getPackages().remove(packageName) != null) { if (packageUserId == owningUserId) {
doNotify = true; if (mUser.getPackages().remove(packageName) != null) {
doNotify = true;
}
} }
// Also remove from the launcher list (if the package is a launcher). // 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. // Then remove pinned shortcuts from all launchers.
for (int i = mUser.getLaunchers().size() - 1; i >= 0; i--) { final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
mUser.getLaunchers().valueAt(i).cleanUpPackage(packageName); 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 // Now there may be orphan shortcuts because we removed pinned shortucts at the previous
// step. Remove them too. // step. Remove them too.
@@ -1487,17 +1493,17 @@ public class ShortcutService extends IShortcutService.Stub {
} }
// Remove the package info too. // Remove the package info too.
mUser.getPackageInfos().remove(packageName); mUser.removePackageInfo(packageUserId, packageName);
scheduleSaveUser(userId); scheduleSaveUser(owningUserId);
if (doNotify) { if (doNotify) {
notifyListeners(packageName, userId); notifyListeners(packageName, owningUserId);
} }
if (!wasUserLoaded) { if (!wasUserLoaded) {
// Note this will execute the scheduled save. // 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 { private class LocalService extends ShortcutServiceInternal {
@Override @Override
public List<ShortcutInfo> getShortcuts( public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince, @NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName, @Nullable String packageName, @Nullable ComponentName componentName,
int queryFlags, int userId) { int queryFlags, int userId) {
@@ -1518,14 +1524,14 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) { synchronized (mLock) {
if (packageName != null) { if (packageName != null) {
getShortcutsInnerLocked( getShortcutsInnerLocked(launcherUserId,
callingPackage, packageName, changedSince, callingPackage, packageName, changedSince,
componentName, queryFlags, userId, ret, cloneFlag); componentName, queryFlags, userId, ret, cloneFlag);
} else { } else {
final ArrayMap<String, ShortcutPackage> packages = final ArrayMap<String, ShortcutPackage> packages =
getUserShortcutsLocked(userId).getPackages(); getUserShortcutsLocked(userId).getPackages();
for (int i = packages.size() - 1; i >= 0; i--) { for (int i = packages.size() - 1; i >= 0; i--) {
getShortcutsInnerLocked( getShortcutsInnerLocked(launcherUserId,
callingPackage, packages.keyAt(i), changedSince, callingPackage, packages.keyAt(i), changedSince,
componentName, queryFlags, userId, ret, cloneFlag); componentName, queryFlags, userId, ret, cloneFlag);
} }
@@ -1534,7 +1540,7 @@ public class ShortcutService extends IShortcutService.Stub {
return ret; return ret;
} }
private void getShortcutsInnerLocked(@NonNull String callingPackage, private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName,long changedSince, @Nullable String packageName,long changedSince,
@Nullable ComponentName componentName, int queryFlags, @Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) { int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
@@ -1554,11 +1560,11 @@ public class ShortcutService extends IShortcutService.Stub {
((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0) ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
&& si.isPinned(); && si.isPinned();
return matchDynamic || matchPinned; return matchDynamic || matchPinned;
}, cloneFlag, callingPackage); }, cloneFlag, callingPackage, launcherUserId);
} }
@Override @Override
public List<ShortcutInfo> getShortcutInfo( public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
@NonNull String callingPackage, @NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId) { @NonNull String packageName, @Nullable List<String> ids, int userId) {
// Calling permission must be checked by LauncherAppsImpl. // Calling permission must be checked by LauncherAppsImpl.
@@ -1570,13 +1576,41 @@ public class ShortcutService extends IShortcutService.Stub {
getPackageShortcutsLocked(packageName, userId).findAll( getPackageShortcutsLocked(packageName, userId).findAll(
ShortcutService.this, ret, ShortcutService.this, ret,
(ShortcutInfo si) -> idSet.contains(si.getId()), (ShortcutInfo si) -> idSet.contains(si.getId()),
ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage); ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
} }
return ret; return ret;
} }
@Override @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) { @NonNull List<String> shortcutIds, int userId) {
// Calling permission must be checked by LauncherAppsImpl. // Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1584,26 +1618,31 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) { synchronized (mLock) {
getUserShortcutsLocked(userId).ensurePackageInfo( getUserShortcutsLocked(userId).ensurePackageInfo(
ShortcutService.this, callingPackage, userId); ShortcutService.this, callingPackage, launcherUserId);
getLauncherShortcuts(callingPackage, userId).pinShortcuts( getLauncherShortcuts(callingPackage, userId, launcherUserId).pinShortcuts(
ShortcutService.this, packageName, shortcutIds); ShortcutService.this, packageName, shortcutIds);
} }
userPackageChanged(packageName, userId); userPackageChanged(packageName, userId);
} }
@Override @Override
public Intent createShortcutIntent(@NonNull String callingPackage, public Intent createShortcutIntent(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) { @NonNull String packageName, @NonNull String shortcutId, int userId) {
// Calling permission must be checked by LauncherAppsImpl. // Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
synchronized (mLock) { synchronized (mLock) {
final ShortcutInfo fullShortcut = // Make sure the shortcut is actually visible to the launcher.
getPackageShortcutsLocked(packageName, userId) final ShortcutInfo si = getShortcutInfoLocked(
.findShortcutById(shortcutId); launcherUserId, callingPackage, packageName, shortcutId, userId);
return fullShortcut == null ? null : fullShortcut.getIntent(); // "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 @Override
public int getShortcutIconResId(@NonNull String callingPackage, public int getShortcutIconResId(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId) { @NonNull ShortcutInfo shortcut, int userId) {
Preconditions.checkNotNull(shortcut, "shortcut"); Preconditions.checkNotNull(shortcut, "shortcut");
@@ -1628,7 +1668,8 @@ public class ShortcutService extends IShortcutService.Stub {
} }
@Override @Override
public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage, public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
@NonNull ShortcutInfo shortcutIn, int userId) { @NonNull ShortcutInfo shortcutIn, int userId) {
Preconditions.checkNotNull(shortcutIn, "shortcut"); Preconditions.checkNotNull(shortcutIn, "shortcut");
@@ -1654,8 +1695,9 @@ public class ShortcutService extends IShortcutService.Stub {
} }
@Override @Override
public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { public boolean hasShortcutHostPermission(int launcherUserId,
return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId); @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 * Called when a user is unlocked. Check all known packages still exist, and otherwise
* perform cleanup. * perform cleanup.
*/ */
private void cleanupGonePackages(@UserIdInt int userId) { @VisibleForTesting
void cleanupGonePackages(@UserIdInt int userId) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "cleanupGonePackages() userId=" + userId); Slog.d(TAG, "cleanupGonePackages() userId=" + userId);
} }
ArrayList<String> gonePackages = null; ArrayList<PackageWithUser> gonePackages = null;
final ShortcutUser user = getUserShortcutsLocked(userId); synchronized (mLock) {
final ArrayMap<String, ShortcutPackageInfo> infos = user.getPackageInfos(); final ShortcutUser user = getUserShortcutsLocked(userId);
for (int i = infos.size() -1; i >= 0; i--) { final ArrayMap<PackageWithUser, ShortcutPackageInfo> infos = user.getAllPackageInfos();
final ShortcutPackageInfo info = infos.valueAt(i); for (int i = infos.size() -1; i >= 0; i--) {
if (info.isShadow()) { final ShortcutPackageInfo info = infos.valueAt(i);
continue; 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)) { if (gonePackages != null) {
continue;
}
gonePackages = ArrayUtils.add(gonePackages, info.getPackageName());
}
if (gonePackages != null) {
synchronized (mLock) {
for (int i = gonePackages.size() - 1; i >= 0; i--) { 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)); Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
} }
synchronized (mLock) { synchronized (mLock) {
final ArrayMap<String, ShortcutPackageInfo> infos = final ShortcutPackageInfo existing = getUserShortcutsLocked(userId)
getUserShortcutsLocked(userId).getPackageInfos(); .getPackageInfo(userId, packageName);
final ShortcutPackageInfo existing = infos.get(packageName);
if (existing != null && existing.isShadow()) { if (existing != null && existing.isShadow()) {
Slog.w(TAG, "handlePackageAdded: TODO Restore not implemented"); Slog.w(TAG, "handlePackageAdded: TODO Restore not implemented");
@@ -1732,7 +1776,7 @@ public class ShortcutService extends IShortcutService.Stub {
} }
synchronized (mLock) { synchronized (mLock) {
final ShortcutPackageInfo spi = final ShortcutPackageInfo spi =
getUserShortcutsLocked(userId).getPackageInfos().get(packageName); getUserShortcutsLocked(userId).getPackageInfo(userId, packageName);
if (spi != null) { if (spi != null) {
spi.refreshAndSave(this, userId); 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)); Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId));
} }
synchronized (mLock) { synchronized (mLock) {
cleanUpPackageLocked(packageName, userId); cleanUpPackageLocked(packageName, userId, userId);
} }
} }
@@ -2161,11 +2205,16 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting @VisibleForTesting
ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId) { ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId) {
return getPackageInfoForTest(packageName, userId, userId);
}
@VisibleForTesting
ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId, int packageUserId) {
synchronized (mLock) { synchronized (mLock) {
final ShortcutUser user = mUsers.get(userId); final ShortcutUser user = mUsers.get(userId);
if (user == null) return null; 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.ArrayMap;
import android.util.Slog; import android.util.Slog;
import com.android.internal.util.Preconditions;
import libcore.util.Objects; import libcore.util.Objects;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
@@ -41,14 +43,48 @@ class ShortcutUser {
private static final String ATTR_VALUE = "value"; 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 @UserIdInt
final int mUserId; final int mUserId;
private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>(); 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; private ComponentName mLauncherComponent;
@@ -60,14 +96,41 @@ class ShortcutUser {
return mPackages; return mPackages;
} }
public ArrayMap<String, ShortcutLauncher> getLaunchers() { public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
return mLaunchers; 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; 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) { public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
ShortcutPackage ret = mPackages.get(packageName); ShortcutPackage ret = mPackages.get(packageName);
if (ret == null) { if (ret == null) {
@@ -77,17 +140,20 @@ class ShortcutUser {
return ret; return ret;
} }
public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName) { public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName,
ShortcutLauncher ret = mLaunchers.get(packageName); @UserIdInt int launcherUserId) {
final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
ShortcutLauncher ret = mLaunchers.get(key);
if (ret == null) { if (ret == null) {
ret = new ShortcutLauncher(mUserId, packageName); ret = new ShortcutLauncher(mUserId, packageName, launcherUserId);
mLaunchers.put(packageName, ret); mLaunchers.put(key, ret);
} }
return ret; return ret;
} }
public void ensurePackageInfo(ShortcutService s, String packageName, @UserIdInt int userId) { 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) { if (existing != null) {
return; return;
@@ -97,7 +163,7 @@ class ShortcutUser {
} }
final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage( final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage(
s, packageName, userId); s, packageName, userId);
mPackageInfos.put(packageName, newSpi); mPackageInfos.put(key, newSpi);
s.scheduleSaveUser(mUserId); s.scheduleSaveUser(mUserId);
} }
@@ -166,18 +232,12 @@ class ShortcutUser {
} }
case ShortcutLauncher.TAG_ROOT: { case ShortcutLauncher.TAG_ROOT: {
final ShortcutLauncher shortcuts = ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId));
ShortcutLauncher.loadFromXml(parser, userId);
ret.getLaunchers().put(shortcuts.getPackageName(), shortcuts);
continue; continue;
} }
case ShortcutPackageInfo.TAG_ROOT: { case ShortcutPackageInfo.TAG_ROOT: {
final ShortcutPackageInfo pi = ret.addPackageInfo(ShortcutPackageInfo.loadFromXml(parser, userId));
ShortcutPackageInfo.loadFromXml(parser);
ret.getPackageInfos().put(pi.getPackageName(), pi);
continue; continue;
} }
} }