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

Commit 244795ca authored by Ebru Kurnaz's avatar Ebru Kurnaz
Browse files

Add BackupHelper for DisplayWindowSettings.

Bug: 346499008
Flag: com.android.window.flags.enable_backup_and_restore_display_window_settings
Test: atest DisplayWindowSettingsBackupHelperTest
Change-Id: I3e82bf6c0f9c3c76bfc790a6a0ff59aca352e2a5
parent 7b610860
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -76,6 +76,9 @@ public enum DesktopExperienceFlags {
            Flags.FLAG_ENABLE_APP_TO_WEB_EDUCATION_ANIMATION),
    ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE(Flags::enableAutoRestartOnDisplayMove, false,
            Flags.FLAG_ENABLE_AUTO_RESTART_ON_DISPLAY_MOVE),
    ENABLE_BACKUP_AND_RESTORE_DISPLAY_WINDOW_SETTINGS(
            Flags::enableBackupAndRestoreDisplayWindowSettings, false,
            Flags.FLAG_ENABLE_BACKUP_AND_RESTORE_DISPLAY_WINDOW_SETTINGS),
    ENABLE_BLOCK_NON_DESKTOP_DISPLAY_WINDOW_DRAG_BUGFIX(
            Flags::enableBlockNonDesktopDisplayWindowDragBugfix, false,
            Flags.FLAG_ENABLE_BLOCK_NON_DESKTOP_DISPLAY_WINDOW_DRAG_BUGFIX),
+74 −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.server.backup;

import android.app.backup.BlobBackupHelper;
import android.util.Slog;

import com.android.server.LocalServices;
import com.android.server.display.utils.DebugUtils;
import com.android.server.wm.WindowManagerInternal;

public class DisplayWindowSettingsBackupHelper extends BlobBackupHelper {
    private static final String TAG = "DWSBackupHelper";

    // current schema of the backup state blob
    private static final int BLOB_VERSION = 1;

    // key under which the data blob is committed to back up
    private static final String KEY_DISPLAY = "display_window";

    // To enable these logs, run:
    // adb shell setprop persist.log.tag.DWSBackupHelper DEBUG
    // adb reboot
    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);

    private final int mUserId;

    public DisplayWindowSettingsBackupHelper(int userId) {
        super(BLOB_VERSION, KEY_DISPLAY);
        mUserId = userId;
    }

    @Override
    protected byte[] getBackupPayload(String key) {
        Slog.i(TAG, "getBackupPayload for " + key + " user " + mUserId);
        if (!KEY_DISPLAY.equals(key)) {
            return null;
        }

        WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
        if (DEBUG) {
            Slog.d(TAG, "Handling backup of " + key);
        }
        return wmInternal.backupDisplayWindowSettings(mUserId);
    }

    @Override
    protected void applyRestoredPayload(String key, byte[] payload) {
        Slog.i(TAG, "applyRestoredPayload for " + key + " user " + mUserId);
        if (!KEY_DISPLAY.equals(key)) {
            return;
        }

        WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
        if (DEBUG) {
            Slog.d(TAG, "Handling restore of " + key);
        }
        wmInternal.restoreDisplayWindowSettings(mUserId, payload);
    }
}
+8 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.window.DesktopExperienceFlags;

import com.android.server.display.DisplayBackupHelper;
import com.android.server.notification.NotificationBackupHelper;
@@ -70,6 +71,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
    private static final String DISPLAY_HELPER = "display";
    private static final String INPUT_HELPER = "input";
    private static final String WEAR_BACKUP_HELPER = "wear";
    private static final String DISPLAY_WINDOW_HELPER = "display_window";

    // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
    // are also used in the full-backup file format, so must not change unless steps are
@@ -108,7 +110,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
                    COMPANION_HELPER,
                    APP_GENDER_HELPER,
                    SYSTEM_GENDER_HELPER,
                    DISPLAY_HELPER);
                    DISPLAY_HELPER,
                    DISPLAY_WINDOW_HELPER);

    /** Helpers that are enabled for full, non-system users. */
    private static final Set<String> sEligibleHelpersForNonSystemUser =
