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

Commit 8ba714d0 authored by Mina Granic's avatar Mina Granic
Browse files

Store restored aspect ratios for not yet installed packages in SharedPreferences.

UserAspectRatioBackupManager registers for updates on package added/removed.
Once package is added, data is read from SharedPreferences and restored user
aspect ratio is committed.

Data for apps which are not installed within 7 days is deleted.

Flag: com.android.window.flags.backup_and_restore_for_user_aspect_ratio_settings
Bug: 405901435
Bug: 408430597
Test: atest UserAspectRatioBackupManagerTest
Test: manual
Change-Id: Id4d8fee385a320370cce7d56910dc75fbfd8792b
parent f82c982d
Loading
Loading
Loading
Loading
+20 −5
Original line number Original line Diff line number Diff line
@@ -22,8 +22,12 @@ import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BlobBackupHelper;
import android.app.backup.BlobBackupHelper;
import android.content.Context;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManager;
import android.os.HandlerThread;
import android.os.Process;
import android.util.Slog;
import android.util.Slog;


import java.time.Clock;

/** A {@link BlobBackupHelper} that handles backup and restore of user aspect ratio settings.*/
/** A {@link BlobBackupHelper} that handles backup and restore of user aspect ratio settings.*/
public class UserAspectRatioBackupHelper extends BlobBackupHelper {
public class UserAspectRatioBackupHelper extends BlobBackupHelper {
    private static final String TAG = "UsrAspRatioBackupHlp"; // Must be < 23 characters.
    private static final String TAG = "UsrAspRatioBackupHlp"; // Must be < 23 characters.
@@ -45,9 +49,16 @@ public class UserAspectRatioBackupHelper extends BlobBackupHelper {
    public UserAspectRatioBackupHelper(@NonNull Context context,
    public UserAspectRatioBackupHelper(@NonNull Context context,
            @NonNull IPackageManager packageManager, @NonNull BackupRestoreEventLogger logger) {
            @NonNull IPackageManager packageManager, @NonNull BackupRestoreEventLogger logger) {
        super(BLOB_VERSION, KEY_USER_ASPECT_RATIO);
        super(BLOB_VERSION, KEY_USER_ASPECT_RATIO);
        mUserAspectRatioBackupManager = new UserAspectRatioBackupManager(context, packageManager,
                context.getPackageManager());
        setLogger(logger);
        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
    @Override
@@ -58,7 +69,7 @@ public class UserAspectRatioBackupHelper extends BlobBackupHelper {
        }
        }
        byte[] newPayload = null;
        byte[] newPayload = null;
        if (KEY_USER_ASPECT_RATIO.equals(key)) {
        if (KEY_USER_ASPECT_RATIO.equals(key)) {
            newPayload = mUserAspectRatioBackupManager.getBackupPayload(getLogger());
            newPayload = mUserAspectRatioBackupManager.getBackupPayload();
            getLogger().logItemsBackedUp(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1);
            getLogger().logItemsBackedUp(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1);
        } else {
        } else {
            Slog.w(TAG, "Unexpected backup key " + key);
            Slog.w(TAG, "Unexpected backup key " + key);
@@ -77,8 +88,12 @@ public class UserAspectRatioBackupHelper extends BlobBackupHelper {
            if (payload == null) {
            if (payload == null) {
                return;
                return;
            }
            }
            mUserAspectRatioBackupManager.stageAndApplyRestoredPayload(payload, getLogger());
            try {
                mUserAspectRatioBackupManager.stageAndApplyRestoredPayload(payload);
                getLogger().logItemsRestored(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1);
                getLogger().logItemsRestored(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1);
            } catch (Exception e) {
                Slog.e(TAG, "Error restoring user aspect ratio ", e);
            }
        } else {
        } else {
            Slog.w(TAG, "Unexpected restore key " + key);
            Slog.w(TAG, "Unexpected restore key " + key);
            getLogger().logItemsRestoreFailed(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1,
            getLogger().logItemsRestoreFailed(DATA_TYPE_USER_ASPECT_RATIO, /* count= */ 1,
+68 −24
Original line number Original line Diff line number Diff line
@@ -29,9 +29,13 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.util.Slog;


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


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


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


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


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


    private boolean isPackageInstalled(String packageName,
    private boolean isPackageInstalled(@NonNull String packageName) {
            @NonNull BackupRestoreEventLogger logger) {
        try {
        try {
            return mPackageManager.getPackageInfo(packageName, /* flags= */ 0) != null;
            return mPackageManager.getPackageInfo(packageName, /* flags= */ 0) != null;
        } catch (PackageManager.NameNotFoundException e) {
        } 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);
                    ERROR_QUERY_PACKAGE_FAILED);
            if (DEBUG) {
            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;
            return false;
        }
        }
@@ -216,8 +261,7 @@ public class UserAspectRatioBackupManager {


    /** Applies the restore for per-app user set min aspect ratio. */
    /** Applies the restore for per-app user set min aspect ratio. */
    private void checkExistingAspectRatioAndApplyRestore(@NonNull String pkgName,
    private void checkExistingAspectRatioAndApplyRestore(@NonNull String pkgName,
            @PackageManager.UserMinAspectRatio int aspectRatio,
            @PackageManager.UserMinAspectRatio int aspectRatio) {
            @NonNull BackupRestoreEventLogger logger) {
        try {
        try {
            final int existingUserAspectRatio = mIPackageManager.getUserMinAspectRatio(pkgName,
            final int existingUserAspectRatio = mIPackageManager.getUserMinAspectRatio(pkgName,
                    mUserId);
                    mUserId);
@@ -237,7 +281,7 @@ public class UserAspectRatioBackupManager {
                }
                }
            }
            }
        } catch (RemoteException | IllegalArgumentException e) {
        } catch (RemoteException | IllegalArgumentException e) {
            logger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
            mLogger.logItemsRestoreFailed(KEY_USER_ASPECT_RATIO, /* count= */ 1,
                    ERROR_ASPECT_RATIO_FAILED);
                    ERROR_ASPECT_RATIO_FAILED);
            Slog.e(TAG, "Could not restore user aspect ratio for package " + pkgName, e);
            Slog.e(TAG, "Could not restore user aspect ratio for package " + pkgName, e);
        }
        }
+115 −0
Original line number Original line 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 Original line 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 android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;


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


import androidx.test.core.app.ApplicationProvider;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
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.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
@@ -52,6 +58,7 @@ import org.mockito.Mock;


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


    private Context mContext;
    private FakeSharedPreferences mFakeSharedPreferences;

    private UserAspectRatioBackupManager mBackupManager;
    private UserAspectRatioBackupManager mBackupManager;


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


    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        Context context = spy(ApplicationProvider.getApplicationContext());
        mContext = spy(ApplicationProvider.getApplicationContext());
        mMockIPackageManager = mock(IPackageManager.class);
        mMockIPackageManager = mock(IPackageManager.class);
        mMockPackageManager = mock(PackageManager.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
    @Test
@@ -100,7 +115,7 @@ public class UserAspectRatioBackupManagerTest {
            throws Exception {
            throws Exception {
        setUpInstalledPackages(List.of());
        setUpInstalledPackages(List.of());


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


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


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


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


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


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


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


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


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


        verifyPayloadForAppAspectRatio(DEFAULT_PACKAGE_ASPECT_RATIO_MAP, payload);
        verifyPayloadForAppAspectRatio(DEFAULT_PACKAGE_ASPECT_RATIO_MAP, payload);
    }
    }


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


        verifyNothingRestored();
        verifyNothingRestored();
    }
    }


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


        verifyNothingRestored();
        verifyNothingRestored();
    }
    }


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


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


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


    @Test
    @Test
@@ -181,9 +196,9 @@ public class UserAspectRatioBackupManagerTest {
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_UNSET);
        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,
        verify(mMockIPackageManager).setUserMinAspectRatio(DEFAULT_PACKAGE_NAME,
                DEFAULT_USER_ID, USER_MIN_ASPECT_RATIO_FULLSCREEN);
                DEFAULT_USER_ID, USER_MIN_ASPECT_RATIO_FULLSCREEN);
    }
    }
@@ -194,11 +209,26 @@ public class UserAspectRatioBackupManagerTest {
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        setUpInstalledPackages(List.of(DEFAULT_PACKAGE_NAME));
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
        setUpAspectRatioForPackage(DEFAULT_PACKAGE_NAME, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);


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


        verifyNothingRestored();
        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.
     * Verifies that nothing was restored for any package.
@@ -252,6 +282,13 @@ public class UserAspectRatioBackupManagerTest {
                eq(packageName), anyInt());
                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,
    private void verifyPayloadForAppAspectRatio(Map<String, Integer> expectedPkgAspectRatioMap,
            byte[] payload) throws IOException, ClassNotFoundException {
            byte[] payload) throws IOException, ClassNotFoundException {


+137 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading