Merge "Letterbox Reacheability Multiplier Persistence" into tm-qpr-dev

This commit is contained in:
Massimo Carli
2022-10-26 07:39:21 +00:00
committed by Android (Google) Code Review
6 changed files with 822 additions and 88 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -17,14 +17,17 @@
package com.android.server.wm; package com.android.server.wm;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import com.android.internal.R; import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.function.Function;
/** Reads letterbox configs from resources and controls their overrides at runtime. */ /** Reads letterbox configs from resources and controls their overrides at runtime. */
final class LetterboxConfiguration { final class LetterboxConfiguration {
@@ -156,34 +159,25 @@ final class LetterboxConfiguration {
// portrait device orientation. // portrait device orientation.
private boolean mIsVerticalReachabilityEnabled; 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. // Whether education is allowed for letterboxed fullscreen apps.
private boolean mIsEducationEnabled; private boolean mIsEducationEnabled;
// Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. // Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled; private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
// Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
@NonNull
private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
LetterboxConfiguration(Context systemUiContext) { LetterboxConfiguration(Context systemUiContext) {
this(systemUiContext, new LetterboxConfigurationPersister(systemUiContext,
() -> readLetterboxHorizontalReachabilityPositionFromConfig(systemUiContext),
() -> readLetterboxVerticalReachabilityPositionFromConfig(systemUiContext)));
}
@VisibleForTesting
LetterboxConfiguration(Context systemUiContext,
LetterboxConfigurationPersister letterboxConfigurationPersister) {
mContext = systemUiContext; mContext = systemUiContext;
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
R.dimen.config_fixedOrientationLetterboxAspectRatio); R.dimen.config_fixedOrientationLetterboxAspectRatio);
@@ -206,14 +200,14 @@ final class LetterboxConfiguration {
readLetterboxHorizontalReachabilityPositionFromConfig(mContext); readLetterboxHorizontalReachabilityPositionFromConfig(mContext);
mDefaultPositionForVerticalReachability = mDefaultPositionForVerticalReachability =
readLetterboxVerticalReachabilityPositionFromConfig(mContext); readLetterboxVerticalReachabilityPositionFromConfig(mContext);
mLetterboxPositionForHorizontalReachability = mDefaultPositionForHorizontalReachability;
mLetterboxPositionForVerticalReachability = mDefaultPositionForVerticalReachability;
mIsEducationEnabled = mContext.getResources().getBoolean( mIsEducationEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsEducationEnabled); R.bool.config_letterboxIsEducationEnabled);
setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat( setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat(
R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps)); R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean( mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled); 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. * <p>The position multiplier is changed after each double tap in the letterbox area.
*/ */
float getHorizontalMultiplierForReachability() { float getHorizontalMultiplierForReachability() {
switch (mLetterboxPositionForHorizontalReachability) { final int letterboxPositionForHorizontalReachability =
mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability();
switch (letterboxPositionForHorizontalReachability) {
case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
return 0.0f; return 0.0f;
case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
@@ -662,10 +658,11 @@ final class LetterboxConfiguration {
return 1.0f; return 1.0f;
default: default:
throw new AssertionError( throw new AssertionError(
"Unexpected letterbox position type: " "Unexpected letterbox position type: "
+ mLetterboxPositionForHorizontalReachability); + letterboxPositionForHorizontalReachability);
} }
} }
/* /*
* Gets vertical position of a center of the letterboxed app window when reachability * 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. * 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. * <p>The position multiplier is changed after each double tap in the letterbox area.
*/ */
float getVerticalMultiplierForReachability() { float getVerticalMultiplierForReachability() {
switch (mLetterboxPositionForVerticalReachability) { final int letterboxPositionForVerticalReachability =
mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability();
switch (letterboxPositionForVerticalReachability) {
case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
return 0.0f; return 0.0f;
case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
@@ -683,7 +682,7 @@ final class LetterboxConfiguration {
default: default:
throw new AssertionError( throw new AssertionError(
"Unexpected letterbox position type: " "Unexpected letterbox position type: "
+ mLetterboxPositionForVerticalReachability); + letterboxPositionForVerticalReachability);
} }
} }
@@ -693,7 +692,7 @@ final class LetterboxConfiguration {
*/ */
@LetterboxHorizontalReachabilityPosition @LetterboxHorizontalReachabilityPosition
int getLetterboxPositionForHorizontalReachability() { int getLetterboxPositionForHorizontalReachability() {
return mLetterboxPositionForHorizontalReachability; return mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability();
} }
/* /*
@@ -702,7 +701,7 @@ final class LetterboxConfiguration {
*/ */
@LetterboxVerticalReachabilityPosition @LetterboxVerticalReachabilityPosition
int getLetterboxPositionForVerticalReachability() { int getLetterboxPositionForVerticalReachability() {
return mLetterboxPositionForVerticalReachability; return mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability();
} }
/** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */ /** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */
@@ -742,9 +741,8 @@ final class LetterboxConfiguration {
* right side. * right side.
*/ */
void movePositionForHorizontalReachabilityToNextRightStop() { void movePositionForHorizontalReachabilityToNextRightStop() {
mLetterboxPositionForHorizontalReachability = Math.min( updatePositionForHorizontalReachability(prev -> Math.min(
mLetterboxPositionForHorizontalReachability + 1, prev + 1, LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT));
LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT);
} }
/** /**
@@ -752,8 +750,7 @@ final class LetterboxConfiguration {
* side. * side.
*/ */
void movePositionForHorizontalReachabilityToNextLeftStop() { void movePositionForHorizontalReachabilityToNextLeftStop() {
mLetterboxPositionForHorizontalReachability = updatePositionForHorizontalReachability(prev -> Math.max(prev - 1, 0));
Math.max(mLetterboxPositionForHorizontalReachability - 1, 0);
} }
/** /**
@@ -761,9 +758,8 @@ final class LetterboxConfiguration {
* side. * side.
*/ */
void movePositionForVerticalReachabilityToNextBottomStop() { void movePositionForVerticalReachabilityToNextBottomStop() {
mLetterboxPositionForVerticalReachability = Math.min( updatePositionForVerticalReachability(prev -> Math.min(
mLetterboxPositionForVerticalReachability + 1, prev + 1, LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM));
LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM);
} }
/** /**
@@ -771,8 +767,7 @@ final class LetterboxConfiguration {
* side. * side.
*/ */
void movePositionForVerticalReachabilityToNextTopStop() { void movePositionForVerticalReachabilityToNextTopStop() {
mLetterboxPositionForVerticalReachability = updatePositionForVerticalReachability(prev -> Math.max(prev - 1, 0));
Math.max(mLetterboxPositionForVerticalReachability - 1, 0);
} }
/** /**
@@ -822,4 +817,26 @@ final class LetterboxConfiguration {
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled); 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);
}
} }

View File

@@ -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");
}
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}