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

Commit 8612606e authored by Pinyao Ting's avatar Pinyao Ting
Browse files

Allow shortcuts to restore in any version

Currently when the backup payload is created on the source device, it
captures also the version number of the app, and invalidates the
shortcuts when they are restored on the target device if the version
of the app is older than source device.

This normally wouldn't be a problem since Play service would download
the latest version of the app on the target device during a restore,
which implies the version number will never be smaller than the one on
the source device. Unfortunately this is not true for apps that are
pre-installed on the device.

This CL simply drops the version check since it isn't a very useful
check and it's causing problems for pre-installed apps which are very
visible to the user.

Bug: 317883241
Test: atest ShortcutManagerTest1
Change-Id: Iac646661c80952eecf77b36243d7d0be337e2d5e
parent ea9f3aee
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -240,7 +240,7 @@ class ShortcutPackage extends ShortcutPackageItem {

    @Override
    protected boolean canRestoreAnyVersion() {
        return false;
        return true;
    }

    @Override
+1 −258
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.pm;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_NOT_DISABLED;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_VERSION_LOWER;

import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyOrNull;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyStringOrNull;
@@ -78,7 +77,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
@@ -4844,7 +4842,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
        doReturn(expected != DISABLED_REASON_SIGNATURE_MISMATCH).when(
                mMockPackageManagerInternal).isDataRestoreSafe(any(byte[].class), anyString());

        assertEquals(expected, spi.canRestoreTo(mService, pi, anyVersionOk));
        assertEquals(expected, spi.canRestoreTo(mService, pi, true));
    }

    public void testCanRestoreTo() {
@@ -4872,7 +4870,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x");
        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x", "y");
        checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x");
        checkCanRestoreTo(DISABLED_REASON_VERSION_LOWER, spi1, false, 9, true, "sig1");

        // Any version okay.
        checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, true, 9, true, "sig1");
