From 3366328245621fa6e8fd764a4d3c2f073774d096 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 22 Aug 2016 16:19:04 -0700 Subject: [PATCH] Do not scan system apps unless after OTA Bug 30956231 Change-Id: I4af34cdd39cfaf0b8712b9ded1ef119efa3e04b0 --- .../com/android/server/pm/ShortcutPackage.java | 6 + .../com/android/server/pm/ShortcutService.java | 19 +++- .../java/com/android/server/pm/ShortcutUser.java | 18 ++- .../android/server/pm/BaseShortcutManagerTest.java | 12 ++ .../android/server/pm/ShortcutManagerTest1.java | 122 +++++++++++++++++++++ 5 files changed, 173 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 827b88a5b61a..6f6fd7c88dcd 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -667,6 +667,12 @@ class ShortcutPackage extends ShortcutPackageItem { // - version code hasn't change // - lastUpdateTime hasn't change // - all target activities are still enabled. + + // Note, system apps timestamps do *not* change after OTAs. (But they do + // after an adb sync or a local flash.) + // This means if a system app's version code doesn't change on an OTA, + // we don't notice it's updated. But that's fine since their version code *should* + // really change on OTAs. if ((getPackageInfo().getVersionCode() == pi.versionCode) && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime) && areAllActivitiesStillEnabled()) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index b80775b690e2..adf19dc8295e 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -54,6 +54,7 @@ import android.graphics.RectF; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -2659,10 +2660,14 @@ public class ShortcutService extends IShortcutService.Stub { boolean forceRescan) { final ShortcutUser user = getUserShortcutsLocked(userId); + // Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime + // is not reliable. final long now = injectCurrentTimeMillis(); + final boolean afterOta = + !injectBuildFingerprint().equals(user.getLastAppScanOsFingerprint()); // Then for each installed app, publish manifest shortcuts when needed. - forUpdatedPackages(userId, lastScanTime, ai -> { + forUpdatedPackages(userId, lastScanTime, afterOta, ai -> { user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId); user.rescanPackageIfNeeded(ai.packageName, forceRescan); }); @@ -2670,6 +2675,7 @@ public class ShortcutService extends IShortcutService.Stub { // Write the time just before the scan, because there may be apps that have just // been updated, and we want to catch them in the next time. user.setLastAppScanTime(now); + user.setLastAppScanOsFingerprint(injectBuildFingerprint()); scheduleSaveUser(userId); } @@ -2908,7 +2914,7 @@ public class ShortcutService extends IShortcutService.Stub { return parceledList.getList(); } - private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, + private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta, Consumer callback) { if (DEBUG) { Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime); @@ -2920,7 +2926,8 @@ public class ShortcutService extends IShortcutService.Stub { // If the package has been updated since the last scan time, then scan it. // Also if it's a system app with no update, lastUpdateTime is not reliable, so // just scan it. - if (pi.lastUpdateTime >= lastScanTime || isPureSystemApp(pi.applicationInfo)) { + if (pi.lastUpdateTime >= lastScanTime + || (afterOta && isPureSystemApp(pi.applicationInfo))) { if (DEBUG) { Slog.d(TAG, "Found updated package " + pi.packageName); } @@ -3598,6 +3605,12 @@ public class ShortcutService extends IShortcutService.Stub { Binder.restoreCallingIdentity(token); } + // Injection point. + @VisibleForTesting + String injectBuildFingerprint() { + return Build.FINGERPRINT; + } + final void wtf(String message) { wtf(message, /* exception= */ null); } diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index ce3ed9c7deef..c05c66fedbf8 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -60,6 +60,7 @@ class ShortcutUser { // Suffix "2" was added to force rescan all packages after the next OTA. private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time2"; + private static final String ATTR_LAST_APP_SCAN_OS_FINGERPRINT = "last-app-scan-fp"; private static final String KEY_USER_ID = "userId"; private static final String KEY_LAUNCHERS = "launchers"; private static final String KEY_PACKAGES = "packages"; @@ -125,6 +126,8 @@ class ShortcutUser { private long mLastAppScanTime; + private String mLastAppScanOsFingerprint; + public ShortcutUser(ShortcutService service, int userId) { mService = service; mUserId = userId; @@ -142,6 +145,14 @@ class ShortcutUser { mLastAppScanTime = lastAppScanTime; } + public String getLastAppScanOsFingerprint() { + return mLastAppScanOsFingerprint; + } + + public void setLastAppScanOsFingerprint(String lastAppScanOsFingerprint) { + mLastAppScanOsFingerprint = lastAppScanOsFingerprint; + } + // We don't expose this directly to non-test code because only ShortcutUser should add to/ // remove from it. @VisibleForTesting @@ -318,6 +329,8 @@ class ShortcutUser { ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales); ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, mLastAppScanTime); + ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_OS_FINGERPRINT, + mLastAppScanOsFingerprint); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); @@ -364,7 +377,8 @@ class ShortcutUser { ATTR_LAST_APP_SCAN_TIME); final long currentTime = s.injectCurrentTimeMillis(); ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0; - + ret.mLastAppScanOsFingerprint = ShortcutService.parseStringAttribute(parser, + ATTR_LAST_APP_SCAN_OS_FINGERPRINT); final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -457,6 +471,8 @@ class ShortcutUser { pw.print(mLastAppScanTime); pw.print("] "); pw.print(ShortcutService.formatTime(mLastAppScanTime)); + pw.print(" Last app scan FP: "); + pw.print(mLastAppScanOsFingerprint); pw.println(); prefix += prefix + " "; diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index e96e97bcaa02..792f3001c049 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -407,6 +407,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override + String injectBuildFingerprint() { + return mInjectedBuildFingerprint; + } + + @Override void wtf(String message, Throwable th) { // During tests, WTF is fatal. fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th)); @@ -528,6 +533,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected Map mInjectedPackages; protected Set mUninstalledPackages; + protected Set mSystemPackages; protected PackageManager mMockPackageManager; protected PackageManagerInternal mMockPackageManagerInternal; @@ -628,6 +634,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback"; protected static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999; + protected String mInjectedBuildFingerprint = "build1"; + static { QUERY_ALL.setQueryFlags( ShortcutQuery.FLAG_GET_ALL_KINDS); @@ -677,6 +685,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP); mUninstalledPackages = new HashSet<>(); + mSystemPackages = new HashSet<>(); mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files"); @@ -963,6 +972,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) { ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; } + if (mSystemPackages.contains(packageName)) { + ret.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + } if (getSignatures) { ret.signatures = pi.signatures; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index ed4e391a6273..46ac7ce88b90 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -4347,6 +4347,128 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } + public void testHandlePackageUpdate_systemAppUpdate() { + + // Package1 is a system app. Package 2 is not a system app, so it's not scanned + // in this test at all. + mSystemPackages.add(CALLING_PACKAGE_1); + + // Initial state: no shortcuts. + mService.checkPackageChanges(USER_0); + + assertEquals(mInjectedCurrentTimeMillis, + mService.getUserShortcutsLocked(USER_0).getLastAppScanTime()); + assertEquals(mInjectedBuildFingerprint, + mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint()); + + // They have no shortcuts. + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Next. + // Update the packages -- now they have 1 manifest shortcut. + // But checkPackageChanges() don't notice it, since their version code / timestamp haven't + // changed. + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_1); + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()), + R.xml.shortcut_1); + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Next. + // Update the build finger print. All system apps will be scanned now. + mInjectedBuildFingerprint = "update1"; + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Next. + // Update manifest shortcuts. + mInjectedBuildFingerprint = "update2"; + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + // Fingerprint hasn't changed, so CALLING_PACKAGE_1 wasn't scanned. + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Update the fingerprint, but CALLING_PACKAGE_1's version code hasn't changed, so + // still not scanned. + mInjectedBuildFingerprint = "update2"; + mInjectedCurrentTimeMillis += 1000; + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Now update the version code, so CALLING_PACKAGE_1 is scanned again. + mInjectedBuildFingerprint = "update3"; + mInjectedCurrentTimeMillis += 1000; + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.checkPackageChanges(USER_0); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2"); + }); + runWithCaller(CALLING_PACKAGE_2, USER_0, () -> { + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + // Make sure getLastAppScanTime / getLastAppScanOsFingerprint are persisted. + initService(); + assertEquals(mInjectedCurrentTimeMillis, + mService.getUserShortcutsLocked(USER_0).getLastAppScanTime()); + assertEquals(mInjectedBuildFingerprint, + mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint()); + } + public void testHandlePackageChanged() { final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1"); final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2"); -- 2.11.0