Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7497b700 authored by Makoto Onuki's avatar Makoto Onuki Committed by android-build-merger
Browse files

Merge "When app is updated, save the new version code, and update shortcuts...

Merge "When app is updated, save the new version code, and update shortcuts with resource based icons." into nyc-dev
am: 711ffe62

* commit '711ffe62':
  When app is updated, save the new version code, and update shortcuts with resource based icons.

Change-Id: I1b8ab5b9ede7d08f7b99f34cb54dda277b01c859
parents e50413b1 711ffe62
Loading
Loading
Loading
Loading
+64 −3
Original line number Diff line number Diff line
@@ -109,6 +109,27 @@ class ShortcutPackage extends ShortcutPackageItem {
        return getPackageUserId();
    }

    /**
     * Called when a shortcut is about to be published.  At this point we know the publisher package
     * exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
     * we do some initialization for the package.
     */
    private void onShortcutPublish(ShortcutService s) {
        // Make sure we have the version code for the app.  We need the version code in
        // handlePackageUpdated().
        if (getPackageInfo().getVersionCode() < 0) {
            final int versionCode = s.getApplicationVersionCode(getPackageName(), getOwnerUserId());
            if (ShortcutService.DEBUG) {
                Slog.d(TAG, String.format("Package %s version = %d", getPackageName(),
                        versionCode));
            }
            if (versionCode >= 0) {
                getPackageInfo().setVersionCode(versionCode);
                s.scheduleSaveUser(getOwnerUserId());
            }
        }
    }

    @Override
    protected void onRestoreBlocked(ShortcutService s) {
        // Can't restore due to version/signature mismatch.  Remove all shortcuts.
@@ -153,6 +174,9 @@ class ShortcutPackage extends ShortcutPackageItem {
     */
    public void addDynamicShortcut(@NonNull ShortcutService s,
            @NonNull ShortcutInfo newShortcut) {

        onShortcutPublish(s);

        newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);

        final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
@@ -387,6 +411,40 @@ class ShortcutPackage extends ShortcutPackageItem {
        mApiCallCount = 0;
    }

    /**
     * Called when the package is updated.  If there are shortcuts with resource icons, update
     * their timestamps.
     */
    public void handlePackageUpdated(ShortcutService s, int newVersionCode) {
        if (getPackageInfo().getVersionCode() >= newVersionCode) {
            // Version hasn't changed; nothing to do.
            return;
        }
        if (ShortcutService.DEBUG) {
            Slog.d(TAG, String.format("Package %s updated, version %d -> %d", getPackageName(),
                    getPackageInfo().getVersionCode(), newVersionCode));
        }

        getPackageInfo().setVersionCode(newVersionCode);

        boolean changed = false;
        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
            final ShortcutInfo si = mShortcuts.valueAt(i);

            if (si.hasIconResource()) {
                changed = true;
                si.setTimestamp(s.injectCurrentTimeMillis());
            }
        }
        if (changed) {
            // This will send a notification to the launcher, and also save .
            s.packageShortcutsChanged(getPackageName(), getPackageUserId());
        } else {
            // Still save the version code.
            s.scheduleSaveUser(getPackageUserId());
        }
    }

    public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
        pw.println();

@@ -413,16 +471,19 @@ class ShortcutPackage extends ShortcutPackageItem {
        getPackageInfo().dump(s, pw, prefix + "  ");
        pw.println();

        pw.print(prefix);
        pw.println("  Shortcuts:");
        long totalBitmapSize = 0;
        final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
        final int size = shortcuts.size();
        for (int i = 0; i < size; i++) {
            final ShortcutInfo si = shortcuts.valueAt(i);
            pw.print(prefix);
            pw.print("    ");
            pw.println(si.toInsecureString());
            if (si.getBitmapPath() != null) {
                final long len = new File(si.getBitmapPath()).length();
                pw.print(prefix);
                pw.print("      ");
                pw.print("bitmap size=");
                pw.println(len);
+8 −2
Original line number Diff line number Diff line
@@ -45,12 +45,14 @@ class ShortcutPackageInfo {
    private static final String TAG_SIGNATURE = "signature";
    private static final String ATTR_SIGNATURE_HASH = "hash";

    private static final int VERSION_UNKNOWN = -1;

    /**
     * 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 int mVersionCode = VERSION_UNKNOWN;
    private ArrayList<byte[]> mSigHashes;

    private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
@@ -60,7 +62,7 @@ class ShortcutPackageInfo {
    }

    public static ShortcutPackageInfo newEmpty() {
        return new ShortcutPackageInfo(0, new ArrayList<>(0), /* isShadow */ false);
        return new ShortcutPackageInfo(VERSION_UNKNOWN, new ArrayList<>(0), /* isShadow */ false);
    }

    public boolean isShadow() {
@@ -75,6 +77,10 @@ class ShortcutPackageInfo {
        return mVersionCode;
    }

    public void setVersionCode(int versionCode) {
        mVersionCode = versionCode;
    }

    public boolean hasSignatures() {
        return mSigHashes.size() > 0;
    }
+44 −16
Original line number Diff line number Diff line
@@ -356,7 +356,7 @@ public class ShortcutService extends IShortcutService.Stub {
            // Preload
            getUserShortcutsLocked(userId);

            cleanupGonePackages(userId);
            checkPackageChanges(userId);
        }
    }

@@ -1158,7 +1158,11 @@ public class ShortcutService extends IShortcutService.Stub {
     * - Sends a notification to LauncherApps
     * - Write to file
     */
    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
    void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) {
        if (DEBUG) {
            Slog.d(TAG, String.format(
                    "Shortcut changes: package=%s, user=%d", packageName, userId));
        }
        notifyListeners(packageName, userId);
        scheduleSaveUser(userId);
    }
@@ -1284,7 +1288,7 @@ public class ShortcutService extends IShortcutService.Stub {
                ps.addDynamicShortcut(this, newShortcut);
            }
        }
        userPackageChanged(packageName, userId);
        packageShortcutsChanged(packageName, userId);
        return true;
    }

@@ -1323,7 +1327,7 @@ public class ShortcutService extends IShortcutService.Stub {
                }
            }
        }
        userPackageChanged(packageName, userId);
        packageShortcutsChanged(packageName, userId);

        return true;
    }
