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

Commit db2b8f9f authored by Josh Hou's avatar Josh Hou
Browse files

[Panlingual] Restore per-app locales for an archived application

Support the data restoration for an archived application

Bug: 335704775
Test: Unit test cases, CTS, and manual testing
Change-Id: I167a87849b7fbecb4d21f5f9bfc672fbbb44f006
parent 5c7b4325
Loading
Loading
Loading
Loading
+41 −13
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.HandlerThread;
import android.os.LocaleList;
@@ -101,6 +102,11 @@ class LocaleManagerBackupHelper {
    // the application setting the app-locale itself.
    private final SharedPreferences mDelegateAppLocalePackages;
    private final BroadcastReceiver mUserMonitor;
    // To determine whether an app is pre-archived, check for Intent.EXTRA_ARCHIVAL upon receiving
    // the initial PACKAGE_ADDED broadcast. If it is indeed pre-archived, perform the data
    // restoration during the second PACKAGE_ADDED broadcast, which is sent subsequently when the
    // app is installed.
    private final Set<String> mPkgsToRestore;

    LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
            PackageManager packageManager, HandlerThread broadcastHandlerThread) {
@@ -119,6 +125,7 @@ class LocaleManagerBackupHelper {
        mStagedData = stagedData;
        mDelegateAppLocalePackages = delegateAppLocalePackages != null ? delegateAppLocalePackages
                : createPersistedInfo();
        mPkgsToRestore = new ArraySet<>();

        mUserMonitor = new UserMonitor();
        IntentFilter filter = new IntentFilter();
@@ -251,6 +258,9 @@ class LocaleManagerBackupHelper {
                LocalesInfo localesInfo = pkgStates.get(pkgName);
                // Check if the application is already installed for the concerned user.
                if (isPackageInstalledForUser(pkgName, userId)) {
                    if (mPkgsToRestore != null) {
                        mPkgsToRestore.remove(pkgName);
                    }
                    // Don't apply the restore if the locales have already been set for the app.
                    checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId);
                } else {
@@ -279,23 +289,18 @@ class LocaleManagerBackupHelper {

    /**
     * <p><b>Note:</b> This is invoked by service's common monitor
     * {@link LocaleManagerServicePackageMonitor#onPackageAdded} when a new package is
     * {@link LocaleManagerServicePackageMonitor#onPackageAddedWithExtras} when a new package is
     * added on device.
     */
    void onPackageAdded(String packageName, int uid) {
        try {
            synchronized (mStagedDataLock) {
                cleanStagedDataForOldEntriesLocked();

                int userId = UserHandle.getUserId(uid);
                if (mStagedData.contains(userId)) {
                    // Perform lazy restore only if the staged data exists.
                    doLazyRestoreLocked(packageName, userId);
                }
    void onPackageAddedWithExtras(String packageName, int uid, Bundle extras) {
        boolean archived = false;
        if (extras != null) {
            archived = extras.getBoolean(Intent.EXTRA_ARCHIVAL, false);
            if (archived && mPkgsToRestore != null) {
                mPkgsToRestore.add(packageName);
            }
        } catch (Exception e) {
            Slog.e(TAG, "Exception in onPackageAdded.", e);
        }
        checkStageDataAndApplyRestore(packageName, uid);
    }

    /**
@@ -305,6 +310,10 @@ class LocaleManagerBackupHelper {
     */
    void onPackageUpdateFinished(String packageName, int uid) {
        int userId = UserHandle.getUserId(uid);
        if (mPkgsToRestore != null && mPkgsToRestore.contains(packageName)) {
            mPkgsToRestore.remove(packageName);
            checkStageDataAndApplyRestore(packageName, uid);
        }
        cleanApplicationLocalesIfNeeded(packageName, userId);
    }

@@ -338,6 +347,25 @@ class LocaleManagerBackupHelper {
        }
    }

    private void checkStageDataAndApplyRestore(String packageName, int uid) {
        try {
            synchronized (mStagedDataLock) {
                cleanStagedDataForOldEntriesLocked();

                int userId = UserHandle.getUserId(uid);
                if (mStagedData.contains(userId)) {
                    if (mPkgsToRestore != null) {
                        mPkgsToRestore.remove(packageName);
                    }
                    // Perform lazy restore only if the staged data exists.
                    doLazyRestoreLocked(packageName, userId);
                }
            }
        } catch (Exception e) {
            Slog.e(TAG, "Exception in onPackageAdded.", e);
        }
    }

    private boolean isPackageInstalledForUser(String packageName, int userId) {
        PackageInfo pkgInfo = null;
        try {
+3 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.locales;

import android.annotation.NonNull;
import android.os.Bundle;
import android.os.UserHandle;

import com.android.internal.content.PackageMonitor;
@@ -48,8 +49,8 @@ final class LocaleManagerServicePackageMonitor extends PackageMonitor {
    }

    @Override
    public void onPackageAdded(String packageName, int uid) {
        mBackupHelper.onPackageAdded(packageName, uid);
    public void onPackageAddedWithExtras(String packageName, int uid, Bundle extras) {
        mBackupHelper.onPackageAddedWithExtras(packageName, uid, extras);
    }

    @Override
+66 −5
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.LocaleList;
import android.os.Process;
@@ -488,7 +489,7 @@ public class LocaleManagerBackupRestoreTest {

        setUpPackageInstalled(pkgNameA);

        mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
        mPackageMonitor.onPackageAddedWithExtras(pkgNameA, DEFAULT_UID, new Bundle());

        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
                LocaleList.forLanguageTags(langTagsA), false, FrameworkStatsLog
@@ -504,7 +505,67 @@ public class LocaleManagerBackupRestoreTest {

        setUpPackageInstalled(pkgNameB);

        mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
        mPackageMonitor.onPackageAddedWithExtras(pkgNameB, DEFAULT_UID, new Bundle());

        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
                LocaleList.forLanguageTags(langTagsB), true, FrameworkStatsLog
                        .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE);

        mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameB, true, false);

        verify(mMockSpEditor, times(1)).putStringSet(Integer.toString(DEFAULT_USER_ID),
                new ArraySet<>(Arrays.asList(pkgNameB)));
        checkStageDataDoesNotExist(DEFAULT_USER_ID);
    }

    @Test
    public void testRestore_appInstalledAfterSUW_restoresFromStage_ArchiveEnabled()
            throws Exception {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        HashMap<String, LocalesInfo> pkgLocalesMap = new HashMap<>();
        String pkgNameA = "com.android.myAppA";
        String pkgNameB = "com.android.myAppB";
        String langTagsA = "ru";
        String langTagsB = "hi,fr";
        LocalesInfo localesInfoA = new LocalesInfo(langTagsA, false);
        LocalesInfo localesInfoB = new LocalesInfo(langTagsB, true);
        pkgLocalesMap.put(pkgNameA, localesInfoA);
        pkgLocalesMap.put(pkgNameB, localesInfoB);
        writeTestPayload(out, pkgLocalesMap);
        setUpPackageNotInstalled(pkgNameA);
        setUpPackageNotInstalled(pkgNameB);
        setUpLocalesForPackage(pkgNameA, LocaleList.getEmptyLocaleList());
        setUpLocalesForPackage(pkgNameB, LocaleList.getEmptyLocaleList());
        setUpPackageNamesForSp(new ArraySet<>());

        Bundle bundle = new Bundle();
        bundle.putBoolean(Intent.EXTRA_ARCHIVAL, true);
        mPackageMonitor.onPackageAddedWithExtras(pkgNameA, DEFAULT_UID, bundle);
        mPackageMonitor.onPackageAddedWithExtras(pkgNameB, DEFAULT_UID, bundle);

        mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);

        verifyNothingRestored();

        setUpPackageInstalled(pkgNameA);

        mPackageMonitor.onPackageUpdateFinished(pkgNameA, DEFAULT_UID);

        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
                LocaleList.forLanguageTags(langTagsA), false, FrameworkStatsLog
                .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE);

        mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameA, false, false);

        verify(mMockSpEditor, times(0)).putStringSet(anyString(), any());

        pkgLocalesMap.remove(pkgNameA);

        verifyStageDataForUser(pkgLocalesMap, DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);

        setUpPackageInstalled(pkgNameB);

        mPackageMonitor.onPackageUpdateFinished(pkgNameB, DEFAULT_UID);

        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
                LocaleList.forLanguageTags(langTagsB), true, FrameworkStatsLog
@@ -535,7 +596,7 @@ public class LocaleManagerBackupRestoreTest {
        setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
        setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.forLanguageTags("hi,mr"));

        mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
        mPackageMonitor.onPackageAddedWithExtras(DEFAULT_PACKAGE_NAME, DEFAULT_UID, new Bundle());

        // Since locales are already set, we should not restore anything for it.
        verifyNothingRestored();
@@ -612,7 +673,7 @@ public class LocaleManagerBackupRestoreTest {
                DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.minusHours(1).toMillis());
        setUpPackageInstalled(pkgNameA);

        mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID);
        mPackageMonitor.onPackageAddedWithExtras(pkgNameA, DEFAULT_UID, new Bundle());

        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(
                pkgNameA, DEFAULT_USER_ID, LocaleList.forLanguageTags(langTagsA), false,
@@ -627,7 +688,7 @@ public class LocaleManagerBackupRestoreTest {
                DEFAULT_CREATION_TIME_MILLIS + RETENTION_PERIOD.plusSeconds(1).toMillis());
        setUpPackageInstalled(pkgNameB);

        mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID);
        mPackageMonitor.onPackageAddedWithExtras(pkgNameB, DEFAULT_UID, new Bundle());

        verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameB), anyInt(),
                any(), anyBoolean(), anyInt());