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

Commit 59358072 authored by Massimo Carli's avatar Massimo Carli
Browse files

Letterbox Reacheability Multiplier Persistence

Persist values for horizontal and vertical position multipliers.

Test: atest LetterboxConfigurationTest LetterboxConfigurationPersisterTest
Fixes: 199434061

Change-Id: I194828be402ff1fd234e371055522962647a4d32
parent fc3a434b
Loading
Loading
Loading
Loading

proto/src/task_snapshot.proto

deleted100644 → 0
+0 −48
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

 syntax = "proto3";

 package com.android.server.wm;

 option java_package = "com.android.server.wm";
 option java_outer_classname = "WindowManagerProtos";

 message TaskSnapshotProto {
     int32 orientation = 1;
     int32 inset_left = 2;
     int32 inset_top = 3;
     int32 inset_right = 4;
     int32 inset_bottom = 5;
     bool is_real_snapshot = 6;
     int32 windowing_mode = 7;
     int32 system_ui_visibility = 8 [deprecated=true];
     bool is_translucent = 9;
     string top_activity_component = 10;
     // deprecated because original width and height are stored now instead of the scale.
     float legacy_scale = 11 [deprecated=true];
     int64 id = 12;
     int32 rotation = 13;
     // The task width when the snapshot was taken
     int32 task_width = 14;
     // The task height when the snapshot was taken
     int32 task_height = 15;
     int32 appearance = 16;
     int32 letterbox_inset_left = 17;
     int32 letterbox_inset_top = 18;
     int32 letterbox_inset_right = 19;
     int32 letterbox_inset_bottom = 20;
 }
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

syntax = "proto3";

package com.android.server.wm;

option java_package = "com.android.server.wm";
option java_outer_classname = "WindowManagerProtos";

message TaskSnapshotProto {
  int32 orientation = 1;
  int32 inset_left = 2;
  int32 inset_top = 3;
  int32 inset_right = 4;
  int32 inset_bottom = 5;
  bool is_real_snapshot = 6;
  int32 windowing_mode = 7;
  int32 system_ui_visibility = 8 [deprecated=true];
  bool is_translucent = 9;
  string top_activity_component = 10;
  // deprecated because original width and height are stored now instead of the scale.
  float legacy_scale = 11 [deprecated=true];
  int64 id = 12;
  int32 rotation = 13;
  // The task width when the snapshot was taken
  int32 task_width = 14;
  // The task height when the snapshot was taken
  int32 task_height = 15;
  int32 appearance = 16;
  int32 letterbox_inset_left = 17;
  int32 letterbox_inset_top = 18;
  int32 letterbox_inset_right = 19;
  int32 letterbox_inset_bottom = 20;
}

// Persistent letterboxing configurations
message LetterboxProto {

  // Possible values for the letterbox horizontal reachability
  enum LetterboxHorizontalReachability {
    LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT = 0;
    LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER = 1;
    LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT = 2;
  }

  // Possible values for the letterbox vertical reachability
  enum LetterboxVerticalReachability {
    LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP = 0;
    LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER = 1;
    LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2;
  }

  // Represents the current horizontal position for the letterboxed activity
  LetterboxHorizontalReachability letterbox_position_for_horizontal_reachability = 1;
  // Represents the current vertical position for the letterboxed activity
  LetterboxVerticalReachability letterbox_position_for_vertical_reachability = 2;
}
 No newline at end of file
+57 −40
Original line number Diff line number Diff line
@@ -17,14 +17,17 @@
package com.android.server.wm;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Function;

