diff options
author | 2022-10-26 07:58:44 +0000 | |
---|---|---|
committer | 2022-10-26 07:58:44 +0000 | |
commit | ed3a1137d42a5790b30f93725cae1dd1e7965558 (patch) | |
tree | 38e11ffdf53129bd09c694c76cb9be46fd79bb16 | |
parent | 727c4b563fe2a78d741a8922f1407a718c8761be (diff) | |
parent | 82a3d574a0a922c6d25086b19e145cbf8c3e027e (diff) |
Merge "Letterbox Reacheability Multiplier Persistence" into tm-qpr-dev am: a8117a579f am: 82a3d574a0
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19429915
Change-Id: I09f187d2620654a68b96f6722abe97ac6ef2c094
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
6 files changed, 822 insertions, 88 deletions
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto deleted file mode 100644 index 1cbc17ed9f41..000000000000 --- a/proto/src/task_snapshot.proto +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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; - } diff --git a/proto/src/windowmanager.proto b/proto/src/windowmanager.proto new file mode 100644 index 000000000000..f26404c66623 --- /dev/null +++ b/proto/src/windowmanager.proto @@ -0,0 +1,71 @@ +/* + * 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 diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index a469c6b39e7f..c19353cb2676 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -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: @@ -662,10 +658,11 @@ final class LetterboxConfiguration { return 1.0f; default: throw new AssertionError( - "Unexpected letterbox position type: " - + mLetterboxPositionForHorizontalReachability); + "Unexpected letterbox position type: " + + 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); + } + } diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java new file mode 100644 index 000000000000..70639b16c828 --- /dev/null +++ b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java @@ -0,0 +1,259 @@ +/* + * 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"); + } + } + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java new file mode 100644 index 000000000000..1246d1ee46e8 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java @@ -0,0 +1,263 @@ +/* + * 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP; +import static com.android.server.wm.LetterboxConfigurationPersister.LETTERBOX_CONFIGURATION_FILENAME; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.platform.test.annotations.Presubmit; +import android.util.AtomicFile; + +import androidx.test.filters.SmallTest; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +@SmallTest +@Presubmit +public class LetterboxConfigurationPersisterTest { + + private static final long TIMEOUT = 2000L; // 2 secs + + private LetterboxConfigurationPersister mLetterboxConfigurationPersister; + private Context mContext; + private PersisterQueue mPersisterQueue; + private QueueState mQueueState; + private PersisterQueue.Listener mQueueListener; + private File mConfigFolder; + + @Before + public void setUp() throws Exception { + mContext = getInstrumentation().getTargetContext(); + mConfigFolder = mContext.getFilesDir(); + mPersisterQueue = new PersisterQueue(); + mQueueState = new QueueState(); + mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(mContext, + () -> mContext.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForHorizontalReachability), + () -> mContext.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForVerticalReachability), + mConfigFolder, mPersisterQueue, mQueueState); + mQueueListener = queueEmpty -> mQueueState.onItemAdded(); + mPersisterQueue.addListener(mQueueListener); + mLetterboxConfigurationPersister.start(); + } + + public void tearDown() throws InterruptedException { + deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue); + waitForCompletion(mPersisterQueue); + mPersisterQueue.removeListener(mQueueListener); + stopPersisterSafe(mPersisterQueue); + } + + @Test + public void test_whenStoreIsCreated_valuesAreDefaults() { + final int positionForHorizontalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(); + final int defaultPositionForHorizontalReachability = + mContext.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForHorizontalReachability); + Assert.assertEquals(defaultPositionForHorizontalReachability, + positionForHorizontalReachability); + final int positionForVerticalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(); + final int defaultPositionForVerticalReachability = + mContext.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForVerticalReachability); + Assert.assertEquals(defaultPositionForVerticalReachability, + positionForVerticalReachability); + } + + @Test + public void test_whenUpdatedWithNewValues_valuesAreWritten() { + mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability( + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT); + mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability( + LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP); + waitForCompletion(mPersisterQueue); + final int newPositionForHorizontalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(); + final int newPositionForVerticalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(); + Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + newPositionForHorizontalReachability); + Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + newPositionForVerticalReachability); + } + + @Test + public void test_whenUpdatedWithNewValues_valuesAreReadAfterRestart() { + final PersisterQueue firstPersisterQueue = new PersisterQueue(); + final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister( + mContext, () -> -1, () -> -1, mContext.getFilesDir(), firstPersisterQueue, + mQueueState); + firstPersister.start(); + firstPersister.setLetterboxPositionForHorizontalReachability( + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT); + firstPersister.setLetterboxPositionForVerticalReachability( + LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP); + waitForCompletion(firstPersisterQueue); + stopPersisterSafe(firstPersisterQueue); + final PersisterQueue secondPersisterQueue = new PersisterQueue(); + final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister( + mContext, () -> -1, () -> -1, mContext.getFilesDir(), secondPersisterQueue, + mQueueState); + secondPersister.start(); + final int newPositionForHorizontalReachability = + secondPersister.getLetterboxPositionForHorizontalReachability(); + final int newPositionForVerticalReachability = + secondPersister.getLetterboxPositionForVerticalReachability(); + Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + newPositionForHorizontalReachability); + Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + newPositionForVerticalReachability); + deleteConfiguration(secondPersister, secondPersisterQueue); + waitForCompletion(secondPersisterQueue); + stopPersisterSafe(secondPersisterQueue); + } + + @Test + public void test_whenUpdatedWithNewValuesAndDeleted_valuesAreDefaults() { + mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability( + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT); + mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability( + LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP); + waitForCompletion(mPersisterQueue); + final int newPositionForHorizontalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(); + final int newPositionForVerticalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(); + Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + newPositionForHorizontalReachability); + Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + newPositionForVerticalReachability); + deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue); + waitForCompletion(mPersisterQueue); + final int positionForHorizontalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(); + final int defaultPositionForHorizontalReachability = + mContext.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForHorizontalReachability); + Assert.assertEquals(defaultPositionForHorizontalReachability, + positionForHorizontalReachability); + final int positionForVerticalReachability = + mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(); + final int defaultPositionForVerticalReachability = + mContext.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForVerticalReachability); + Assert.assertEquals(defaultPositionForVerticalReachability, + positionForVerticalReachability); + } + + private void stopPersisterSafe(PersisterQueue persisterQueue) { + try { + persisterQueue.stopPersisting(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void waitForCompletion(PersisterQueue persisterQueue) { + final long endTime = System.currentTimeMillis() + TIMEOUT; + // The queue could be empty but the last item still processing and not completed. For this + // reason the completion happens when there are not more items to process and the last one + // has completed. + while (System.currentTimeMillis() < endTime && (!isQueueEmpty(persisterQueue) + || !hasLastItemCompleted())) { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { /* Nope */} + } + } + + private boolean isQueueEmpty(PersisterQueue persisterQueue) { + return persisterQueue.findLastItem( + writeQueueItem -> true, PersisterQueue.WriteQueueItem.class) != null; + } + + private boolean hasLastItemCompleted() { + return mQueueState.isEmpty(); + } + + private void deleteConfiguration(LetterboxConfigurationPersister persister, + PersisterQueue persisterQueue) { + final AtomicFile fileToDelete = new AtomicFile( + new File(mConfigFolder, LETTERBOX_CONFIGURATION_FILENAME)); + persisterQueue.addItem( + new DeleteFileCommand(fileToDelete, mQueueState.andThen( + s -> persister.useDefaultValue())), true); + } + + private static class DeleteFileCommand implements + PersisterQueue.WriteQueueItem<DeleteFileCommand> { + + @NonNull + private final AtomicFile mFileToDelete; + @Nullable + private final Consumer<String> mOnComplete; + + DeleteFileCommand(@NonNull AtomicFile fileToDelete, Consumer<String> onComplete) { + mFileToDelete = fileToDelete; + mOnComplete = onComplete; + } + + @Override + public void process() { + mFileToDelete.delete(); + if (mOnComplete != null) { + mOnComplete.accept("DeleteFileCommand"); + } + } + } + + // Contains the current length of the persister queue + private static class QueueState implements Consumer<String> { + + // The current number of commands in the queue + @VisibleForTesting + private final AtomicInteger mCounter = new AtomicInteger(0); + + @Override + public void accept(String s) { + mCounter.decrementAndGet(); + } + + void onItemAdded() { + mCounter.incrementAndGet(); + } + + boolean isEmpty() { + return mCounter.get() == 0; + } + + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java new file mode 100644 index 000000000000..c927f9e449d5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java @@ -0,0 +1,172 @@ +/* + * 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +import java.util.function.Consumer; + +@SmallTest +@Presubmit +public class LetterboxConfigurationTest { + + private LetterboxConfiguration mLetterboxConfiguration; + private LetterboxConfigurationPersister mLetterboxConfigurationPersister; + + @Before + public void setUp() throws Exception { + Context context = getInstrumentation().getTargetContext(); + mLetterboxConfigurationPersister = mock(LetterboxConfigurationPersister.class); + mLetterboxConfiguration = new LetterboxConfiguration(context, + mLetterboxConfigurationPersister); + } + + @Test + public void test_whenReadingValues_storeIsInvoked() { + mLetterboxConfiguration.getLetterboxPositionForHorizontalReachability(); + verify(mLetterboxConfigurationPersister).getLetterboxPositionForHorizontalReachability(); + mLetterboxConfiguration.getLetterboxPositionForVerticalReachability(); + verify(mLetterboxConfigurationPersister).getLetterboxPositionForVerticalReachability(); + } + + @Test + public void test_whenSettingValues_updateConfigurationIsInvoked() { + mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop(); + verify(mLetterboxConfigurationPersister).setLetterboxPositionForHorizontalReachability( + anyInt()); + mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop(); + verify(mLetterboxConfigurationPersister).setLetterboxPositionForVerticalReachability( + anyInt()); + } + + @Test + public void test_whenMovedHorizontally_updatePositionAccordingly() { + // Starting from center + assertForHorizontalMove( + /* from */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, + /* expected */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + /* expectedTime */ 1, + LetterboxConfiguration::movePositionForHorizontalReachabilityToNextLeftStop); + assertForHorizontalMove( + /* from */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, + /* expected */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT, + /* expectedTime */ 1, + LetterboxConfiguration::movePositionForHorizontalReachabilityToNextRightStop); + // Starting from left + assertForHorizontalMove( + /* from */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + /* expected */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + /* expectedTime */ 2, + LetterboxConfiguration::movePositionForHorizontalReachabilityToNextLeftStop); + assertForHorizontalMove( + /* from */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + /* expected */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, + /* expectedTime */ 1, + LetterboxConfiguration::movePositionForHorizontalReachabilityToNextRightStop); + // Starting from right + assertForHorizontalMove( + /* from */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT, + /* expected */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT, + /* expectedTime */ 2, + LetterboxConfiguration::movePositionForHorizontalReachabilityToNextRightStop); + assertForHorizontalMove( + /* from */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT, + /* expected */ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, + /* expectedTime */ 2, + LetterboxConfiguration::movePositionForHorizontalReachabilityToNextLeftStop); + } + + @Test + public void test_whenMovedVertically_updatePositionAccordingly() { + // Starting from center + assertForVerticalMove( + /* from */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, + /* expected */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM, + /* expectedTime */ 1, + LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop); + assertForVerticalMove( + /* from */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, + /* expected */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + /* expectedTime */ 1, + LetterboxConfiguration::movePositionForVerticalReachabilityToNextTopStop); + // Starting from top + assertForVerticalMove( + /* from */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + /* expected */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, + /* expectedTime */ 1, + LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop); + assertForVerticalMove( + /* from */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + /* expected */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + /* expectedTime */ 2, + LetterboxConfiguration::movePositionForVerticalReachabilityToNextTopStop); + // Starting from bottom + assertForVerticalMove( + /* from */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM, + /* expected */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, + /* expectedTime */ 2, + LetterboxConfiguration::movePositionForVerticalReachabilityToNextTopStop); + assertForVerticalMove( + /* from */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM, + /* expected */ LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM, + /* expectedTime */ 2, + LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop); + } + + private void assertForHorizontalMove(int from, int expected, int expectedTime, + Consumer<LetterboxConfiguration> move) { + // We are in the current position + when(mLetterboxConfiguration.getLetterboxPositionForHorizontalReachability()) + .thenReturn(from); + move.accept(mLetterboxConfiguration); + verify(mLetterboxConfigurationPersister, + times(expectedTime)).setLetterboxPositionForHorizontalReachability( + expected); + } + + private void assertForVerticalMove(int from, int expected, int expectedTime, + Consumer<LetterboxConfiguration> move) { + // We are in the current position + when(mLetterboxConfiguration.getLetterboxPositionForVerticalReachability()) + .thenReturn(from); + move.accept(mLetterboxConfiguration); + verify(mLetterboxConfigurationPersister, + times(expectedTime)).setLetterboxPositionForVerticalReachability( + expected); + } +} |