Loading services/core/java/com/android/server/pm/ShortcutPackage.java +11 −0 Original line number Diff line number Diff line Loading @@ -1151,6 +1151,17 @@ class ShortcutPackage extends ShortcutPackageItem { } } /** @return true if there's any shortcuts that are not manifest shortcuts. */ public boolean hasNonManifestShortcuts() { for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); if (!si.isDeclaredInManifest()) { return true; } } return false; } public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { pw.println(); Loading services/core/java/com/android/server/pm/ShortcutPackageItem.java +8 −1 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ abstract class ShortcutPackageItem { private final ShortcutPackageInfo mPackageInfo; protected final ShortcutUser mShortcutUser; protected ShortcutUser mShortcutUser; protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser, int packageUserId, @NonNull String packageName, Loading @@ -51,6 +51,13 @@ abstract class ShortcutPackageItem { mPackageInfo = Preconditions.checkNotNull(packageInfo); } /** * Change the parent {@link ShortcutUser}. Need it in the restore code. */ public void replaceUser(ShortcutUser user) { mShortcutUser = user; } public ShortcutUser getUser() { return mShortcutUser; } Loading services/core/java/com/android/server/pm/ShortcutService.java +14 −6 Original line number Diff line number Diff line Loading @@ -380,6 +380,12 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") private Exception mLastWtfStacktrace; static class InvalidFileFormatException extends Exception { public InvalidFileFormatException(String message, Throwable cause) { super(message, cause); } } public ShortcutService(Context context) { this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); } Loading Loading @@ -961,7 +967,7 @@ public class ShortcutService extends IShortcutService.Stub { try { final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false); return ret; } catch (IOException | XmlPullParserException e) { } catch (IOException | XmlPullParserException | InvalidFileFormatException e) { Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); return null; } finally { Loading @@ -970,7 +976,8 @@ public class ShortcutService extends IShortcutService.Stub { } private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, boolean fromBackup) throws XmlPullParserException, IOException { boolean fromBackup) throws XmlPullParserException, IOException, InvalidFileFormatException { final BufferedInputStream bis = new BufferedInputStream(is); Loading Loading @@ -3170,15 +3177,16 @@ public class ShortcutService extends IShortcutService.Stub { wtf("Can't restore: user " + userId + " is locked or not running"); return; } final ShortcutUser user; // Actually do restore. final ShortcutUser restored; final ByteArrayInputStream is = new ByteArrayInputStream(payload); try { user = loadUserInternal(userId, is, /* fromBackup */ true); } catch (XmlPullParserException | IOException e) { restored = loadUserInternal(userId, is, /* fromBackup */ true); } catch (XmlPullParserException | IOException | InvalidFileFormatException e) { Slog.w(TAG, "Restoration failed.", e); return; } mUsers.put(userId, user); getUserShortcutsLocked(userId).mergeRestoredFile(restored); // Rescan all packages to re-publish manifest shortcuts and do other checks. rescanUpdatedPackagesLocked(userId, Loading services/core/java/com/android/server/pm/ShortcutUser.java +111 −50 Original line number Diff line number Diff line Loading @@ -23,12 +23,14 @@ import android.content.pm.ShortcutManager; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.pm.ShortcutService.InvalidFileFormatException; import libcore.util.Objects; Loading Loading @@ -164,6 +166,11 @@ class ShortcutUser { return mPackages.containsKey(packageName); } private void addPackage(@NonNull ShortcutPackage p) { p.replaceUser(this); mPackages.put(p.getPackageName(), p); } public ShortcutPackage removePackage(@NonNull String packageName) { final ShortcutPackage removed = mPackages.remove(packageName); Loading @@ -179,7 +186,8 @@ class ShortcutUser { return mLaunchers; } public void addLauncher(ShortcutLauncher launcher) { private void addLauncher(ShortcutLauncher launcher) { launcher.replaceUser(this); mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(), launcher.getPackageName()), launcher); } Loading Loading @@ -326,6 +334,8 @@ class ShortcutUser { throws IOException, XmlPullParserException { out.startTag(null, TAG_ROOT); if (!forBackup) { // Don't have to back them up. ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales); ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, mLastAppScanTime); Loading @@ -333,6 +343,7 @@ class ShortcutUser { mLastAppScanOsFingerprint); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); } // Can't use forEachPackageItem due to the checked exceptions. { Loading Loading @@ -365,9 +376,10 @@ class ShortcutUser { } public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, boolean fromBackup) throws IOException, XmlPullParserException { boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException { final ShortcutUser ret = new ShortcutUser(s, userId); try { ret.mKnownLocales = ShortcutService.parseStringAttribute(parser, ATTR_KNOWN_LOCALES); Loading Loading @@ -414,6 +426,10 @@ class ShortcutUser { } ShortcutService.warnForInvalidTag(depth, tag); } } catch (RuntimeException e) { throw new ShortcutService.InvalidFileFormatException( "Unable to parse file", e); } return ret; } Loading Loading @@ -461,6 +477,51 @@ class ShortcutUser { } } public void mergeRestoredFile(ShortcutUser restored) { final ShortcutService s = mService; // Note, a restore happens only at the end of setup wizard. At this point, no apps are // installed from Play Store yet, but it's still possible that system apps have already // published dynamic shortcuts, since some apps do so on BOOT_COMPLETED. // When such a system app has allowbackup=true, then we go ahead and replace all existing // shortcuts with the restored shortcuts. (Then we'll re-publish manifest shortcuts later // in the call site.) // When such a system app has allowbackup=false, then we'll keep the shortcuts that have // already been published. So we selectively add restored ShortcutPackages here. // // The same logic applies to launchers, but since launchers shouldn't pin shortcuts // without users interaction it's really not a big deal, so we just clear existing // ShortcutLauncher instances in mLaunchers and add all the restored ones here. mLaunchers.clear(); restored.forAllLaunchers(sl -> { // If the app is already installed and allowbackup = false, then ignore the restored // data. if (s.isPackageInstalled(sl.getPackageName(), getUserId()) && !s.shouldBackupApp(sl.getPackageName(), getUserId())) { return; } addLauncher(sl); }); restored.forAllPackages(sp -> { // If the app is already installed and allowbackup = false, then ignore the restored // data. if (s.isPackageInstalled(sp.getPackageName(), getUserId()) && !s.shouldBackupApp(sp.getPackageName(), getUserId())) { return; } final ShortcutPackage previous = getPackageShortcutsIfExists(sp.getPackageName()); if (previous != null && previous.hasNonManifestShortcuts()) { Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored." + " Existing non-manifeset shortcuts will be overwritten."); } addPackage(sp); }); // Empty the launchers and packages in restored to avoid accidentally using them. restored.mLaunchers.clear(); restored.mPackages.clear(); } public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { pw.print(prefix); pw.print("User: "); Loading services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +30 −0 Original line number Diff line number Diff line Loading @@ -5359,6 +5359,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * It's the case with preintalled apps -- when applyRestore() is called, the system * apps are already installed, so manifest shortcuts need to be re-published. * * Also, when a restore target app is already installed, and * - if it has allowBackup=true, we'll restore normally, so all existing shortcuts will be * replaced. (but manifest shortcuts will be re-published anyway.) We log a warning on * logcat. * - if it has allowBackup=false, we don't touch any of the existing shortcuts. */ public void testBackupAndRestore_appAlreadyInstalledWhenRestored() { // Pre-backup. Same as testBackupAndRestore_manifestRePublished(). Loading Loading @@ -5390,6 +5396,19 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mService.mPackageMonitor.onReceive(mServiceContext, genPackageAddIntent(CALLING_PACKAGE_1, USER_0)); // Set up shortcuts for package 3, which won't be backed up / restored. addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()), R.xml.shortcut_1); updatePackageVersion(CALLING_PACKAGE_3, 1); mService.mPackageMonitor.onReceive(mServiceContext, genPackageAddIntent(CALLING_PACKAGE_3, USER_0)); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertTrue(getManager().setDynamicShortcuts(list( makeShortcut("s1")))); }); // Make sure the manifest shortcuts have been published. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertWith(getCallerShortcuts()) Loading @@ -5415,6 +5434,11 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .areAllDisabled(); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertWith(getCallerShortcuts()) .haveIds("s1", "ms1"); }); // Backup and *without restarting the service, just call applyRestore()*. { int prevUid = mInjectedCallingUid; Loading Loading @@ -5454,6 +5478,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .areAllNotDynamic() ; }); // Package 3 still has the same shortcuts. runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertWith(getCallerShortcuts()) .haveIds("s1", "ms1"); }); } public void testSaveAndLoad_crossProfile() { Loading Loading
services/core/java/com/android/server/pm/ShortcutPackage.java +11 −0 Original line number Diff line number Diff line Loading @@ -1151,6 +1151,17 @@ class ShortcutPackage extends ShortcutPackageItem { } } /** @return true if there's any shortcuts that are not manifest shortcuts. */ public boolean hasNonManifestShortcuts() { for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); if (!si.isDeclaredInManifest()) { return true; } } return false; } public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { pw.println(); Loading
services/core/java/com/android/server/pm/ShortcutPackageItem.java +8 −1 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ abstract class ShortcutPackageItem { private final ShortcutPackageInfo mPackageInfo; protected final ShortcutUser mShortcutUser; protected ShortcutUser mShortcutUser; protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser, int packageUserId, @NonNull String packageName, Loading @@ -51,6 +51,13 @@ abstract class ShortcutPackageItem { mPackageInfo = Preconditions.checkNotNull(packageInfo); } /** * Change the parent {@link ShortcutUser}. Need it in the restore code. */ public void replaceUser(ShortcutUser user) { mShortcutUser = user; } public ShortcutUser getUser() { return mShortcutUser; } Loading
services/core/java/com/android/server/pm/ShortcutService.java +14 −6 Original line number Diff line number Diff line Loading @@ -380,6 +380,12 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") private Exception mLastWtfStacktrace; static class InvalidFileFormatException extends Exception { public InvalidFileFormatException(String message, Throwable cause) { super(message, cause); } } public ShortcutService(Context context) { this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); } Loading Loading @@ -961,7 +967,7 @@ public class ShortcutService extends IShortcutService.Stub { try { final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false); return ret; } catch (IOException | XmlPullParserException e) { } catch (IOException | XmlPullParserException | InvalidFileFormatException e) { Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); return null; } finally { Loading @@ -970,7 +976,8 @@ public class ShortcutService extends IShortcutService.Stub { } private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, boolean fromBackup) throws XmlPullParserException, IOException { boolean fromBackup) throws XmlPullParserException, IOException, InvalidFileFormatException { final BufferedInputStream bis = new BufferedInputStream(is); Loading Loading @@ -3170,15 +3177,16 @@ public class ShortcutService extends IShortcutService.Stub { wtf("Can't restore: user " + userId + " is locked or not running"); return; } final ShortcutUser user; // Actually do restore. final ShortcutUser restored; final ByteArrayInputStream is = new ByteArrayInputStream(payload); try { user = loadUserInternal(userId, is, /* fromBackup */ true); } catch (XmlPullParserException | IOException e) { restored = loadUserInternal(userId, is, /* fromBackup */ true); } catch (XmlPullParserException | IOException | InvalidFileFormatException e) { Slog.w(TAG, "Restoration failed.", e); return; } mUsers.put(userId, user); getUserShortcutsLocked(userId).mergeRestoredFile(restored); // Rescan all packages to re-publish manifest shortcuts and do other checks. rescanUpdatedPackagesLocked(userId, Loading
services/core/java/com/android/server/pm/ShortcutUser.java +111 −50 Original line number Diff line number Diff line Loading @@ -23,12 +23,14 @@ import android.content.pm.ShortcutManager; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.pm.ShortcutService.InvalidFileFormatException; import libcore.util.Objects; Loading Loading @@ -164,6 +166,11 @@ class ShortcutUser { return mPackages.containsKey(packageName); } private void addPackage(@NonNull ShortcutPackage p) { p.replaceUser(this); mPackages.put(p.getPackageName(), p); } public ShortcutPackage removePackage(@NonNull String packageName) { final ShortcutPackage removed = mPackages.remove(packageName); Loading @@ -179,7 +186,8 @@ class ShortcutUser { return mLaunchers; } public void addLauncher(ShortcutLauncher launcher) { private void addLauncher(ShortcutLauncher launcher) { launcher.replaceUser(this); mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(), launcher.getPackageName()), launcher); } Loading Loading @@ -326,6 +334,8 @@ class ShortcutUser { throws IOException, XmlPullParserException { out.startTag(null, TAG_ROOT); if (!forBackup) { // Don't have to back them up. ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales); ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, mLastAppScanTime); Loading @@ -333,6 +343,7 @@ class ShortcutUser { mLastAppScanOsFingerprint); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); } // Can't use forEachPackageItem due to the checked exceptions. { Loading Loading @@ -365,9 +376,10 @@ class ShortcutUser { } public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, boolean fromBackup) throws IOException, XmlPullParserException { boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException { final ShortcutUser ret = new ShortcutUser(s, userId); try { ret.mKnownLocales = ShortcutService.parseStringAttribute(parser, ATTR_KNOWN_LOCALES); Loading Loading @@ -414,6 +426,10 @@ class ShortcutUser { } ShortcutService.warnForInvalidTag(depth, tag); } } catch (RuntimeException e) { throw new ShortcutService.InvalidFileFormatException( "Unable to parse file", e); } return ret; } Loading Loading @@ -461,6 +477,51 @@ class ShortcutUser { } } public void mergeRestoredFile(ShortcutUser restored) { final ShortcutService s = mService; // Note, a restore happens only at the end of setup wizard. At this point, no apps are // installed from Play Store yet, but it's still possible that system apps have already // published dynamic shortcuts, since some apps do so on BOOT_COMPLETED. // When such a system app has allowbackup=true, then we go ahead and replace all existing // shortcuts with the restored shortcuts. (Then we'll re-publish manifest shortcuts later // in the call site.) // When such a system app has allowbackup=false, then we'll keep the shortcuts that have // already been published. So we selectively add restored ShortcutPackages here. // // The same logic applies to launchers, but since launchers shouldn't pin shortcuts // without users interaction it's really not a big deal, so we just clear existing // ShortcutLauncher instances in mLaunchers and add all the restored ones here. mLaunchers.clear(); restored.forAllLaunchers(sl -> { // If the app is already installed and allowbackup = false, then ignore the restored // data. if (s.isPackageInstalled(sl.getPackageName(), getUserId()) && !s.shouldBackupApp(sl.getPackageName(), getUserId())) { return; } addLauncher(sl); }); restored.forAllPackages(sp -> { // If the app is already installed and allowbackup = false, then ignore the restored // data. if (s.isPackageInstalled(sp.getPackageName(), getUserId()) && !s.shouldBackupApp(sp.getPackageName(), getUserId())) { return; } final ShortcutPackage previous = getPackageShortcutsIfExists(sp.getPackageName()); if (previous != null && previous.hasNonManifestShortcuts()) { Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored." + " Existing non-manifeset shortcuts will be overwritten."); } addPackage(sp); }); // Empty the launchers and packages in restored to avoid accidentally using them. restored.mLaunchers.clear(); restored.mPackages.clear(); } public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { pw.print(prefix); pw.print("User: "); Loading
services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +30 −0 Original line number Diff line number Diff line Loading @@ -5359,6 +5359,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * It's the case with preintalled apps -- when applyRestore() is called, the system * apps are already installed, so manifest shortcuts need to be re-published. * * Also, when a restore target app is already installed, and * - if it has allowBackup=true, we'll restore normally, so all existing shortcuts will be * replaced. (but manifest shortcuts will be re-published anyway.) We log a warning on * logcat. * - if it has allowBackup=false, we don't touch any of the existing shortcuts. */ public void testBackupAndRestore_appAlreadyInstalledWhenRestored() { // Pre-backup. Same as testBackupAndRestore_manifestRePublished(). Loading Loading @@ -5390,6 +5396,19 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mService.mPackageMonitor.onReceive(mServiceContext, genPackageAddIntent(CALLING_PACKAGE_1, USER_0)); // Set up shortcuts for package 3, which won't be backed up / restored. addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()), R.xml.shortcut_1); updatePackageVersion(CALLING_PACKAGE_3, 1); mService.mPackageMonitor.onReceive(mServiceContext, genPackageAddIntent(CALLING_PACKAGE_3, USER_0)); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertTrue(getManager().setDynamicShortcuts(list( makeShortcut("s1")))); }); // Make sure the manifest shortcuts have been published. runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { assertWith(getCallerShortcuts()) Loading @@ -5415,6 +5434,11 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .areAllDisabled(); }); runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertWith(getCallerShortcuts()) .haveIds("s1", "ms1"); }); // Backup and *without restarting the service, just call applyRestore()*. { int prevUid = mInjectedCallingUid; Loading Loading @@ -5454,6 +5478,12 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .areAllNotDynamic() ; }); // Package 3 still has the same shortcuts. runWithCaller(CALLING_PACKAGE_3, USER_0, () -> { assertWith(getCallerShortcuts()) .haveIds("s1", "ms1"); }); } public void testSaveAndLoad_crossProfile() { Loading