@@ -5983,14 +5980,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
        });
    }

    public void testBackupAndRestore_publisherLowerVersion() {
        prepareForBackupTest();

        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version

        checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_VERSION_LOWER);
    }

    public void testBackupAndRestore_publisherWrongSignature() {
        prepareForBackupTest();

@@ -6626,252 +6615,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
        });
    }


    /**
     * Restored to a lower version with no manifest shortcuts. All shortcuts are now invisible,
     * and all calls from the publisher should ignore them.
     */
    public void testBackupAndRestore_disabledShortcutsAreIgnored() {
        // Publish two manifest shortcuts.
        addManifestShortcutResource(
                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                R.xml.shortcut_5_altalt);
        updatePackageVersion(CALLING_PACKAGE_1, 1);
        mService.mPackageMonitor.onReceive(mServiceContext,
                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));

        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(
                    makeShortcutWithShortLabel("s1", "original-title"),
                    makeShortcut("s2"), makeShortcut("s3"))));
        });

        // Pin from launcher 1.
        runWithCaller(LAUNCHER_1, USER_0, () -> {
            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
                    list("ms1", "ms2", "ms3", "ms4", "s1", "s2"), HANDLE_USER_0);
        });

        doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(
                any(byte[].class), anyString());

        backupAndRestore();

        // Lower the version and remove the manifest shortcuts.
        addManifestShortcutResource(
                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                R.xml.shortcut_0);
        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version

        // When re-installing the app, the manifest shortcut should be re-published.
        mService.mPackageMonitor.onReceive(mServiceContext,
                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
        mService.mPackageMonitor.onReceive(mServiceContext,
                genPackageAddIntent(LAUNCHER_1, USER_0));

        // No shortcuts should be visible to the publisher.
        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
            assertWith(getCallerVisibleShortcuts())
                    .isEmpty();
        });

        final Runnable checkAllDisabledForLauncher = () -> {
            runWithCaller(LAUNCHER_1, USER_0, () -> {
                assertWith(getShortcutAsLauncher(USER_0))
                        .areAllPinned()
                        .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
                        .areAllDisabled()
                        .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)

                        .forShortcutWithId("s1", si -> {
                            assertEquals("original-title", si.getShortLabel());
                        })
                        .forShortcutWithId("ms1", si -> {
                            assertEquals("string-com.android.test.1-user:0-res:"
                                            + R.string.shortcut_title1 + "/en"
                                    , si.getShortLabel());
                        })
                        .forShortcutWithId("ms2", si -> {
                            assertEquals("string-com.android.test.1-user:0-res:"
                                            + R.string.shortcut_title2 + "/en"
                                    , si.getShortLabel());
                        })
                        .forShortcutWithId("ms3", si -> {
                            assertEquals("string-com.android.test.1-user:0-res:"
                                            + R.string.shortcut_title1 + "/en"
                                    , si.getShortLabel());
                            assertEquals("string-com.android.test.1-user:0-res:"
                                            + R.string.shortcut_title2 + "/en"
                                    , si.getLongLabel());
                        })
                        .forShortcutWithId("ms4", si -> {
                            assertEquals("string-com.android.test.1-user:0-res:"
                                            + R.string.shortcut_title2 + "/en"
                                    , si.getShortLabel());
                            assertEquals("string-com.android.test.1-user:0-res:"
                                            + R.string.shortcut_title2 + "/en"
                                    , si.getLongLabel());
                        });
            });
        };

        checkAllDisabledForLauncher.run();

        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {

            makeCallerForeground(); // CALLING_PACKAGE_1 is now in the foreground.

            // All changing API calls should be ignored.

            getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
            checkAllDisabledForLauncher.run();

            getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
            checkAllDisabledForLauncher.run();

            getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
            checkAllDisabledForLauncher.run();

            getManager().removeAllDynamicShortcuts();
            getManager().removeDynamicShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
            checkAllDisabledForLauncher.run();

            getManager().updateShortcuts(list(makeShortcutWithShortLabel("s1", "new-title")));
            checkAllDisabledForLauncher.run();


            // Add a shortcut -- even though ms1 was immutable, it will succeed.
            assertTrue(getManager().addDynamicShortcuts(list(
                    makeShortcutWithShortLabel("ms1", "original-title"))));

            runWithCaller(LAUNCHER_1, USER_0, () -> {
                assertWith(getShortcutAsLauncher(USER_0))
                        .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")

                        .selectByIds("ms1")
                        .areAllEnabled()
                        .areAllDynamic()
                        .areAllPinned()
                        .forAllShortcuts(si -> {
                            assertEquals("original-title", si.getShortLabel());
                        })

                        // The rest still exist and disabled.
                        .revertToOriginalList()
                        .selectByIds("ms2", "ms3", "ms4", "s1", "s2")
                        .areAllDisabled()
                        .areAllPinned()
                ;
            });

            assertTrue(getManager().setDynamicShortcuts(list(
                    makeShortcutWithShortLabel("ms2", "new-title-2"))));

            runWithCaller(LAUNCHER_1, USER_0, () -> {
                assertWith(getShortcutAsLauncher(USER_0))
                        .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")

                        .selectByIds("ms1")
                        .areAllEnabled()
                        .areAllNotDynamic() // ms1 was not in the list, so no longer dynamic.
                        .areAllPinned()
                        .areAllMutable()
                        .forAllShortcuts(si -> {
                            assertEquals("original-title", si.getShortLabel());
                        })

                        .revertToOriginalList()
                        .selectByIds("ms2")
                        .areAllEnabled()
                        .areAllDynamic()
                        .areAllPinned()
                        .areAllMutable()
                        .forAllShortcuts(si -> {
                            assertEquals("new-title-2", si.getShortLabel());
                        })

                        // The rest still exist and disabled.
                        .revertToOriginalList()
                        .selectByIds("ms3", "ms4", "s1", "s2")
                        .areAllDisabled()
                        .areAllPinned()
                ;
            });

            // Prepare for requestPinShortcut().
            setDefaultLauncher(USER_0, LAUNCHER_1);
            mPinConfirmActivityFetcher = (packageName, userId) ->
                    new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);

            mManager.requestPinShortcut(
                    makeShortcutWithShortLabel("ms3", "new-title-3"),
                    /*PendingIntent=*/ null);

            // Note this was pinned, so it'll be accepted right away.
            runWithCaller(LAUNCHER_1, USER_0, () -> {
                assertWith(getShortcutAsLauncher(USER_0))
                        .selectByIds("ms3")
                        .areAllEnabled()
                        .areAllNotDynamic()
                        .areAllPinned()
                        .areAllMutable()
                        .forAllShortcuts(si -> {
                            assertEquals("new-title-3", si.getShortLabel());
                            // The new one replaces the old manifest shortcut, so the long label
                            // should be gone now.
                            assertNull(si.getLongLabel());
                        });
            });

            // Now, change the launcher to launcher2, and request pin again.
            setDefaultLauncher(USER_0, LAUNCHER_2);

            reset(mServiceContext);

            assertTrue(mManager.isRequestPinShortcutSupported());
            mManager.requestPinShortcut(
                    makeShortcutWithShortLabel("ms4", "new-title-4"),
                    /*PendingIntent=*/ null);

            // Initially there should be no pinned shortcuts for L2.
            runWithCaller(LAUNCHER_2, USER_0, () -> {
                assertWith(getShortcutAsLauncher(USER_0))
                        .selectPinned()
                        .isEmpty();

                final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);

                verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));

                assertEquals(LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT,
                        intent.getValue().getAction());
                assertEquals(LAUNCHER_2, intent.getValue().getComponent().getPackageName());

                // Check the request object.
                final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());

                assertNotNull(request);
                assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType());

                assertWith(request.getShortcutInfo())
                        .haveIds("ms4")
                        .areAllOrphan()
                        .forAllShortcuts(si -> {
                            assertEquals("new-title-4", si.getShortLabel());
                            // The new one replaces the old manifest shortcut, so the long label
                            // should be gone now.
                            assertNull(si.getLongLabel());
                        });
                assertTrue(request.accept());

                assertWith(getShortcutAsLauncher(USER_0))
                        .selectPinned()
                        .haveIds("ms4")
                        .areAllEnabled();
            });
        });
    }

    /**
     * Test for restoring the pre-P backup format.
     */