@@ -154,7 +157,10 @@ public class SystemBackupAgent extends BackupAgentHelper {
        if (com.android.hardware.input.Flags.enableBackupAndRestoreForInputGestures()) {
            addHelperIfEligibleForUser(INPUT_HELPER, new InputBackupHelper(mUserId));
        }

        if (DesktopExperienceFlags.ENABLE_BACKUP_AND_RESTORE_DISPLAY_WINDOW_SETTINGS.isTrue()) {
            addHelperIfEligibleForUser(DISPLAY_WINDOW_HELPER,
                    new DisplayWindowSettingsBackupHelper(mUserId));
        }
        // Add Wear helper only if the device is a watch
        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
            addHelperIfEligibleForUser(WEAR_BACKUP_HELPER, new WearBackupHelper());
+15 −6
Original line number Diff line number Diff line
@@ -19,14 +19,16 @@ package com.android.server.wm;
import static android.os.UserHandle.USER_SYSTEM;
import static android.view.Display.TYPE_VIRTUAL;

import static com.android.server.wm.DisplayWindowSettingsXmlHelper.DisplayIdentifierType;
import static com.android.server.wm.DisplayWindowSettingsXmlHelper.IDENTIFIER_PORT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.DisplayWindowSettingsXmlHelper.IDENTIFIER_PORT;
import static com.android.server.wm.DisplayWindowSettingsXmlHelper.DisplayIdentifierType;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.content.Context;
import android.os.Environment;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -84,17 +86,22 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
    private ReadableSettings mBaseSettings;
    @NonNull
    private WritableSettings mOverrideSettings;
    @NonNull
    private BackupManager mBackupManager;

    DisplayWindowSettingsProvider() {
    DisplayWindowSettingsProvider(@NonNull Context context) {
        this(new AtomicFileStorage(getVendorSettingsFile()),
                new AtomicFileStorage(getOverrideSettingsFileForUser(USER_SYSTEM)));
                new AtomicFileStorage(getOverrideSettingsFileForUser(USER_SYSTEM)),
                new BackupManager(context));
    }

    @VisibleForTesting
    DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage baseSettingsStorage,
            @NonNull WritableSettingsStorage overrideSettingsStorage) {
            @NonNull WritableSettingsStorage overrideSettingsStorage,
            @NonNull BackupManager backupManager) {
        mBaseSettings = new ReadableSettings(baseSettingsStorage);
        mOverrideSettings = new WritableSettings(overrideSettingsStorage);
        mBackupManager = backupManager;
    }

    /**
@@ -169,6 +176,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
    public void updateOverrideSettings(@NonNull DisplayInfo info,
            @NonNull SettingsEntry overrides) {
        mOverrideSettings.updateSettingsEntry(info, overrides);
        mBackupManager.dataChanged();
    }

    @Override
@@ -179,6 +187,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
    @Override
    public void clearDisplaySettings(@NonNull DisplayInfo info) {
        mOverrideSettings.clearDisplaySettings(info);
        mBackupManager.dataChanged();
    }

    @VisibleForTesting
@@ -366,7 +375,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
    }

    @NonNull
    public static AtomicFile getOverrideSettingsFileForUser(@UserIdInt int userId) {
    static AtomicFile getOverrideSettingsFileForUser(@UserIdInt int userId) {
        final File directory = (userId == USER_SYSTEM)
                ? Environment.getDataDirectory()
                : Environment.getDataSystemCeDirectory(userId);
+20 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntr
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -162,6 +163,25 @@ class DisplayWindowSettingsXmlHelper {
        return success;
    }

    /**
     * Reads display window settings from an {@link InputStream}, filters out device-specific
     * attributes that should not be backed up, and returns the result as a byte array.
     * <p>
     * This method is specifically designed for backup operations. It parses the full settings
     * file and then re-serializes it, omitting attributes like forced width, height, and
     * density, which are not suitable for restoration on a different device.
     *
     * @param stream The input stream to read the settings from. This stream is closed by the
     *               method upon completion.
     * @return A byte array containing the filtered XML data, ready for backup.
     */
    public static byte[] readAndFilterSettings(InputStream stream) {
        FileData data = FileData.readSettings(stream);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        writeSettings(byteArrayOutputStream, data, /* forBackup= */ true);
        return byteArrayOutputStream.toByteArray();
    }

    static final class FileData {
        int mIdentifierType;
        @NonNull
Loading