Merge "ShortcutManager: Handle package broadcasts." into nyc-dev

This commit is contained in:
Makoto Onuki
2016-03-28 16:32:45 +00:00
committed by Android (Google) Code Review
8 changed files with 974 additions and 64 deletions

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import android.app.backup.BlobBackupHelper;
public class ShortcutBackupAgent extends BlobBackupHelper {
private static final String TAG = "ShortcutBackupAgent";
private static final int BLOB_VERSION = 1;
public ShortcutBackupAgent(int currentBlobVersion, String... keys) {
super(currentBlobVersion, keys);
}
@Override
protected byte[] getBackupPayload(String key) {
throw new RuntimeException("not implemented yet"); // todo
}
@Override
protected void applyRestoredPayload(String key, byte[] payload) {
throw new RuntimeException("not implemented yet"); // todo
}
}

View File

@@ -32,7 +32,7 @@ import java.util.List;
/** /**
* Launcher information used by {@link ShortcutService}. * Launcher information used by {@link ShortcutService}.
*/ */
class ShortcutLauncher { class ShortcutLauncher implements ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG; private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "launcher-pins"; static final String TAG_ROOT = "launcher-pins";
@@ -44,10 +44,10 @@ class ShortcutLauncher {
private static final String ATTR_PACKAGE_NAME = "package-name"; private static final String ATTR_PACKAGE_NAME = "package-name";
@UserIdInt @UserIdInt
final int mUserId; private final int mUserId;
@NonNull @NonNull
final String mPackageName; private final String mPackageName;
/** /**
* Package name -> IDs. * Package name -> IDs.
@@ -59,6 +59,16 @@ class ShortcutLauncher {
mPackageName = packageName; mPackageName = packageName;
} }
@UserIdInt
public int getUserId() {
return mUserId;
}
@NonNull
public String getPackageName() {
return mPackageName;
}
public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName, public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
@NonNull List<String> ids) { @NonNull List<String> ids) {
final int idSize = ids.size(); final int idSize = ids.size();
@@ -103,7 +113,7 @@ class ShortcutLauncher {
/** /**
* Persist. * Persist.
*/ */
public void saveToXml(XmlSerializer out) throws IOException { public void saveToXml(XmlSerializer out, boolean forBackup) throws IOException {
final int size = mPinnedShortcuts.size(); final int size = mPinnedShortcuts.size();
if (size == 0) { if (size == 0) {
return; // Nothing to write. return; // Nothing to write.
@@ -190,7 +200,7 @@ class ShortcutLauncher {
for (int j = 0; j < idSize; j++) { for (int j = 0; j < idSize; j++) {
pw.print(prefix); pw.print(prefix);
pw.print(" "); pw.print(" Pinned: ");
pw.print(ids.valueAt(j)); pw.print(ids.valueAt(j));
pw.println(); pw.println();
} }

View File

@@ -41,7 +41,7 @@ import java.util.function.Predicate;
/** /**
* Package information used by {@link ShortcutService}. * Package information used by {@link ShortcutService}.
*/ */
class ShortcutPackage { class ShortcutPackage implements ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG; private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package"; static final String TAG_ROOT = "package";
@@ -64,10 +64,10 @@ class ShortcutPackage {
private static final String ATTR_BITMAP_PATH = "bitmap-path"; private static final String ATTR_BITMAP_PATH = "bitmap-path";
@UserIdInt @UserIdInt
final int mUserId; private final int mUserId;
@NonNull @NonNull
final String mPackageName; private final String mPackageName;
/** /**
* All the shortcuts from the package, keyed on IDs. * All the shortcuts from the package, keyed on IDs.
@@ -94,6 +94,16 @@ class ShortcutPackage {
mPackageName = packageName; mPackageName = packageName;
} }
@UserIdInt
public int getUserId() {
return mUserId;
}
@NonNull
public String getPackageName() {
return mPackageName;
}
@Nullable @Nullable
public ShortcutInfo findShortcutById(String id) { public ShortcutInfo findShortcutById(String id) {
return mShortcuts.get(id); return mShortcuts.get(id);
@@ -381,7 +391,8 @@ class ShortcutPackage {
pw.println(")"); pw.println(")");
} }
public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException { public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
final int size = mShortcuts.size(); final int size = mShortcuts.size();
if (size == 0 && mApiCallCount == 0) { if (size == 0 && mApiCallCount == 0) {
@@ -396,14 +407,19 @@ class ShortcutPackage {
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
for (int j = 0; j < size; j++) { for (int j = 0; j < size; j++) {
saveShortcut(out, mShortcuts.valueAt(j)); saveShortcut(out, mShortcuts.valueAt(j), forBackup);
} }
out.endTag(null, TAG_ROOT); out.endTag(null, TAG_ROOT);
} }
private static void saveShortcut(XmlSerializer out, ShortcutInfo si) private static void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
if (forBackup) {
if (!si.isPinned()) {
return; // Backup only pinned icons.
}
}
out.startTag(null, TAG_SHORTCUT); out.startTag(null, TAG_SHORTCUT);
ShortcutService.writeAttr(out, ATTR_ID, si.getId()); ShortcutService.writeAttr(out, ATTR_ID, si.getId());
// writeAttr(out, "package", si.getPackageName()); // not needed // writeAttr(out, "package", si.getPackageName()); // not needed
@@ -414,9 +430,17 @@ class ShortcutPackage {
ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight()); ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
ShortcutService.writeAttr(out, ATTR_TIMESTAMP, ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
si.getLastChangedTimestamp()); si.getLastChangedTimestamp());
ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags()); if (forBackup) {
ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId()); // Don't write icon information. Also drop the dynamic flag.
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath()); ShortcutService.writeAttr(out, ATTR_FLAGS,
si.getFlags() &
~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_DYNAMIC));
} else {
ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
}
ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS, ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
si.getIntentPersistableExtras()); si.getIntentPersistableExtras());

View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.util.Slog;
import com.android.internal.util.Preconditions;
import libcore.io.Base64;
import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
*
* TODO: The methods about signature hashes are copied from BackupManagerService, which is not
* visible here. Unify the code.
*/
class ShortcutPackageInfo implements ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package-info";
private static final String ATTR_NAME = "name";
private static final String ATTR_VERSION = "version";
private static final String ATTR_SHADOW = "shadow";
private static final String TAG_SIGNATURE = "signature";
private static final String ATTR_SIGNATURE_HASH = "hash";
public interface ShortcutPackageInfoHolder {
ShortcutPackageInfo getShortcutPackageInfo();
}
private final String mPackageName;
/**
* When true, this package information was restored from the previous device, and the app hasn't
* been installed yet.
*/
private boolean mIsShadow;
private int mVersionCode;
private ArrayList<byte[]> mSigHashes;
private ShortcutPackageInfo(String packageName, int versionCode, ArrayList<byte[]> sigHashes,
boolean isShadow) {
mVersionCode = versionCode;
mIsShadow = isShadow;
mSigHashes = sigHashes;
mPackageName = Preconditions.checkNotNull(packageName);
}
@NonNull
public String getPackageName() {
return mPackageName;
}
public boolean isShadow() {
return mIsShadow;
}
public boolean isInstalled() {
return !mIsShadow;
}
public void setShadow(boolean shadow) {
mIsShadow = shadow;
}
public int getVersionCode() {
return mVersionCode;
}
private static byte[] hashSignature(Signature sig) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(sig.toByteArray());
return digest.digest();
} catch (NoSuchAlgorithmException e) {
Slog.w(TAG, "No SHA-256 algorithm found!");
}
return null;
}
private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
if (sigs == null) {
return null;
}
ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length);
for (Signature s : sigs) {
hashes.add(hashSignature(s));
}
return hashes;
}
private static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
if (target == null) {
return false;
}
// If the target resides on the system partition, we allow it to restore
// data from the like-named package in a restore set even if the signatures
// do not match. (Unlike general applications, those flashed to the system
// partition will be signed with the device's platform certificate, so on
// different phones the same system app will have different signatures.)
if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return true;
}
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
Signature[] deviceSigs = target.signatures;
if ((storedSigHashes == null || storedSigHashes.size() == 0)
&& (deviceSigs == null || deviceSigs.length == 0)) {
return true;
}
if (storedSigHashes == null || deviceSigs == null) {
return false;
}
// !!! TODO: this demands that every stored signature match one
// that is present on device, and does not demand the converse.
// Is this this right policy?
final int nStored = storedSigHashes.size();
final int nDevice = deviceSigs.length;
// hash each on-device signature
ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
for (int i = 0; i < nDevice; i++) {
deviceHashes.add(hashSignature(deviceSigs[i]));
}
// now ensure that each stored sig (hash) matches an on-device sig (hash)
for (int n = 0; n < nStored; n++) {
boolean match = false;
final byte[] storedHash = storedSigHashes.get(n);
for (int i = 0; i < nDevice; i++) {
if (Arrays.equals(storedHash, deviceHashes.get(i))) {
match = true;
break;
}
}
// match is false when no on-device sig matched one of the stored ones
if (!match) {
return false;
}
}
return true;
}
public boolean canRestoreTo(PackageInfo target) {
if (target.versionCode < mVersionCode) {
Slog.w(TAG, String.format("Package current version %d < backed up version %d",
target.versionCode, mVersionCode));
return false;
}
if (!signaturesMatch(mSigHashes, target)) {
Slog.w(TAG, "Package signature mismtach");
return false;
}
return true;
}
public static ShortcutPackageInfo generateForInstalledPackage(
ShortcutService s, String packageName, @UserIdInt int userId) {
final PackageInfo pi = s.getPackageInfo(packageName, userId, /*signature=*/ true);
if (pi.signatures == null || pi.signatures.length == 0) {
Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null;
}
final ShortcutPackageInfo ret = new ShortcutPackageInfo(packageName, pi.versionCode,
hashSignatureArray(pi.signatures), /* shadow=*/ false);
return ret;
}
public void refreshAndSave(ShortcutService s, @UserIdInt int userId) {
final PackageInfo pi = s.getPackageInfo(mPackageName, userId, /*getSignatures=*/ true);
if (pi == null) {
Slog.w(TAG, "Package not found: " + mPackageName);
return;
}
mVersionCode = pi.versionCode;
mSigHashes = hashSignatureArray(pi.signatures);
s.scheduleSaveUser(userId);
}
public void saveToXml(XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
for (int i = 0; i < mSigHashes.size(); i++) {
out.startTag(null, TAG_SIGNATURE);
ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, Base64.encode(mSigHashes.get(i)));
out.endTag(null, TAG_SIGNATURE);
}
out.endTag(null, TAG_ROOT);
}
public static ShortcutPackageInfo loadFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_NAME);
final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
final ArrayList<byte[]> hashes = new ArrayList<>();
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final int depth = parser.getDepth();
final String tag = parser.getName();
switch (tag) {
case TAG_SIGNATURE: {
final String hash = ShortcutService.parseStringAttribute(
parser, ATTR_SIGNATURE_HASH);
hashes.add(Base64.decode(hash.getBytes()));
continue;
}
}
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutPackageInfo(packageName, versionCode, hashes, shadow);
}
public void dump(ShortcutService s, PrintWriter pw, String prefix) {
pw.println();
pw.print(prefix);
pw.print("PackageInfo: ");
pw.print(mPackageName);
pw.println();
pw.print(prefix);
pw.print(" IsShadow: ");
pw.print(mIsShadow);
pw.println();
pw.print(prefix);
pw.print(" Version: ");
pw.print(mVersionCode);
pw.println();
for (int i = 0; i < mSigHashes.size(); i++) {
pw.print(prefix);
pw.print(" ");
pw.print("SigHash: ");
pw.println(HexEncoding.encode(mSigHashes.get(i)));
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import android.annotation.NonNull;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
public interface ShortcutPackageItem {
@NonNull
String getPackageName();
void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException;
}

View File

@@ -19,13 +19,17 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService; import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps; 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.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
@@ -55,7 +59,6 @@ import android.os.ShellCommand;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.format.Time; import android.text.format.Time;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
@@ -70,13 +73,13 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor; import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread; import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer; 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 libcore.io.IoUtils; import libcore.io.IoUtils;
import libcore.util.Objects;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
@@ -122,6 +125,7 @@ public class ShortcutService extends IShortcutService.Stub {
static final boolean DEBUG = false; // STOPSHIP if true static final boolean DEBUG = false; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true static final boolean DEBUG_LOAD = false; // STOPSHIP if true
static final boolean ENABLE_DEBUG_COMMAND = true; // STOPSHIP if true
@VisibleForTesting @VisibleForTesting
static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -249,6 +253,7 @@ public class ShortcutService extends IShortcutService.Stub {
private int mSaveDelayMillis; private int mSaveDelayMillis;
private final IPackageManager mIPackageManager;
private final PackageManagerInternal mPackageManagerInternal; private final PackageManagerInternal mPackageManagerInternal;
private final UserManager mUserManager; private final UserManager mUserManager;
@@ -264,6 +269,7 @@ public class ShortcutService extends IShortcutService.Stub {
mContext = Preconditions.checkNotNull(context); mContext = Preconditions.checkNotNull(context);
LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
mHandler = new Handler(looper); mHandler = new Handler(looper);
mIPackageManager = AppGlobals.getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mUserManager = context.getSystemService(UserManager.class); mUserManager = context.getSystemService(UserManager.class);
@@ -319,6 +325,8 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) { synchronized (mLock) {
// Preload // Preload
getUserShortcutsLocked(userId); getUserShortcutsLocked(userId);
cleanupGonePackages(userId);
} }
} }
@@ -434,6 +442,10 @@ public class ShortcutService extends IShortcutService.Stub {
return parser.getAttributeValue(null, attribute); return parser.getAttributeValue(null, attribute);
} }
static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
return parseLongAttribute(parser, attribute) == 1;
}
static int parseIntAttribute(XmlPullParser parser, String attribute) { static int parseIntAttribute(XmlPullParser parser, String attribute) {
return (int) parseLongAttribute(parser, attribute); return (int) parseLongAttribute(parser, attribute);
} }
@@ -510,6 +522,12 @@ public class ShortcutService extends IShortcutService.Stub {
writeAttr(out, name, String.valueOf(value)); writeAttr(out, name, String.valueOf(value));
} }
static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
if (value) {
writeAttr(out, name, "1");
}
}
static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
if (comp == null) return; if (comp == null) return;
writeAttr(out, name, comp.flattenToString()); writeAttr(out, name, comp.flattenToString());
@@ -616,7 +634,7 @@ public class ShortcutService extends IShortcutService.Stub {
out.setOutput(outs, StandardCharsets.UTF_8.name()); out.setOutput(outs, StandardCharsets.UTF_8.name());
out.startDocument(null, true); out.startDocument(null, true);
getUserShortcutsLocked(userId).saveToXml(out); getUserShortcutsLocked(userId).saveToXml(this, out, /* forBackup= */ false);
out.endDocument(); out.endDocument();
@@ -682,17 +700,17 @@ public class ShortcutService extends IShortcutService.Stub {
} }
private void scheduleSaveBaseState() { private void scheduleSaveBaseState() {
scheduleSave(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state. scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
} }
void scheduleSaveUser(@UserIdInt int userId) { void scheduleSaveUser(@UserIdInt int userId) {
scheduleSave(userId); scheduleSaveInner(userId);
} }
// In order to re-schedule, we need to reuse the same instance, so keep it in final. // In order to re-schedule, we need to reuse the same instance, so keep it in final.
private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo; private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
private void scheduleSave(@UserIdInt int userId) { private void scheduleSaveInner(@UserIdInt int userId) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Scheduling to save for " + userId); Slog.d(TAG, "Scheduling to save for " + userId);
} }
@@ -1169,6 +1187,8 @@ public class ShortcutService extends IShortcutService.Stub {
final int size = newShortcuts.size(); final int size = newShortcuts.size();
synchronized (mLock) { synchronized (mLock) {
getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
// Throttling. // Throttling.
@@ -1204,6 +1224,8 @@ public class ShortcutService extends IShortcutService.Stub {
final int size = newShortcuts.size(); final int size = newShortcuts.size();
synchronized (mLock) { synchronized (mLock) {
getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
// Throttling. // Throttling.
@@ -1241,6 +1263,8 @@ public class ShortcutService extends IShortcutService.Stub {
verifyCaller(packageName, userId); verifyCaller(packageName, userId);
synchronized (mLock) { synchronized (mLock) {
getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId);
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
// Throttling. // Throttling.
@@ -1376,10 +1400,7 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting @VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
synchronized (mLock) { synchronized (mLock) {
long start = 0; long start = System.currentTimeMillis();
if (DEBUG) {
start = System.currentTimeMillis();
}
final ShortcutUser user = getUserShortcutsLocked(userId); final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -1430,8 +1451,8 @@ public class ShortcutService extends IShortcutService.Stub {
lastPriority = ri.priority; lastPriority = ri.priority;
} }
} }
final long end = System.currentTimeMillis();
if (DEBUG) { if (DEBUG) {
long end = System.currentTimeMillis();
Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start)); Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
} }
if (detected != null) { if (detected != null) {
@@ -1473,6 +1494,9 @@ public class ShortcutService extends IShortcutService.Stub {
mUser.getPackages().valueAt(i).refreshPinnedFlags(this); mUser.getPackages().valueAt(i).refreshPinnedFlags(this);
} }
// Remove the package info too.
mUser.getPackageInfos().remove(packageName);
scheduleSaveUser(userId); scheduleSaveUser(userId);
if (doNotify) { if (doNotify) {
@@ -1567,6 +1591,9 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkNotNull(shortcutIds, "shortcutIds"); Preconditions.checkNotNull(shortcutIds, "shortcutIds");
synchronized (mLock) { synchronized (mLock) {
getUserShortcutsLocked(userId).ensurePackageInfo(
ShortcutService.this, callingPackage, userId);
getLauncherShortcuts(callingPackage, userId).pinShortcuts( getLauncherShortcuts(callingPackage, userId).pinShortcuts(
ShortcutService.this, packageName, shortcutIds); ShortcutService.this, packageName, shortcutIds);
} }
@@ -1640,7 +1667,16 @@ public class ShortcutService extends IShortcutService.Stub {
} }
} }
private PackageMonitor mPackageMonitor = new PackageMonitor() { /**
* Package event callbacks.
*/
@VisibleForTesting
final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
public void onPackageAdded(String packageName, int uid) {
handlePackageAdded(packageName, getChangingUserId());
}
@Override @Override
public void onPackageUpdateFinished(String packageName, int uid) { public void onPackageUpdateFinished(String packageName, int uid) {
handlePackageUpdateFinished(packageName, getChangingUserId()); handlePackageUpdateFinished(packageName, getChangingUserId());
@@ -1650,38 +1686,120 @@ public class ShortcutService extends IShortcutService.Stub {
public void onPackageRemoved(String packageName, int uid) { public void onPackageRemoved(String packageName, int uid) {
handlePackageRemoved(packageName, getChangingUserId()); handlePackageRemoved(packageName, getChangingUserId());
} }
@Override
public void onPackageRemovedAllUsers(String packageName, int uid) {
handlePackageRemovedAllUsers(packageName, getChangingUserId());
}
}; };
void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) { /**
* Called when a user is unlocked. Check all known packages still exist, and otherwise
* perform cleanup.
*/
private void cleanupGonePackages(@UserIdInt int userId) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "onPackageUpdateFinished() userId=" + userId); Slog.d(TAG, "cleanupGonePackages() userId=" + userId);
}
ArrayList<String> 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;
}
if (isPackageInstalled(info.getPackageName(), userId)) {
continue;
}
gonePackages = ArrayUtils.add(gonePackages, info.getPackageName());
}
if (gonePackages != null) {
for (int i = gonePackages.size() - 1; i >= 0; i--) {
handlePackageGone(gonePackages.get(i), userId);
}
} }
// TODO Update the version.
} }
void handlePackageRemoved(String packageName, @UserIdInt int userId) { private void handlePackageAdded(String packageName, @UserIdInt int userId) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "onPackageRemoved() userId=" + userId); 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);
if (existing != null && existing.isShadow()) {
Slog.w(TAG, "handlePackageAdded: TODO Restore not implemented");
}
}
}
private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
final PackageInfo pi = getPackageInfo(packageName, userId);
if (pi == null) {
Slog.w(TAG, "Package not found: " + packageName);
return;
}
synchronized (mLock) {
final ShortcutPackageInfo spi =
getUserShortcutsLocked(userId).getPackageInfos().get(packageName);
if (spi != null) {
spi.refreshAndSave(this, userId);
}
}
}
private void handlePackageRemoved(String packageName, @UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId));
} }
synchronized (mLock) { synchronized (mLock) {
cleanUpPackageLocked(packageName, userId); cleanUpPackageLocked(packageName, userId);
} }
} }
void handlePackageRemovedAllUsers(String packageName, @UserIdInt int userId) { private void handlePackageGone(String packageName, @UserIdInt int userId) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "onPackageRemovedAllUsers() userId=" + userId); Slog.d(TAG, String.format("handlePackageGone: %s user=%d", packageName, userId));
} }
synchronized (mLock) { synchronized (mLock) {
cleanUpPackageLocked(packageName, userId); cleanUpPackageLocked(packageName, userId);
} }
}
// TODO Remove from all users, which we can't if the user is locked. // === Backup & restore ===
PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
return getPackageInfo(packageName, userId, /*getSignatures=*/ false);
}
PackageInfo getPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
return injectPackageInfo(packageName, userId, getSignatures);
}
@VisibleForTesting
PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
try {
return mIPackageManager.getPackageInfo(packageName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| (getSignatures ? PackageManager.GET_SIGNATURES : 0)
, userId);
} catch (RemoteException e) {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
return null;
}
}
boolean shouldBackupApp(String packageName, int userId) {
final PackageInfo pi = getPackageInfo(packageName, userId);
return (pi != null) &&
((pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
}
private boolean isPackageInstalled(String packageName, int userId) {
return getPackageInfo(packageName, userId) != null;
} }
// === Dump === // === Dump ===
@@ -2039,4 +2157,14 @@ public class ShortcutService extends IShortcutService.Stub {
return pkg.findShortcutById(shortcutId); return pkg.findShortcutById(shortcutId);
} }
} }
@VisibleForTesting
ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId) {
synchronized (mLock) {
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
return user.getPackageInfos().get(packageName);
}
}
} }

View File

@@ -19,6 +19,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.content.ComponentName; import android.content.ComponentName;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Slog;
import libcore.util.Objects; import libcore.util.Objects;
@@ -47,6 +48,8 @@ class ShortcutUser {
private final ArrayMap<String, ShortcutLauncher> mLaunchers = new ArrayMap<>(); private final ArrayMap<String, ShortcutLauncher> mLaunchers = new ArrayMap<>();
private final ArrayMap<String, ShortcutPackageInfo> mPackageInfos = new ArrayMap<>();
private ComponentName mLauncherComponent; private ComponentName mLauncherComponent;
public ShortcutUser(int userId) { public ShortcutUser(int userId) {
@@ -61,6 +64,10 @@ class ShortcutUser {
return mLaunchers; return mLaunchers;
} }
public ArrayMap<String, ShortcutPackageInfo> getPackageInfos() {
return mPackageInfos;
}
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) {
@@ -79,25 +86,58 @@ class ShortcutUser {
return ret; return ret;
} }
public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { public void ensurePackageInfo(ShortcutService s, String packageName, @UserIdInt int userId) {
final ShortcutPackageInfo existing = mPackageInfos.get(packageName);
if (existing != null) {
return;
}
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format("Fetching package info: %s user=%d", packageName, userId));
}
final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage(
s, packageName, userId);
mPackageInfos.put(packageName, newSpi);
s.scheduleSaveUser(mUserId);
}
public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
out.startTag(null, TAG_ROOT); out.startTag(null, TAG_ROOT);
ShortcutService.writeTagValue(out, TAG_LAUNCHER, ShortcutService.writeTagValue(out, TAG_LAUNCHER,
mLauncherComponent); mLauncherComponent);
final int lsize = mLaunchers.size(); {
for (int i = 0; i < lsize; i++) { final int size = mPackageInfos.size();
mLaunchers.valueAt(i).saveToXml(out); for (int i = 0; i < size; i++) {
saveShortcutPackageItem(s, out, mPackageInfos.valueAt(i), forBackup);
}
} }
{
final int psize = mPackages.size(); final int size = mLaunchers.size();
for (int i = 0; i < psize; i++) { for (int i = 0; i < size; i++) {
mPackages.valueAt(i).saveToXml(out); saveShortcutPackageItem(s, out, mLaunchers.valueAt(i), forBackup);
}
}
{
final int size = mPackages.size();
for (int i = 0; i < size; i++) {
saveShortcutPackageItem(s, out, mPackages.valueAt(i), forBackup);
}
} }
out.endTag(null, TAG_ROOT); out.endTag(null, TAG_ROOT);
} }
private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out,
ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
if (forBackup && !s.shouldBackupApp(spi.getPackageName(), mUserId)) {
return; // Don't save.
}
spi.saveToXml(out, forBackup);
}
public static ShortcutUser loadFromXml(XmlPullParser parser, int userId) public static ShortcutUser loadFromXml(XmlPullParser parser, int userId)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
final ShortcutUser ret = new ShortcutUser(userId); final ShortcutUser ret = new ShortcutUser(userId);
@@ -121,7 +161,7 @@ class ShortcutUser {
final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(parser, userId); final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(parser, userId);
// Don't use addShortcut(), we don't need to save the icon. // Don't use addShortcut(), we don't need to save the icon.
ret.getPackages().put(shortcuts.mPackageName, shortcuts); ret.getPackages().put(shortcuts.getPackageName(), shortcuts);
continue; continue;
} }
@@ -129,7 +169,15 @@ class ShortcutUser {
final ShortcutLauncher shortcuts = final ShortcutLauncher shortcuts =
ShortcutLauncher.loadFromXml(parser, userId); ShortcutLauncher.loadFromXml(parser, userId);
ret.getLaunchers().put(shortcuts.mPackageName, shortcuts); ret.getLaunchers().put(shortcuts.getPackageName(), shortcuts);
continue;
}
case ShortcutPackageInfo.TAG_ROOT: {
final ShortcutPackageInfo pi =
ShortcutPackageInfo.loadFromXml(parser);
ret.getPackageInfos().put(pi.getPackageName(), pi);
continue; continue;
} }
} }
@@ -175,5 +223,9 @@ class ShortcutUser {
for (int i = 0; i < mPackages.size(); i++) { for (int i = 0; i < mPackages.size(); i++) {
mPackages.valueAt(i).dump(s, pw, prefix + " "); mPackages.valueAt(i).dump(s, pw, prefix + " ");
} }
for (int i = 0; i < mPackageInfos.size(); i++) {
mPackageInfos.valueAt(i).dump(s, pw, prefix + " ");
}
} }
} }

View File

@@ -32,19 +32,23 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps; import android.content.pm.ILauncherApps;
import android.content.pm.LauncherApps; 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.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager; import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal; import android.content.pm.ShortcutServiceInternal;
import android.content.pm.Signature;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat; import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.BaseBundle; import android.os.BaseBundle;
import android.os.Bundle; import android.os.Bundle;
import android.os.FileUtils; import android.os.FileUtils;
@@ -103,7 +107,6 @@ import java.util.Set;
* TODO: separate, detailed tests for ShortcutInfo (CTS?) * * TODO: separate, detailed tests for ShortcutInfo (CTS?) *
* *
* TODO: Cross-user test (do in CTS?) * TODO: Cross-user test (do in CTS?)
*
*/ */
@SmallTest @SmallTest
public class ShortcutManagerTest extends InstrumentationTestCase { public class ShortcutManagerTest extends InstrumentationTestCase {
@@ -217,8 +220,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
@Override @Override
int injectGetPackageUid(String packageName, int userId) { int injectGetPackageUid(String packageName, int userId) {
Integer uid = mInjectedPackageUidMap.get(packageName); return getInjectedPackageInfo(packageName, userId, false).applicationInfo.uid;
return UserHandle.getUid(getCallingUserId(), (uid != null ? uid : 0));
} }
@Override @Override
@@ -252,6 +254,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage); return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage);
} }
@Override
PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
return getInjectedPackageInfo(packageName, userId, getSignatures);
}
@Override @Override
void postToHandler(Runnable r) { void postToHandler(Runnable r) {
final long token = mContext.injectClearCallingIdentity(); final long token = mContext.injectClearCallingIdentity();
@@ -355,7 +363,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
private int mInjectedCallingUid; private int mInjectedCallingUid;
private String mInjectedClientPackage; private String mInjectedClientPackage;
private Map<String, Integer> mInjectedPackageUidMap; private Map<String, PackageInfo> mInjectedPackages;
private PackageManager mMockPackageManager; private PackageManager mMockPackageManager;
private PackageManagerInternal mMockPackageManagerInternal; private PackageManagerInternal mMockPackageManagerInternal;
@@ -407,12 +415,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
mInjectedCurrentTimeLillis = START_TIME; mInjectedCurrentTimeLillis = START_TIME;
mInjectedPackageUidMap = new HashMap<>(); mInjectedPackages = new HashMap<>();;
mInjectedPackageUidMap.put(CALLING_PACKAGE_1, CALLING_UID_1); addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1);
mInjectedPackageUidMap.put(CALLING_PACKAGE_2, CALLING_UID_2); addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2);
mInjectedPackageUidMap.put(CALLING_PACKAGE_3, CALLING_UID_3); addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3);
mInjectedPackageUidMap.put(LAUNCHER_1, LAUNCHER_UID_1); addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4);
mInjectedPackageUidMap.put(LAUNCHER_2, LAUNCHER_UID_2); addPackage(LAUNCHER_2, LAUNCHER_UID_2, 5);
mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files"); mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
@@ -448,12 +456,57 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY); mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
} }
private void addPackage(String packageName, int uid, int version) {
addPackage(packageName, uid, version, packageName);
}
private Signature[] genSignatures(String... signatures) {
final Signature[] sigs = new Signature[signatures.length];
for (int i = 0; i < signatures.length; i++){
sigs[i] = new Signature(signatures[i].getBytes());
}
return sigs;
}
private PackageInfo genPackage(String packageName, int uid, int version, String... signatures) {
final PackageInfo pi = new PackageInfo();
pi.packageName = packageName;
pi.applicationInfo = new ApplicationInfo();
pi.applicationInfo.uid = uid;
pi.versionCode = version;
pi.signatures = genSignatures(signatures);
return pi;
}
private void addPackage(String packageName, int uid, int version, String... signatures) {
mInjectedPackages.put(packageName, genPackage(packageName, uid, version, signatures));
}
PackageInfo getInjectedPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
final PackageInfo pi = mInjectedPackages.get(packageName);
if (pi == null) return null;
final PackageInfo ret = new PackageInfo();
ret.packageName = pi.packageName;
ret.versionCode = pi.versionCode;
ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
ret.applicationInfo.uid = UserHandle.getUid(userId, pi.applicationInfo.uid);
if (getSignatures) {
ret.signatures = pi.signatures;
}
return ret;
}
/** Replace the current calling package */ /** Replace the current calling package */
private void setCaller(String packageName, int userId) { private void setCaller(String packageName, int userId) {
mInjectedClientPackage = packageName; mInjectedClientPackage = packageName;
mInjectedCallingUid = UserHandle.getUid(userId, mInjectedCallingUid =
Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName), Preconditions.checkNotNull(getInjectedPackageInfo(packageName, userId, false),
"Unknown package")); "Unknown package").applicationInfo.uid;
} }
private void setCaller(String packageName) { private void setCaller(String packageName) {
@@ -466,13 +519,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
private void runWithCaller(String packageName, int userId, Runnable r) { private void runWithCaller(String packageName, int userId, Runnable r) {
final String previousPackage = mInjectedClientPackage; final String previousPackage = mInjectedClientPackage;
final int previousUid = mInjectedCallingUid; final int previousUserId = UserHandle.getUserId(mInjectedCallingUid);
setCaller(packageName, userId); setCaller(packageName, userId);
r.run(); r.run();
setCaller(previousPackage, previousUid); setCaller(previousPackage, previousUserId);
} }
private int getCallingUserId() { private int getCallingUserId() {
@@ -852,6 +905,18 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertTrue(b == null || b.size() == 0); assertTrue(b == null || b.size() == 0);
} }
private void assertShortcutPackageInfo(String packageName, int userId, int expectedVersion) {
ShortcutPackageInfo spi = mService.getPackageInfoForTest(packageName, userId);
assertNotNull(spi);
assertEquals(expectedVersion, spi.getVersionCode());
assertTrue(spi.canRestoreTo(genPackage(packageName, /*uid*/ 0, 9999999, packageName)));
}
private void assertNoShortcutPackageInfo(String packageName, int userId) {
assertNull(mService.getPackageInfoForTest(packageName, userId));
}
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) { private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
return mService.getPackageShortcutForTest(packageName, shortcutId, userId); return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
} }
@@ -886,6 +951,22 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED); return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
} }
private Intent genPackageDeleteIntent(String pakcageName, int userId) {
Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
i.setData(Uri.parse("package:" + pakcageName));
i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
return i;
}
private Intent genPackageUpdateIntent(String pakcageName, int userId) {
Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED);
i.setData(Uri.parse("package:" + pakcageName));
i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
i.putExtra(Intent.EXTRA_REPLACING, true);
return i;
}
/** /**
* Wrap a set in an ArraySet just to get a better toString. * Wrap a set in an ArraySet just to get a better toString.
*/ */
@@ -1017,6 +1098,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
} }
public void testSetDynamicShortcuts() { public void testSetDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_0);
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1); final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource( final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.icon2)); getTestContext().getResources(), R.drawable.icon2));
@@ -1045,6 +1128,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
"shortcut1", "shortcut2"); "shortcut1", "shortcut2");
assertEquals(2, mManager.getRemainingCallCount()); assertEquals(2, mManager.getRemainingCallCount());
assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_0);
assertNoShortcutPackageInfo(CALLING_PACKAGE_1, USER_10);
assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_10);
// TODO: Check fields // TODO: Check fields
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
@@ -1068,9 +1156,20 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertEquals(2, mManager.getDynamicShortcuts().size()); assertEquals(2, mManager.getDynamicShortcuts().size());
// TODO Check max number // TODO Check max number
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(Arrays.asList(makeShortcut("s1"))));
assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_0);
assertNoShortcutPackageInfo(CALLING_PACKAGE_1, USER_10);
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_10, 2);
});
} }
public void testAddDynamicShortcuts() { public void testAddDynamicShortcuts() {
setCaller(CALLING_PACKAGE_1, USER_0);
final ShortcutInfo si1 = makeShortcut("shortcut1"); final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2"); final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3"); final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -1099,6 +1198,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// TODO Check max number // TODO Check max number
// TODO Check fields. // TODO Check fields.
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_10, 2);
});
} }
public void testDeleteDynamicShortcut() { public void testDeleteDynamicShortcut() {
@@ -1691,6 +1795,15 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// TODO Check with other fields too. // TODO Check with other fields too.
// TODO Check bitmap removal too. // TODO Check bitmap removal too.
runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_11);
mManager.updateShortcuts(Arrays.asList());
// Even an empty update call will populate the package info.
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_11, 2);
});
} }
// TODO: updateShortcuts() // TODO: updateShortcuts()
@@ -1886,9 +1999,16 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// Pin some. // Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> { runWithCaller(LAUNCHER_1, USER_0, () -> {
assertNoShortcutPackageInfo(LAUNCHER_1, USER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s2", "s3"), getCallingUser()); Arrays.asList("s2", "s3"), getCallingUser());
assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
assertNoShortcutPackageInfo(LAUNCHER_1, USER_10);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s3", "s4", "s5"), getCallingUser()); Arrays.asList("s3", "s4", "s5"), getCallingUser());
@@ -1949,11 +2069,19 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
dumpsysOnLogcat(); dumpsysOnLogcat();
assertNoShortcutPackageInfo(LAUNCHER_1, USER_0);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
assertNoShortcutPackageInfo(LAUNCHER_1, USER_10);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
// Pin some. // Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> { runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s3", "s4"), getCallingUser()); Arrays.asList("s3", "s4"), getCallingUser());
assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s1", "s2", "s4"), getCallingUser()); Arrays.asList("s1", "s2", "s4"), getCallingUser());
}); });
@@ -2027,10 +2155,16 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())), | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s2"); "s2");
assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
// Now pin some. // Now pin some.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
Arrays.asList("s1", "s2"), getCallingUser()); Arrays.asList("s1", "s2"), getCallingUser());
assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
Arrays.asList("s1", "s2"), getCallingUser()); Arrays.asList("s1", "s2"), getCallingUser());
@@ -2048,10 +2182,26 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
"s2"); "s2");
}); });
assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
// Re-initialize and load from the files. // Re-initialize and load from the files.
mService.saveDirtyInfo(); mService.saveDirtyInfo();
initService(); initService();
// Load from file.
mService.handleUnlockUser(USER_0);
// Make sure package info is restored too.
assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
runWithCaller(LAUNCHER_1, USER_0, () -> { runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly( assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1, mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2489,6 +2639,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent( mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent(
mService, new ComponentName("pkg1", "class")); mService, new ComponentName("pkg1", "class"));
assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
// Restore. // Restore.
mService.saveDirtyInfo(); mService.saveDirtyInfo();
initService(); initService();
@@ -2499,6 +2653,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// this will pre-load the per-user info. // this will pre-load the per-user info.
mService.handleUnlockUser(UserHandle.USER_SYSTEM); mService.handleUnlockUser(UserHandle.USER_SYSTEM);
assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
// Now it's loaded. // Now it's loaded.
assertEquals(1, mService.getShortcutsForTest().size()); assertEquals(1, mService.getShortcutsForTest().size());
@@ -2637,6 +2795,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10); assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
mService.saveDirtyInfo(); mService.saveDirtyInfo();
// Nonexistent package. // Nonexistent package.
@@ -2664,6 +2827,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10); assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
mService.saveDirtyInfo(); mService.saveDirtyInfo();
// Remove a package. // Remove a package.
@@ -2690,6 +2858,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10); assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
mService.saveDirtyInfo(); mService.saveDirtyInfo();
// Remove a launcher. // Remove a launcher.
@@ -2738,6 +2911,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10); assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
mService.saveDirtyInfo(); mService.saveDirtyInfo();
// Remove the other launcher from user 10 too. // Remove the other launcher from user 10 too.
@@ -2792,4 +2970,162 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// TODO Detailed test for hasShortcutPermissionInner(). // TODO Detailed test for hasShortcutPermissionInner().
// TODO Add tests for the command line functions too. // TODO Add tests for the command line functions too.
private void checkCanRestoreTo(boolean expected, ShortcutPackageInfo spi,
int version, String... signatures) {
assertEquals(expected, spi.canRestoreTo(genPackage(
"dummy", /* uid */ 0, version, signatures)));
}
public void testCanRestoreTo() {
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
addPackage(CALLING_PACKAGE_2, CALLING_UID_1, 10, "sig1", "sig2");
final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage(
mService, CALLING_PACKAGE_1, USER_0);
final ShortcutPackageInfo spi2 = ShortcutPackageInfo.generateForInstalledPackage(
mService, CALLING_PACKAGE_2, USER_0);
checkCanRestoreTo(true, spi1, 10, "sig1");
checkCanRestoreTo(true, spi1, 10, "x", "sig1");
checkCanRestoreTo(true, spi1, 10, "sig1", "y");
checkCanRestoreTo(true, spi1, 10, "x", "sig1", "y");
checkCanRestoreTo(true, spi1, 11, "sig1");
checkCanRestoreTo(false, spi1, 10 /* empty */);
checkCanRestoreTo(false, spi1, 10, "x");
checkCanRestoreTo(false, spi1, 10, "x", "y");
checkCanRestoreTo(false, spi1, 10, "x");
checkCanRestoreTo(false, spi1, 9, "sig1");
checkCanRestoreTo(true, spi2, 10, "sig1", "sig2");
checkCanRestoreTo(true, spi2, 10, "sig2", "sig1");
checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2");
checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1");
checkCanRestoreTo(true, spi2, 10, "sig1", "sig2", "y");
checkCanRestoreTo(true, spi2, 10, "sig2", "sig1", "y");
checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2", "y");
checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1", "y");
checkCanRestoreTo(true, spi2, 11, "x", "sig2", "sig1", "y");
checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x");
checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x");
checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2");
checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1");
checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x", "y");
checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x", "y");
checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2", "y");
checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1", "y");
checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y");
}
public void testShortcutPackageInfoRefresh() {
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage(
mService, CALLING_PACKAGE_1, USER_0);
checkCanRestoreTo(true, spi1, 10, "sig1");
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 11, "sig1", "sig2");
spi1.refreshAndSave(mService, USER_0);
mService.handleCleanupUser(USER_0);
initService();
checkCanRestoreTo(false, spi1, 10, "sig1", "sig2");
checkCanRestoreTo(false, spi1, 11, "sig", "sig2");
checkCanRestoreTo(false, spi1, 11, "sig1", "sig");
checkCanRestoreTo(true, spi1, 11, "sig1", "sig2");
}
public void testHandlePackageDelete() {
setCaller(CALLING_PACKAGE_1, USER_0);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
setCaller(CALLING_PACKAGE_2, USER_0);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
setCaller(CALLING_PACKAGE_3, USER_0);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
setCaller(CALLING_PACKAGE_1, USER_10);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
setCaller(CALLING_PACKAGE_2, USER_10);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
setCaller(CALLING_PACKAGE_3, USER_10);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
mInjectedPackages.remove(CALLING_PACKAGE_1);
mInjectedPackages.remove(CALLING_PACKAGE_3);
mService.handleUnlockUser(USER_0);
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
mService.handleUnlockUser(USER_10);
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
}
public void testHandlePackageUpdate() {
setCaller(CALLING_PACKAGE_1, USER_0);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode());
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 123);
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent("abc", USER_0));
assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode());
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent("abc", USER_10));
assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode());
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(123, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)
.getVersionCode());
}
} }