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

Commit 77e7b277 authored by David Padlipsky's avatar David Padlipsky Committed by Android (Google) Code Review
Browse files

Merge "Implement backup and restore of input gesture customizations" into main

parents 64bca52b 94f56763
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -233,3 +233,12 @@ flag {
    description: "Key Event Activity Detection"
    bug: "356412905"
}

flag {
   name: "enable_backup_and_restore_for_input_gestures"
   namespace: "input"
   description: "Adds backup and restore support for custom input gestures"
   bug: "382184249"
   is_fixed_read_only: true
}
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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 static com.android.server.input.InputManagerInternal.BACKUP_CATEGORY_INPUT_GESTURES;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.backup.BlobBackupHelper;
import android.util.Slog;

import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;

import java.util.HashMap;
import java.util.Map;

public class InputBackupHelper extends BlobBackupHelper {
    private static final String TAG = "InputBackupHelper";   // must be < 23 chars

    // Current version of the blob schema
    private static final int BLOB_VERSION = 1;

    // Key under which the payload blob is stored
    private static final String KEY_INPUT_GESTURES = "input_gestures";

    private final @UserIdInt int mUserId;

    private final @NonNull InputManagerInternal mInputManagerInternal;

    public InputBackupHelper(int userId) {
        super(BLOB_VERSION, KEY_INPUT_GESTURES);
        mUserId = userId;
        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
    }

    @Override
    protected byte[] getBackupPayload(String key) {
        Map<Integer, byte[]> payloads;
        try {
            payloads = mInputManagerInternal.getBackupPayload(mUserId);
        } catch (Exception exception) {
            Slog.e(TAG, "Failed to get backup payload for input gestures", exception);
            return null;
        }

        if (KEY_INPUT_GESTURES.equals(key)) {
            return payloads.getOrDefault(BACKUP_CATEGORY_INPUT_GESTURES, null);
        }

        return null;
    }

    @Override
    protected void applyRestoredPayload(String key, byte[] payload) {
        Map<Integer, byte[]> payloads = new HashMap<>();
        if (KEY_INPUT_GESTURES.equals(key)) {
            payloads.put(BACKUP_CATEGORY_INPUT_GESTURES, payload);
        }

        try {
            mInputManagerInternal.applyBackupPayload(payloads, mUserId);
        } catch (Exception exception) {
            Slog.e(TAG, "Failed to apply input backup payload", exception);
        }
    }

}
+5 −1
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
    private static final String COMPANION_HELPER = "companion";
    private static final String SYSTEM_GENDER_HELPER = "system_gender";
    private static final String DISPLAY_HELPER = "display";
    private static final String INPUT_HELPER = "input";

    // 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
@@ -112,7 +113,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
    private static final Set<String> sEligibleHelpersForNonSystemUser =
            SetUtils.union(sEligibleHelpersForProfileUser,
                    Sets.newArraySet(ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER, PREFERRED_HELPER,
                            SHORTCUT_MANAGER_HELPER));
                            SHORTCUT_MANAGER_HELPER, INPUT_HELPER));

    private int mUserId = UserHandle.USER_SYSTEM;
    private boolean mIsProfileUser = false;
@@ -149,6 +150,9 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelperIfEligibleForUser(SYSTEM_GENDER_HELPER,
                new SystemGrammaticalGenderBackupHelper(mUserId));
        addHelperIfEligibleForUser(DISPLAY_HELPER, new DisplayBackupHelper(mUserId));
        if (com.android.hardware.input.Flags.enableBackupAndRestoreForInputGestures()) {
            addHelperIfEligibleForUser(INPUT_HELPER, new InputBackupHelper(mUserId));
        }
    }

    @Override
+39 −20
Original line number Diff line number Diff line
@@ -125,8 +125,20 @@ public final class InputDataStore {
        }
    }

    @VisibleForTesting
    List<InputGestureData> readInputGesturesXml(InputStream stream, boolean utf8Encoded)
    /**
     * Parses the given input stream and returns the list of {@link InputGestureData} objects.
     * This parsing happens on a best effort basis. If invalid data exists in the given payload
     * it will be skipped. An example of this would be a keycode that does not exist in the
     * present version of Android.  If the payload is malformed, instead this will throw an
     * exception and require the caller to handel this appropriately for its situation.
     *
     * @param stream stream of the input payload of XML data
     * @param utf8Encoded whether or not the input data is UTF-8 encoded
     * @return list of {@link InputGestureData} objects pulled from the payload
     * @throws XmlPullParserException
     * @throws IOException
     */
    public List<InputGestureData> readInputGesturesXml(InputStream stream, boolean utf8Encoded)
            throws XmlPullParserException, IOException {
        List<InputGestureData> inputGestureDataList = new ArrayList<>();
        TypedXmlPullParser parser;
@@ -153,6 +165,31 @@ public final class InputDataStore {
        return inputGestureDataList;
    }

    /**
     * Serializes the given list of {@link InputGestureData} objects to XML in the provided output
     * stream.
     *
     * @param stream               output stream to put serialized data.
     * @param utf8Encoded          whether or not to encode the serialized data in UTF-8 format.
     * @param inputGestureDataList the list of {@link InputGestureData} objects to serialize.
     */
    public void writeInputGestureXml(OutputStream stream, boolean utf8Encoded,
            List<InputGestureData> inputGestureDataList) throws IOException {
        final TypedXmlSerializer serializer;
        if (utf8Encoded) {
            serializer = Xml.newFastSerializer();
            serializer.setOutput(stream, StandardCharsets.UTF_8.name());
        } else {
            serializer = Xml.resolveSerializer(stream);
        }

        serializer.startDocument(null, true);
        serializer.startTag(null, TAG_ROOT);
        writeInputGestureListToXml(serializer, inputGestureDataList);
        serializer.endTag(null, TAG_ROOT);
        serializer.endDocument();
    }

    private InputGestureData readInputGestureFromXml(TypedXmlPullParser parser)
            throws XmlPullParserException, IOException, IllegalArgumentException {
        InputGestureData.Builder builder = new InputGestureData.Builder();
@@ -239,24 +276,6 @@ public final class InputDataStore {
        return inputGestureDataList;
    }

    @VisibleForTesting
    void writeInputGestureXml(OutputStream stream, boolean utf8Encoded,
            List<InputGestureData> inputGestureDataList) throws IOException {
        final TypedXmlSerializer serializer;
        if (utf8Encoded) {
            serializer = Xml.newFastSerializer();
            serializer.setOutput(stream, StandardCharsets.UTF_8.name());
        } else {
            serializer = Xml.resolveSerializer(stream);
        }

        serializer.startDocument(null, true);
        serializer.startTag(null, TAG_ROOT);
        writeInputGestureListToXml(serializer, inputGestureDataList);
        serializer.endTag(null, TAG_ROOT);
        serializer.endDocument();
    }

    private void writeInputGestureToXml(TypedXmlSerializer serializer,
            InputGestureData inputGestureData) throws IOException {
        serializer.startTag(null, TAG_INPUT_GESTURE);
+32 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.input;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -32,7 +33,11 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.policy.IShortcutService;

import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * Input manager local system service interface.
@@ -41,6 +46,15 @@ import java.util.List;
 */
public abstract class InputManagerInternal {

    // Backup and restore information for custom input gestures.
    public static final int BACKUP_CATEGORY_INPUT_GESTURES = 0;

    // Backup and Restore categories for sending map of data back and forth to backup and restore
    // infrastructure.
    @IntDef({BACKUP_CATEGORY_INPUT_GESTURES})
    public @interface BackupCategory {
    }

    /**
     * Called by the display manager to set information about the displays as needed
     * by the input system.  The input system must copy this information to retain it.
@@ -312,4 +326,22 @@ public abstract class InputManagerInternal {
     * @return true if setting power wakeup was successful.
     */
    public abstract boolean setKernelWakeEnabled(int deviceId, boolean enabled);

    /**
     * Retrieves the input gestures backup payload data.
     *
     * @param userId the user ID of the backup data.
     * @return byte array of UTF-8 encoded backup data.
     */
    public abstract Map<Integer, byte[]> getBackupPayload(int userId) throws IOException;

    /**
     * Applies the given UTF-8 encoded byte array payload to the given user's input data
     * on a best effort basis.
     *
     * @param payload UTF-8 encoded map of byte arrays of restored data
     * @param userId the user ID for which to apply the payload data
     */
    public abstract void applyBackupPayload(Map<Integer, byte[]> payload, int userId)
            throws XmlPullParserException, IOException;
}
Loading