/** Reads letterbox configs from resources and controls their overrides at runtime. */
final class LetterboxConfiguration {
@@ -156,34 +159,25 @@ final class LetterboxConfiguration {
    // portrait device orientation.
    private boolean mIsVerticalReachabilityEnabled;


    // Horizontal position of a center of the letterboxed app window which is global to prevent
    // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
    // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
    // LetterboxUiController#getHorizontalPositionMultiplier which is called from
    // ActivityRecord#updateResolvedBoundsPosition.
    // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
    // Overview after changing position in another app.
    @LetterboxHorizontalReachabilityPosition
    private volatile int mLetterboxPositionForHorizontalReachability;

    // Vertical position of a center of the letterboxed app window which is global to prevent
    // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
    // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
    // LetterboxUiController#getVerticalPositionMultiplier which is called from
    // ActivityRecord#updateResolvedBoundsPosition.
    // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
    // Overview after changing position in another app.
    @LetterboxVerticalReachabilityPosition
    private volatile int mLetterboxPositionForVerticalReachability;

    // Whether education is allowed for letterboxed fullscreen apps.
    private boolean mIsEducationEnabled;

    // Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
    private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;

    // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
    @NonNull
    private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;

    LetterboxConfiguration(Context systemUiContext) {
        this(systemUiContext, new LetterboxConfigurationPersister(systemUiContext,
                () -> readLetterboxHorizontalReachabilityPositionFromConfig(systemUiContext),
                () -> readLetterboxVerticalReachabilityPositionFromConfig(systemUiContext)));
    }

    @VisibleForTesting
    LetterboxConfiguration(Context systemUiContext,
            LetterboxConfigurationPersister letterboxConfigurationPersister) {
        mContext = systemUiContext;
        mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
                R.dimen.config_fixedOrientationLetterboxAspectRatio);
@@ -206,14 +200,14 @@ final class LetterboxConfiguration {
                readLetterboxHorizontalReachabilityPositionFromConfig(mContext);
        mDefaultPositionForVerticalReachability =
                readLetterboxVerticalReachabilityPositionFromConfig(mContext);
        mLetterboxPositionForHorizontalReachability = mDefaultPositionForHorizontalReachability;
        mLetterboxPositionForVerticalReachability = mDefaultPositionForVerticalReachability;
        mIsEducationEnabled = mContext.getResources().getBoolean(
                R.bool.config_letterboxIsEducationEnabled);
        setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat(
                R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
        mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
                R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
        mLetterboxConfigurationPersister = letterboxConfigurationPersister;
        mLetterboxConfigurationPersister.start();
    }

    /**
@@ -653,7 +647,9 @@ final class LetterboxConfiguration {
     * <p>The position multiplier is changed after each double tap in the letterbox area.
     */
    float getHorizontalMultiplierForReachability() {
        switch (mLetterboxPositionForHorizontalReachability) {
        final int letterboxPositionForHorizontalReachability =
                mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability();
        switch (letterboxPositionForHorizontalReachability) {
            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
                return 0.0f;
            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
@@ -663,9 +659,10 @@ final class LetterboxConfiguration {
            default:
                throw new AssertionError(
                        "Unexpected letterbox position type: "
                            + mLetterboxPositionForHorizontalReachability);
                                + letterboxPositionForHorizontalReachability);
        }
    }

    /*
     * Gets vertical position of a center of the letterboxed app window when reachability
     * is enabled specified. 0 corresponds to the top side of the screen and 1 to the bottom side.
@@ -673,7 +670,9 @@ final class LetterboxConfiguration {
     * <p>The position multiplier is changed after each double tap in the letterbox area.
     */
    float getVerticalMultiplierForReachability() {
        switch (mLetterboxPositionForVerticalReachability) {
        final int letterboxPositionForVerticalReachability =
                mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability();
        switch (letterboxPositionForVerticalReachability) {
            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
                return 0.0f;
            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
@@ -683,7 +682,7 @@ final class LetterboxConfiguration {
            default:
                throw new AssertionError(
                        "Unexpected letterbox position type: "
                                + mLetterboxPositionForVerticalReachability);
                                + letterboxPositionForVerticalReachability);
        }
    }

@@ -693,7 +692,7 @@ final class LetterboxConfiguration {
     */
    @LetterboxHorizontalReachabilityPosition
    int getLetterboxPositionForHorizontalReachability() {
        return mLetterboxPositionForHorizontalReachability;
        return mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability();
    }

    /*
@@ -702,7 +701,7 @@ final class LetterboxConfiguration {
     */
    @LetterboxVerticalReachabilityPosition
    int getLetterboxPositionForVerticalReachability() {
        return mLetterboxPositionForVerticalReachability;
        return mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability();
    }

    /** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */
@@ -742,9 +741,8 @@ final class LetterboxConfiguration {
     * right side.
     */
    void movePositionForHorizontalReachabilityToNextRightStop() {
        mLetterboxPositionForHorizontalReachability = Math.min(
                mLetterboxPositionForHorizontalReachability + 1,
                LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT);
        updatePositionForHorizontalReachability(prev -> Math.min(
                prev + 1, LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT));
    }

    /**
@@ -752,8 +750,7 @@ final class LetterboxConfiguration {
     * side.
     */
    void movePositionForHorizontalReachabilityToNextLeftStop() {
        mLetterboxPositionForHorizontalReachability =
                Math.max(mLetterboxPositionForHorizontalReachability - 1, 0);
        updatePositionForHorizontalReachability(prev -> Math.max(prev - 1, 0));
    }

    /**
@@ -761,9 +758,8 @@ final class LetterboxConfiguration {
     * side.
     */
    void movePositionForVerticalReachabilityToNextBottomStop() {
        mLetterboxPositionForVerticalReachability = Math.min(
                mLetterboxPositionForVerticalReachability + 1,
                LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM);
        updatePositionForVerticalReachability(prev -> Math.min(
                prev + 1, LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM));
    }

    /**
@@ -771,8 +767,7 @@ final class LetterboxConfiguration {
     * side.
     */
    void movePositionForVerticalReachabilityToNextTopStop() {
        mLetterboxPositionForVerticalReachability =
                Math.max(mLetterboxPositionForVerticalReachability - 1, 0);
        updatePositionForVerticalReachability(prev -> Math.max(prev - 1, 0));
    }

    /**
@@ -822,4 +817,26 @@ final class LetterboxConfiguration {
                R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
    }

    /** Calculates a new letterboxPositionForHorizontalReachability value and updates the store */
    private void updatePositionForHorizontalReachability(
            Function<Integer, Integer> newHorizonalPositionFun) {
        final int letterboxPositionForHorizontalReachability =
                mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability();
        final int nextHorizontalPosition = newHorizonalPositionFun.apply(
                letterboxPositionForHorizontalReachability);
        mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(
                nextHorizontalPosition);
    }

    /** Calculates a new letterboxPositionForVerticalReachability value and updates the store */
    private void updatePositionForVerticalReachability(
            Function<Integer, Integer> newVerticalPositionFun) {
        final int letterboxPositionForVerticalReachability =
                mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability();
        final int nextVerticalPosition = newVerticalPositionFun.apply(
                letterboxPositionForVerticalReachability);
        mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(
                nextVerticalPosition);
    }

}
+259 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.wm;

import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Environment;
import android.util.AtomicFile;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition;
import com.android.server.wm.LetterboxConfiguration.LetterboxVerticalReachabilityPosition;
import com.android.server.wm.nano.WindowManagerProtos;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Persists the values of letterboxPositionForHorizontalReachability and
 * letterboxPositionForVerticalReachability for {@link LetterboxConfiguration}.
 */
class LetterboxConfigurationPersister {

    private static final String TAG =
            TAG_WITH_CLASS_NAME ? "LetterboxConfigurationPersister" : TAG_WM;

    @VisibleForTesting
    static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";

    private final Context mContext;
    private final Supplier<Integer> mDefaultHorizontalReachabilitySupplier;
    private final Supplier<Integer> mDefaultVerticalReachabilitySupplier;

    // Horizontal position of a center of the letterboxed app window which is global to prevent
    // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
    // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
    // LetterboxUiController#getHorizontalPositionMultiplier which is called from
    // ActivityRecord#updateResolvedBoundsPosition.
    @LetterboxHorizontalReachabilityPosition
    private volatile int mLetterboxPositionForHorizontalReachability;

    // Vertical position of a center of the letterboxed app window which is global to prevent
    // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
    // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
    // LetterboxUiController#getVerticalPositionMultiplier which is called from
    // ActivityRecord#updateResolvedBoundsPosition.
    @LetterboxVerticalReachabilityPosition
    private volatile int mLetterboxPositionForVerticalReachability;

    @NonNull
    private final AtomicFile mConfigurationFile;

    @Nullable
    private final Consumer<String> mCompletionCallback;

    @NonNull
    private final PersisterQueue mPersisterQueue;

    LetterboxConfigurationPersister(Context systemUiContext,
            Supplier<Integer> defaultHorizontalReachabilitySupplier,
            Supplier<Integer> defaultVerticalReachabilitySupplier) {
        this(systemUiContext, defaultHorizontalReachabilitySupplier,
                defaultVerticalReachabilitySupplier,
                Environment.getDataSystemDirectory(), new PersisterQueue(),
                /* completionCallback */ null);
    }

    @VisibleForTesting
    LetterboxConfigurationPersister(Context systemUiContext,
            Supplier<Integer> defaultHorizontalReachabilitySupplier,
            Supplier<Integer> defaultVerticalReachabilitySupplier, File configFolder,
            PersisterQueue persisterQueue, @Nullable Consumer<String> completionCallback) {
        mContext = systemUiContext.createDeviceProtectedStorageContext();
        mDefaultHorizontalReachabilitySupplier = defaultHorizontalReachabilitySupplier;
        mDefaultVerticalReachabilitySupplier = defaultVerticalReachabilitySupplier;
        mCompletionCallback = completionCallback;
        final File prefFiles = new File(configFolder, LETTERBOX_CONFIGURATION_FILENAME);
        mConfigurationFile = new AtomicFile(prefFiles);
        mPersisterQueue = persisterQueue;
        readCurrentConfiguration();
    }

    /**
     * Startes the persistence queue
     */
    void start() {
        mPersisterQueue.startPersisting();
    }

    /*
     * Gets the horizontal position of the letterboxed app window when horizontal reachability is
     * enabled.
     */
    @LetterboxHorizontalReachabilityPosition
    int getLetterboxPositionForHorizontalReachability() {
        return mLetterboxPositionForHorizontalReachability;
    }

    /*
     * Gets the vertical position of the letterboxed app window when vertical reachability is
     * enabled.
     */
    @LetterboxVerticalReachabilityPosition
    int getLetterboxPositionForVerticalReachability() {
        return mLetterboxPositionForVerticalReachability;
    }

    /**
     * Updates letterboxPositionForVerticalReachability if different from the current value
     */
    void setLetterboxPositionForHorizontalReachability(
            int letterboxPositionForHorizontalReachability) {
        if (mLetterboxPositionForHorizontalReachability
                != letterboxPositionForHorizontalReachability) {
            mLetterboxPositionForHorizontalReachability =
                    letterboxPositionForHorizontalReachability;
            updateConfiguration();
        }
    }

    /**
     * Updates letterboxPositionForVerticalReachability if different from the current value
     */
    void setLetterboxPositionForVerticalReachability(
            int letterboxPositionForVerticalReachability) {
        if (mLetterboxPositionForVerticalReachability != letterboxPositionForVerticalReachability) {
            mLetterboxPositionForVerticalReachability = letterboxPositionForVerticalReachability;
            updateConfiguration();
        }
    }

    @VisibleForTesting
    void useDefaultValue() {
        mLetterboxPositionForHorizontalReachability = mDefaultHorizontalReachabilitySupplier.get();
        mLetterboxPositionForVerticalReachability = mDefaultVerticalReachabilitySupplier.get();
    }

    private void readCurrentConfiguration() {
        FileInputStream fis = null;
        try {
            fis = mConfigurationFile.openRead();
            byte[] protoData = readInputStream(fis);
            final WindowManagerProtos.LetterboxProto letterboxData =
                    WindowManagerProtos.LetterboxProto.parseFrom(protoData);
            mLetterboxPositionForHorizontalReachability =
                    letterboxData.letterboxPositionForHorizontalReachability;
            mLetterboxPositionForVerticalReachability =
                    letterboxData.letterboxPositionForVerticalReachability;
        } catch (IOException ioe) {
            Slog.e(TAG,
                    "Error reading from LetterboxConfigurationPersister. "
                            + "Using default values!", ioe);
            useDefaultValue();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    useDefaultValue();
                    Slog.e(TAG, "Error reading from LetterboxConfigurationPersister ", e);
                }
            }
        }
    }

    private void updateConfiguration() {
        mPersisterQueue.addItem(new UpdateValuesCommand(mConfigurationFile,
                mLetterboxPositionForHorizontalReachability,
                mLetterboxPositionForVerticalReachability,
                mCompletionCallback), /* flush */ true);
    }

    private static byte[] readInputStream(InputStream in) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            int size = in.read(buffer);
            while (size > 0) {
                outputStream.write(buffer, 0, size);
                size = in.read(buffer);
            }
            return outputStream.toByteArray();
        } finally {
            outputStream.close();
        }
    }

    private static class UpdateValuesCommand implements
            PersisterQueue.WriteQueueItem<UpdateValuesCommand> {

        @NonNull
        private final AtomicFile mFileToUpdate;
        @Nullable
        private final Consumer<String> mOnComplete;


        private final int mHorizontalReachability;
        private final int mVerticalReachability;

        UpdateValuesCommand(@NonNull AtomicFile fileToUpdate,
                int horizontalReachability, int verticalReachability,
                @Nullable Consumer<String> onComplete) {
            mFileToUpdate = fileToUpdate;
            mHorizontalReachability = horizontalReachability;
            mVerticalReachability = verticalReachability;
            mOnComplete = onComplete;
        }

        @Override
        public void process() {
            final WindowManagerProtos.LetterboxProto letterboxData =
                    new WindowManagerProtos.LetterboxProto();
            letterboxData.letterboxPositionForHorizontalReachability = mHorizontalReachability;
            letterboxData.letterboxPositionForVerticalReachability = mVerticalReachability;
            final byte[] bytes = WindowManagerProtos.LetterboxProto.toByteArray(letterboxData);

            FileOutputStream fos = null;
            try {
                fos = mFileToUpdate.startWrite();
                fos.write(bytes);
                mFileToUpdate.finishWrite(fos);
            } catch (IOException ioe) {
                mFileToUpdate.failWrite(fos);
                Slog.e(TAG,
                        "Error writing to LetterboxConfigurationPersister. "
                                + "Using default values!", ioe);
            } finally {
                if (mOnComplete != null) {
                    mOnComplete.accept("UpdateValuesCommand");
                }
            }
        }
    }
}
+263 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading