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

Commit bb7b68af authored by Mina Granic's avatar Mina Granic Committed by Android (Google) Code Review
Browse files

Merge "Store restored aspect ratios for not yet installed packages in SharedPreferences." into main

parents 6632c19c 8ba714d0
Loading
Loading
Loading
Loading
+20 −5
Original line number Diff line number Diff line
@@ -22,8 +22,12 @@ import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BlobBackupHelper;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.os.HandlerThread;
import android.os.Process;
import android.util.Slog;

import java.time.Clock;

/** A {@link BlobBackupHelper} that handles backup and restore of user aspect ratio settings.*/
public class UserAspectRatioBackupHelper extends BlobBackupHelper {
    private static final String TAG = "UsrAspRatioBackupHlp"; // Must be < 23 characters.
@@ -45,9 +49,16 @@ public class UserAspectRatioBackupHelper extends BlobBackupHelper {
    public UserAspectRatioBackupHelper(@NonNull Context context,
            @NonNull IPackageManager packageManager, @NonNull BackupRestoreEventLogger logger) {
        super(BLOB_VERSION, KEY_USER_ASPECT_RATIO);
        mUserAspectRatioBackupManager = new UserAspectRatioBackupManager(context, packageManager,
                context.getPackageManager());
        setLogger(logger);

        // Handler for package updates.
        HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
                Process.THREAD_PRIORITY_BACKGROUND);
        broadcastHandlerThread.start();

        mUserAspectRatioBackupManager = new UserAspectRatioBackupManager(context, packageManager,
                context.getPackageManager(), getLogger(), broadcastHandlerThread.getThreadHandler(),
                Clock.systemUTC());
    }

    @Override
@@ -58,7 +69,7 @@ public class UserAspectRatioBackupHelper extends BlobBackupHelper {
        }
        byte[] newPayload = null;
        if (KEY_USER_ASPECT_RATIO.equals(key)) {
            newPayload = mUserAspectRatioBackupManager.getBackupPayload(getLogger());
            newPayload = mUserAspectRatioBackupManager.getBackupPayload();
            getLogger().logItemsBackedUp(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1);
        } else {
            Slog.w(TAG, "Unexpected backup key " + key);
@@ -77,8 +88,12 @@ public class UserAspectRatioBackupHelper extends BlobBackupHelper {
            if (payload == null) {
                return;
            }
            mUserAspectRatioBackupManager.stageAndApplyRestoredPayload(payload, getLogger());
            try {
                mUserAspectRatioBackupManager.stageAndApplyRestoredPayload(payload);
                getLogger().logItemsRestored(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1);
            } catch (Exception e) {
                Slog.e(TAG, "Error restoring user aspect ratio ", e);
            }
        } else {
            Slog.w(TAG, "Unexpected restore key " + key);
            getLogger().logItemsRestoreFailed(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1,
+68 −24
Original line number Diff line number Diff line
@@ -29,9 +29,13 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.settings.R;

import java.io.ByteArrayInputStream;
@@ -40,6 +44,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.StandardCharsets;
import java.time.InstantSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -76,20 +81,55 @@ public class UserAspectRatioBackupManager {
    private final PackageManager mPackageManager;
    @NonNull
    private final Set<Integer> mAvailableUserMinAspectRatioSet;
    @NonNull
    private final UserAspectRatioRestoreStorage mStorage;

    @UserIdInt
    private final int mUserId;
    @NonNull
    private final BackupRestoreEventLogger mLogger;


    /**
     * Helper to monitor package states for the purpose of restoring user aspect ratios.
     *
     * <p>This package monitor also keeps the backed-up and restored data up-to-date when a package
     * is removed.
     */
    @VisibleForTesting
    final PackageMonitor mPackageMonitor = new PackageMonitor() {
        @Override
        public void onPackageAdded(String packageName, int uid) {
            final int aspectRatio = mStorage.getAndRemoveUserAspectRatioForPackage(packageName);
            if (aspectRatio != USER_MIN_ASPECT_RATIO_UNSET) {
                checkExistingAspectRatioAndApplyRestore(packageName, aspectRatio);
            }
        }

        @Override
        public void onPackageRemoved(@NonNull String packageName, int uid) {
            // Just in case, but the user aspect ratio should be restored and removed as soon as an
            // app is installed. Therefore there should not be anything to clean up here.
            mStorage.getAndRemoveUserAspectRatioForPackage(packageName);
        }
    };

    public UserAspectRatioBackupManager(@NonNull Context context,
            @NonNull IPackageManager iPackageManager, @NonNull PackageManager packageManager) {
            @NonNull IPackageManager iPackageManager, @NonNull PackageManager packageManager,
            @NonNull BackupRestoreEventLogger logger, @NonNull Handler handler,
            @NonNull InstantSource instantSource) {
        mIPackageManager = iPackageManager;
        mPackageManager = packageManager;
        mUserId = mPackageManager.getUserId();
        mStorage = new UserAspectRatioRestoreStorage(context, mUserId, instantSource);
        mLogger = logger;

        mPackageMonitor.register(context, UserHandle.of(UserHandle.USER_ALL), handler);

        final int[] userAspectRatioResourceValues = context.getResources().getIntArray(
                R.array.config_userAspectRatioOverrideValues);
        mAvailableUserMinAspectRatioSet = Arrays.stream(
                userAspectRatioResourceValues).boxed().collect(Collectors.toSet());
        mAvailableUserMinAspectRatioSet = Arrays.stream(userAspectRatioResourceValues).boxed()
                .collect(Collectors.toSet());
        // App Default is not in the set above, but is always offered. Users can also specify this
        // value, for example if there is an OEM override to some other value.
        mAvailableUserMinAspectRatioSet.add(USER_MIN_ASPECT_RATIO_APP_DEFAULT);
@@ -99,8 +139,8 @@ public class UserAspectRatioBackupManager {
     * Returns the per-app user aspect ratio settings to be backed up as a data-blob.
     */
    @Nullable
    public byte[] getBackupPayload(@NonNull BackupRestoreEventLogger logger) {
        final Map<String, Integer> aspectRatioStates = getAllUserAspectRatios(logger);
    public byte[] getBackupPayload() {
        final Map<String, Integer> aspectRatioStates = getAllUserAspectRatios(mLogger);

        if (DEBUG) {
            Slog.d(TAG, "User aspect ratio states to backup =" + aspectRatioStates);
@@ -110,8 +150,8 @@ public class UserAspectRatioBackupManager {
                new ObjectOutputStream(bos)) {
            oos.writeObject(aspectRatioStates);
            return bos.toByteArray();
        } catch (IOException e) {
            logger.logItemsBackupFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
        } catch (Exception e) {
            mLogger.logItemsBackupFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
                    ERROR_SERIALIZE_FAILED);
            Slog.e(TAG, "Could not serialize payload.", e);
            return null;
@@ -142,8 +182,7 @@ public class UserAspectRatioBackupManager {
    }

    @Nullable
    private static Map<String, Integer> readFromByteArray(@NonNull byte[] payload,
            @NonNull BackupRestoreEventLogger logger) {
    private Map<String, Integer> readFromByteArray(@NonNull byte[] payload) {
        Object readObject = null;
        try (ByteArrayInputStream bis = new ByteArrayInputStream(payload); ObjectInputStream ois =
                new ObjectInputStream(bis)) {
@@ -152,12 +191,12 @@ public class UserAspectRatioBackupManager {
            // type is incorrect.
            return (Map<String, Integer>) readObject;
        } catch (ClassCastException e) {
            logger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
            mLogger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
                    ERROR_TYPE_CAST_FAILED);
            Slog.e(TAG, "Could not cast to Map<String, Integer>: " + readObject, e);
            return null;
        } catch (IOException | ClassNotFoundException e) {
            logger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
            mLogger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
                    ERROR_DESERIALIZE_FAILED);
            Slog.e(TAG, "Could not read payload for backup", e);
            return null;
@@ -171,9 +210,8 @@ public class UserAspectRatioBackupManager {
     * which are present on the device. It will stage the aspect ratio data for the apps which are
     * not installed at the time this is called, to be referenced later when the app is installed.
     */
    public void stageAndApplyRestoredPayload(@NonNull byte[] payload,
            @NonNull BackupRestoreEventLogger logger) {
        final Map<String, Integer> userAspectRatioStates = readFromByteArray(payload, logger);
    public void stageAndApplyRestoredPayload(@NonNull byte[] payload) {
        final Map<String, Integer> userAspectRatioStates = readFromByteArray(payload);
        if (userAspectRatioStates == null || userAspectRatioStates.isEmpty()) {
            Slog.d(TAG, "StageAndApplyRestoredPayload: payload is empty.");
            return;
@@ -190,25 +228,32 @@ public class UserAspectRatioBackupManager {
                        + pkgName);
                continue;
            }
            if (isPackageInstalled(pkgName, logger)) {
            if (isPackageInstalled(pkgName)) {
                Slog.d(TAG, "StageAndApplyRestoredPayload Found package: " + pkgName);
                checkExistingAspectRatioAndApplyRestore(pkgName, aspectRatio, logger);
                checkExistingAspectRatioAndApplyRestore(pkgName, aspectRatio);
            } else {
                // TODO(b/405902444): Support lazy restore when package is installed.
                Slog.d(TAG, "StageAndApplyRestoredPayload package not installed: " + pkgName);
                mStorage.storePackageAndUserAspectRatio(pkgName, aspectRatio);
            }
        }
        mStorage.restoreCompleted();
    }

    private boolean isPackageInstalled(String packageName,
            @NonNull BackupRestoreEventLogger logger) {
    private boolean isPackageInstalled(@NonNull String packageName) {
        try {
            return mPackageManager.getPackageInfo(packageName, /* flags= */ 0) != null;
        } catch (PackageManager.NameNotFoundException e) {
            logger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
            // It is common that the package is not installed during setup. Restore will be retried
            // when the package is installed.
            if (DEBUG) {
                Slog.d(TAG, "Could not get package info for " + packageName);
            }
            return false;
        } catch (Exception e) {
            mLogger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
                    ERROR_QUERY_PACKAGE_FAILED);
            if (DEBUG) {
                Slog.d(TAG, "Could not get package info for " + packageName, e);
                Slog.e(TAG, "Could not get package info for " + packageName, e);
            }
            return false;
        }
@@ -216,8 +261,7 @@ public class UserAspectRatioBackupManager {

    /** Applies the restore for per-app user set min aspect ratio. */
    private void checkExistingAspectRatioAndApplyRestore(@NonNull String pkgName,
            @PackageManager.UserMinAspectRatio int aspectRatio,
            @NonNull BackupRestoreEventLogger logger) {
            @PackageManager.UserMinAspectRatio int aspectRatio) {
        try {
            final int existingUserAspectRatio = mIPackageManager.getUserMinAspectRatio(pkgName,
                    mUserId);
@@ -237,7 +281,7 @@ public class UserAspectRatioBackupManager {
                }
            }
        } catch (RemoteException | IllegalArgumentException e) {
            logger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
            mLogger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
                    ERROR_ASPECT_RATIO_FAILED);
            Slog.e(TAG, "Could not restore user aspect ratio for package " + pkgName, e);
        }
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settings.applications.appcompat;

import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Environment;

import com.android.internal.annotations.VisibleForTesting;

import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;

/**
 * Storage for restored per-app user min aspect ratio.
 *
 * This class uses SharedPreferences to preserve data. Only non-committed data is held in storage.
 * Any stored data waiting for apps to be installed will be deleted after 7 days.
 */
public class UserAspectRatioRestoreStorage {
    @VisibleForTesting
    static final String ASPECT_RATIO_STAGED_DATA_PREFS = "AspectRatioStagedDataPrefs.xml";
    @VisibleForTesting
    static final String KEY_STAGED_DATA_TIME = "staged_data_time";
    // 7 days after completing restore, the staged data will be deleted automatically. This will
    // erase restored user aspect ratios for apps that haven't been installed since.
    private static final Duration EXPIRY_DURATION = Duration.ofDays(7);

    @NonNull
    private final Context mContext;
    @UserIdInt
    private final int mUserId;
    @NonNull
    private final InstantSource mInstantSource;

    // SharedPreferences to store restored user aspect ratios for not-yet-installed packages.
    private final SharedPreferences mSharedPreferences;

    UserAspectRatioRestoreStorage(@NonNull Context context, @UserIdInt int userId,
            @NonNull InstantSource instantSource) {
        mContext = context;
        mUserId = userId;
        mInstantSource = instantSource;

        mSharedPreferences = createSharedPrefs(getPackageNamePrefix()
                + ASPECT_RATIO_STAGED_DATA_PREFS);
    }

    @NonNull
    private String getPackageNamePrefix() {
        return mContext.getPackageName() + "." + mUserId + ".";
    }

    void restoreCompleted() {
        // Store restore time, for the purpose of cleanup after expiry date.
        mSharedPreferences.edit().putLong(KEY_STAGED_DATA_TIME, mInstantSource.millis()).apply();
    }

    /*
     * Stores package name and user aspect ratio that is restored, but cannot be committed at the
     * moment, for example because given package has not been installed yet.
     */
    void storePackageAndUserAspectRatio(@NonNull String packageName,
            @PackageManager.UserMinAspectRatio int userAspectRatio) {
        mSharedPreferences.edit().putInt(packageName, userAspectRatio).apply();
    }

    int getAndRemoveUserAspectRatioForPackage(@NonNull String packageName) {
        removeExpiredDataIfNeeded();
        final int aspectRatio = mSharedPreferences.getInt(packageName, USER_MIN_ASPECT_RATIO_UNSET);
        mSharedPreferences.edit().remove(packageName).apply();
        return aspectRatio;
    }

    private SharedPreferences createSharedPrefs(@NonNull String fileName) {
        final File prefsFile = new File(Environment.getDataSystemDeDirectory(mUserId), fileName);
        return mContext.createDeviceProtectedStorageContext().getSharedPreferences(prefsFile,
                Context.MODE_PRIVATE);
    }

    void removeExpiredDataIfNeeded() {
        if (!mSharedPreferences.contains(KEY_STAGED_DATA_TIME)) {
            // Restore not yet completed (too early to clean up), or already cleaned up.
            return;
        }

        final Instant restoreTime = Instant.ofEpochMilli(mSharedPreferences.getLong(
                KEY_STAGED_DATA_TIME, 0));
        if (Duration.between(restoreTime, mInstantSource.instant()).compareTo(EXPIRY_DURATION)
                >= 0) {
            // Remove the restore time and all data to restore.
            mSharedPreferences.edit().clear().apply();
        }
    }
}
+58 −21
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCRE
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -40,11 +41,16 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteException;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.android.settings.testutils.FakeInstantSource;
import com.android.settings.testutils.FakeSharedPreferences;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +58,7 @@ import org.mockito.Mock;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -79,20 +86,28 @@ public class UserAspectRatioBackupManagerTest {
    @Mock
    private PackageManager mMockPackageManager;

    private Context mContext;
    private FakeSharedPreferences mFakeSharedPreferences;

    private UserAspectRatioBackupManager mBackupManager;

    private final BackupRestoreEventLogger mBackupLogger = new BackupRestoreEventLogger(
            BackupAnnotations.OperationType.BACKUP);
    private final BackupRestoreEventLogger mRestoreLogger = new BackupRestoreEventLogger(
            BackupAnnotations.OperationType.RESTORE);
    private final BackupRestoreEventLogger mLogger = new BackupRestoreEventLogger(
            BackupAnnotations.OperationType.UNKNOWN);

    @Before
    public void setUp() throws Exception {
        Context context = spy(ApplicationProvider.getApplicationContext());
        mContext = spy(ApplicationProvider.getApplicationContext());
        mMockIPackageManager = mock(IPackageManager.class);
        mMockPackageManager = mock(PackageManager.class);
        mBackupManager = new UserAspectRatioBackupManager(context, mMockIPackageManager,
                mMockPackageManager);

        HandlerThread broadcastHandlerThread = new HandlerThread("UARTest",
                Process.THREAD_PRIORITY_BACKGROUND);
        broadcastHandlerThread.start();
        setupMockSharedPreferences();

        mBackupManager = new UserAspectRatioBackupManager(mContext, mMockIPackageManager,
                mMockPackageManager, mLogger, broadcastHandlerThread.getThreadHandler(),
                new FakeInstantSource());
    }

    @Test
@@ -100,7 +115,7 @@ public class UserAspectRatioBackupManagerTest {
            throws Exception {
        setUpInstalledPackages(List.of());

        verifyPayloadForAppAspectRatio(Map.of(), mBackupManager.getBackupPayload(mBackupLogger));
        verifyPayloadForAppAspectRatio(Map.of(), mBackupManager.getBackupPayload());
    }

    @Test
@@ -108,7 +123,7 @@ public class UserAspectRatioBackupManagerTest {
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_UNSET);
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));

        verifyPayloadForAppAspectRatio(Map.of(), mBackupManager.getBackupPayload(mBackupLogger));
        verifyPayloadForAppAspectRatio(Map.of(), mBackupManager.getBackupPayload());
    }

    @Test
@@ -116,7 +131,7 @@ public class UserAspectRatioBackupManagerTest {
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_FULLSCREEN);
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));

        final byte[] payload = mBackupManager.getBackupPayload(mBackupLogger);
        final byte[] payload = mBackupManager.getBackupPayload();

        verifyPayloadForAppAspectRatio(DEFAULT_PACKAGE_ASPECT_RATIO_MAP, payload);
    }
@@ -128,7 +143,7 @@ public class UserAspectRatioBackupManagerTest {
        doThrow(new RemoteException("mock")).when(mMockIPackageManager).getUserMinAspectRatio(
                anyString(), anyInt());

        verifyPayloadForAppAspectRatio(Map.of(), mBackupManager.getBackupPayload(mBackupLogger));
        verifyPayloadForAppAspectRatio(Map.of(), mBackupManager.getBackupPayload());
    }

    @Test
@@ -143,36 +158,36 @@ public class UserAspectRatioBackupManagerTest {
                .getUserMinAspectRatio(
                        eq(OTHER_PACKAGE_NAME), anyInt());

        byte[] payload = mBackupManager.getBackupPayload(mBackupLogger);
        byte[] payload = mBackupManager.getBackupPayload();

        verifyPayloadForAppAspectRatio(DEFAULT_PACKAGE_ASPECT_RATIO_MAP, payload);
    }

    @Test
    public void testRestore_emptyPayload_nothingRestored() throws Exception {
        mBackupManager.stageAndApplyRestoredPayload(/* payload= */ new byte[0],
                mRestoreLogger);
        mBackupManager.stageAndApplyRestoredPayload(/* payload= */ new byte[0]);

        verifyNothingRestored();
    }

    @Test
    public void testRestore_zeroLengthPayload_nothingRestored() throws Exception {
        mBackupManager.stageAndApplyRestoredPayload(/* payload= */ writeEmptyTestPayload(),
                mRestoreLogger);
        mBackupManager.stageAndApplyRestoredPayload(/* payload= */ writeEmptyTestPayload());

        verifyNothingRestored();
    }

    @Test
    public void testRestore_appNotInstalled_nothingIsRestored() throws Exception {
    public void testRestore_appNotInstalled_aspectRatioStored() throws Exception {
        final byte[] out = writeTestPayload(DEFAULT_PACKAGE_ASPECT_RATIO_MAP);
        // Backed up app is not installed on the restore device.
        setUpInstalledPackages(List.of());

        mBackupManager.stageAndApplyRestoredPayload(out, mRestoreLogger);
        mBackupManager.stageAndApplyRestoredPayload(out);

        verifyNothingRestored();
        assertEquals(USER_MIN_ASPECT_RATIO_FULLSCREEN, mFakeSharedPreferences.getInt(
                DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_UNSET));
    }

    @Test
@@ -181,9 +196,9 @@ public class UserAspectRatioBackupManagerTest {
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_UNSET);

        mBackupManager.stageAndApplyRestoredPayload(out, mRestoreLogger);
        mBackupManager.stageAndApplyRestoredPayload(out);

        // Locales were restored.
        // User aspect ratio is restored.
        verify(mMockIPackageManager).setUserMinAspectRatio(DEFAULT_PACKAGE_NAME,
                DEFAULT_USER_ID, USER_MIN_ASPECT_RATIO_FULLSCREEN);
    }
@@ -194,11 +209,26 @@ public class UserAspectRatioBackupManagerTest {
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);

        mBackupManager.stageAndApplyRestoredPayload(out, mRestoreLogger);
        mBackupManager.stageAndApplyRestoredPayload(out);

        verifyNothingRestored();
    }

    @Test
    public void testPackageAdded_aspectRatioRestored() throws Exception {
        final byte[] out = writeTestPayload(DEFAULT_PACKAGE_ASPECT_RATIO_MAP);
        // Backed up app is not installed on the restore device.
        setUpInstalledPackages(List.of());
        // Restored data should be stored and wait until package is installed.
        mBackupManager.stageAndApplyRestoredPayload(out);

        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        mBackupManager.mPackageMonitor.onPackageAdded(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);

        // User aspect ratio is restored.
        verify(mMockIPackageManager).setUserMinAspectRatio(DEFAULT_PACKAGE_NAME,
                DEFAULT_USER_ID, USER_MIN_ASPECT_RATIO_FULLSCREEN);
    }

    /**
     * Verifies that nothing was restored for any package.
@@ -252,6 +282,13 @@ public class UserAspectRatioBackupManagerTest {
                eq(packageName), anyInt());
    }

    private void setupMockSharedPreferences() {
        mFakeSharedPreferences = new FakeSharedPreferences();
        doReturn(mContext).when(mContext).createDeviceProtectedStorageContext();
        doReturn(mFakeSharedPreferences).when(mContext).getSharedPreferences(any(File.class),
                eq(Context.MODE_PRIVATE));
    }

    private void verifyPayloadForAppAspectRatio(Map<String, Integer> expectedPkgAspectRatioMap,
            byte[] payload) throws IOException, ClassNotFoundException {

+137 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading