Merge "ShortcutManager: Handle package broadcasts." into nyc-dev
am: ec1a2da
* commit 'ec1a2da6e086347c73618d63d0409910ebd47efb':
ShortcutManager: Handle package broadcasts.
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 + " ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user