@@ -1353,7 +1357,7 @@ public class ShortcutService extends IShortcutService.Stub {
                ps.addDynamicShortcut(this, newShortcut);
            }
        }
        userPackageChanged(packageName, userId);
        packageShortcutsChanged(packageName, userId);

        return true;
    }
@@ -1370,7 +1374,7 @@ public class ShortcutService extends IShortcutService.Stub {
                        Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
            }
        }
        userPackageChanged(packageName, userId);
        packageShortcutsChanged(packageName, userId);
    }

    @Override
@@ -1380,7 +1384,7 @@ public class ShortcutService extends IShortcutService.Stub {
        synchronized (mLock) {
            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
        }
        userPackageChanged(packageName, userId);
        packageShortcutsChanged(packageName, userId);
    }

    @Override
@@ -1729,7 +1733,7 @@ public class ShortcutService extends IShortcutService.Stub {
                launcher.pinShortcuts(
                        ShortcutService.this, userId, packageName, shortcutIds);
            }
            userPackageChanged(packageName, userId);
            packageShortcutsChanged(packageName, userId);
        }

        @Override
@@ -1841,13 +1845,15 @@ public class ShortcutService extends IShortcutService.Stub {
    };

    /**
     * Called when a user is unlocked.  Check all known packages still exist, and otherwise
     * perform cleanup.
     * Called when a user is unlocked.
     * - Check all known packages still exist, and otherwise perform cleanup.
     * - If a package still exists, check the version code.  If it's been updated, may need to
     *   update timestamps of its shortcuts.
     */
    @VisibleForTesting
    void cleanupGonePackages(@UserIdInt int ownerUserId) {
    void checkPackageChanges(@UserIdInt int ownerUserId) {
        if (DEBUG) {
            Slog.d(TAG, "cleanupGonePackages() ownerUserId=" + ownerUserId);
            Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
        }
        final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();

@@ -1858,10 +1864,15 @@ public class ShortcutService extends IShortcutService.Stub {
                if (spi.getPackageInfo().isShadow()) {
                    return; // Don't delete shadow information.
                }
                if (isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
                    return; // Package not gone.
                }
                final int versionCode = getApplicationVersionCode(
                        spi.getPackageName(), spi.getPackageUserId());
                if (versionCode >= 0) {
                    // Package still installed, see if it's updated.
                    getUserShortcutsLocked(ownerUserId).handlePackageUpdated(
                            this, spi.getPackageName(), versionCode);
                } else {
                    gonePackages.add(PackageWithUser.of(spi));
                }
            });
            if (gonePackages.size() > 0) {
                for (int i = gonePackages.size() - 1; i >= 0; i--) {
@@ -1890,6 +1901,12 @@ public class ShortcutService extends IShortcutService.Stub {
        synchronized (mLock) {
            forEachLoadedUserLocked(user ->
                    user.attemptToRestoreIfNeededAndSave(this, packageName, userId));

            final int versionCode = getApplicationVersionCode(packageName, userId);
            if (versionCode < 0) {
                return; // shouldn't happen
            }
            getUserShortcutsLocked(userId).handlePackageUpdated(this, packageName, versionCode);
        }
    }

@@ -1978,6 +1995,17 @@ public class ShortcutService extends IShortcutService.Stub {
        return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
    }

    /**
     * @return the version code of the package, or -1 if the app is not installed.
     */
    int getApplicationVersionCode(String packageName, int userId) {
        final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
        if ((ai == null) || ((ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
            return -1;
        }
        return ai.versionCode;
    }

    // === Backup & restore ===

    boolean shouldBackupApp(String packageName, int userId) {
+11 −0
Original line number Diff line number Diff line
@@ -176,6 +176,17 @@ class ShortcutUser {
        });
    }

    /**
     * Called when a package is updated.
     */
    public void handlePackageUpdated(ShortcutService s, @NonNull String packageName,
            int newVersionCode) {
        if (!mPackages.containsKey(packageName)) {
            return;
        }
        getPackageShortcuts(s, packageName).handlePackageUpdated(s, newVersionCode);
    }

    public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName,
            @UserIdInt int packageUserId) {
        forPackageItem(packageName, packageUserId, spi -> {
+200 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicOnly;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertShortcutIds;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.findShortcut;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.hashSet;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makeBundle;
@@ -644,6 +645,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
        pi.applicationInfo.flags = ApplicationInfo.FLAG_INSTALLED
                | ApplicationInfo.FLAG_ALLOW_BACKUP;
        pi.versionCode = version;
        pi.applicationInfo.versionCode = version;
        pi.signatures = genSignatures(signatures);

        return pi;
@@ -657,6 +659,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
        c.accept(mInjectedPackages.get(packageName));
    }

    private void updatePackageVersion(String packageName, int increment) {
        updatePackageInfo(packageName, pi -> {
            pi.versionCode += increment;
            pi.applicationInfo.versionCode += increment;
        });
    }

    private void uninstallPackage(int userId, String packageName) {
        if (ENABLE_DUMP) {
            Log.i(TAG, "Unnstall package " + packageName + " / " + userId);
@@ -3839,7 +3848,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {

        // Start uninstalling.
        uninstallPackage(USER_10, LAUNCHER_1);
        mService.cleanupGonePackages(USER_10);
        mService.checkPackageChanges(USER_10);

        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
@@ -3859,7 +3868,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {

        // Uninstall.
        uninstallPackage(USER_10, CALLING_PACKAGE_1);
        mService.cleanupGonePackages(USER_10);
        mService.checkPackageChanges(USER_10);

        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
@@ -3878,7 +3887,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
        assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));

        uninstallPackage(USER_P0, LAUNCHER_1);
        mService.cleanupGonePackages(USER_0);
        mService.checkPackageChanges(USER_0);

        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
@@ -3896,7 +3905,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
        assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
        assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));

        mService.cleanupGonePackages(USER_P0);
        mService.checkPackageChanges(USER_P0);
        
        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
@@ -4150,6 +4159,193 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
    }

    public void testHandlePackageUpdate() throws Throwable {

        // Set up shortcuts and launchers.

        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                getTestContext().getResources(), R.drawable.black_32x32));

        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(
                    makeShortcut("s1"),
                    makeShortcutWithIcon("s2", res32x32),
                    makeShortcutWithIcon("s3", res32x32),
                    makeShortcutWithIcon("s4", bmp32x32))));
        });
        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(
                    makeShortcut("s1"),
                    makeShortcutWithIcon("s2", bmp32x32))));
        });
        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(
                    makeShortcutWithIcon("s1", res32x32))));
        });

        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(
                    makeShortcutWithIcon("s1", res32x32),
                    makeShortcutWithIcon("s2", res32x32))));
        });
        runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(
                    makeShortcutWithIcon("s1", bmp32x32),
                    makeShortcutWithIcon("s2", bmp32x32))));
        });

        LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
        LauncherApps.Callback c10 = mock(LauncherApps.Callback.class);

        runWithCaller(LAUNCHER_1, USER_0, () -> {
            mLauncherApps.registerCallback(c0, new Handler(Looper.getMainLooper()));
        });
        runWithCaller(LAUNCHER_1, USER_10, () -> {
            mLauncherApps.registerCallback(c10, new Handler(Looper.getMainLooper()));
        });

        mInjectedCurrentTimeLillis = START_TIME + 100;

        ArgumentCaptor<List> shortcuts;

        // First, call the event without updating the versions.
        reset(c0);
        reset(c10);

        mService.mPackageMonitor.onReceive(getTestContext(),
                genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
        mService.mPackageMonitor.onReceive(getTestContext(),
                genPackageUpdateIntent(CALLING_PACKAGE_1, USER_10));

        waitOnMainThread();

        // Version not changed, so no callback.
        verify(c0, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                any(List.class),
                any(UserHandle.class));
        verify(c10, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                any(List.class),
                any(UserHandle.class));

        // Next, update the version info for package 1.
        reset(c0);
        reset(c10);
        updatePackageVersion(CALLING_PACKAGE_1, 1);

        // Then send the broadcast, to only user-0.
        mService.mPackageMonitor.onReceive(getTestContext(),
                genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));

        waitOnMainThread();

        // User-0 should get the notification.
        shortcuts = ArgumentCaptor.forClass(List.class);
        verify(c0).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                shortcuts.capture(),
                eq(HANDLE_USER_0));

        // User-10 shouldn't yet get the notification.
        verify(c10, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                any(List.class),
                any(UserHandle.class));
        assertShortcutIds(shortcuts.getValue(), "s1", "s2", "s3", "s4");
        assertEquals(START_TIME,
                findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
        assertEquals(START_TIME + 100,
                findShortcut(shortcuts.getValue(), "s2").getLastChangedTimestamp());
        assertEquals(START_TIME + 100,
                findShortcut(shortcuts.getValue(), "s3").getLastChangedTimestamp());
        assertEquals(START_TIME,
                findShortcut(shortcuts.getValue(), "s4").getLastChangedTimestamp());

        // Next, send unlock even on user-10.  Now we scan packages on this user and send a
        // notification to the launcher.
        mInjectedCurrentTimeLillis = START_TIME + 200;

        when(mMockUserManager.isUserRunning(eq(USER_10))).thenReturn(true);

        reset(c0);
        reset(c10);
        mService.handleUnlockUser(USER_10);

        shortcuts = ArgumentCaptor.forClass(List.class);
        verify(c0, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                any(List.class),
                any(UserHandle.class));

        verify(c10).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                shortcuts.capture(),
                eq(HANDLE_USER_10));

        assertShortcutIds(shortcuts.getValue(), "s1", "s2");
        assertEquals(START_TIME + 200,
                findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
        assertEquals(START_TIME + 200,
                findShortcut(shortcuts.getValue(), "s2").getLastChangedTimestamp());


        // Do the same thing for package 2, which doesn't have resource icons.
        mInjectedCurrentTimeLillis = START_TIME + 300;

        reset(c0);
        reset(c10);
        updatePackageVersion(CALLING_PACKAGE_2, 10);

        // Then send the broadcast, to only user-0.
        mService.mPackageMonitor.onReceive(getTestContext(),
                genPackageUpdateIntent(CALLING_PACKAGE_2, USER_0));
        mService.handleUnlockUser(USER_10);

        waitOnMainThread();

        verify(c0, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                any(List.class),
                any(UserHandle.class));

        verify(c10, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_1),
                any(List.class),
                any(UserHandle.class));

        // Do the same thing for package 3
        mInjectedCurrentTimeLillis = START_TIME + 400;

        reset(c0);
        reset(c10);
        updatePackageVersion(CALLING_PACKAGE_3, 100);

        // Then send the broadcast, to only user-0.
        mService.mPackageMonitor.onReceive(getTestContext(),
                genPackageUpdateIntent(CALLING_PACKAGE_3, USER_0));
        mService.handleUnlockUser(USER_10);

        waitOnMainThread();

        shortcuts = ArgumentCaptor.forClass(List.class);
        verify(c0).onShortcutsChanged(
                eq(CALLING_PACKAGE_3),
                shortcuts.capture(),
                eq(HANDLE_USER_0));

        // User 10 doesn't have package 3, so no callback.
        verify(c10, times(0)).onShortcutsChanged(
                eq(CALLING_PACKAGE_3),
                any(List.class),
                any(UserHandle.class));

        assertShortcutIds(shortcuts.getValue(), "s1");
        assertEquals(START_TIME + 400,
                findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
    }

    private void backupAndRestore() {
        int prevUid = mInjectedCallingUid;

Loading