• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import android.annotation.NonNull;
20 import android.provider.DeviceConfig;
21 import android.util.ArraySet;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 
25 import java.util.Map;
26 import java.util.concurrent.Executor;
27 
28 /**
29  * Utility class that caches {@link DeviceConfig} flags for app compat features and listens
30  * to updates by implementing {@link DeviceConfig.OnPropertiesChangedListener}.
31  */
32 final class LetterboxConfigurationDeviceConfig
33         implements DeviceConfig.OnPropertiesChangedListener {
34 
35     static final String KEY_ENABLE_CAMERA_COMPAT_TREATMENT = "enable_compat_camera_treatment";
36     private static final boolean DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT = true;
37 
38     static final String KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY =
39             "enable_display_rotation_immersive_app_compat_policy";
40     private static final boolean DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY =
41             true;
42 
43     static final String KEY_ALLOW_IGNORE_ORIENTATION_REQUEST =
44             "allow_ignore_orientation_request";
45     private static final boolean DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST = true;
46 
47     static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus";
48     private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true;
49 
50     static final String KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY =
51             "enable_letterbox_translucent_activity";
52 
53     private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true;
54 
55     static final String KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP =
56             "disable_size_compat_mode_after_orientation_change_from_app";
57     private static final boolean
58             DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP = true;
59 
60     @VisibleForTesting
61     static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
62             KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
63             DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT,
64             KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
65             DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
66             KEY_ALLOW_IGNORE_ORIENTATION_REQUEST,
67             DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST,
68             KEY_ENABLE_COMPAT_FAKE_FOCUS,
69             DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS,
70             KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
71             DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
72             KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP,
73             DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP
74     );
75 
76     // Whether camera compatibility treatment is enabled.
77     // See DisplayRotationCompatPolicy for context.
78     private boolean mIsCameraCompatTreatmentEnabled =
79             DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT;
80 
81     // Whether enabling rotation compat policy for immersive apps that prevents auto rotation
82     // into non-optimal screen orientation while in fullscreen. This is needed because immersive
83     // apps, such as games, are often not optimized for all orientations and can have a poor UX
84     // when rotated. Additionally, some games rely on sensors for the gameplay so users can trigger
85     // such rotations accidentally when auto rotation is on.
86     private boolean mIsDisplayRotationImmersiveAppCompatPolicyEnabled =
87             DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
88 
89     // Whether enabling ignoreOrientationRequest is allowed on the device.
90     private boolean mIsAllowIgnoreOrientationRequest =
91             DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST;
92 
93     // Whether sending compat fake focus for split screen resumed activities is enabled. This is
94     // needed because some game engines wait to get focus before drawing the content of the app
95     // which isn't guaranteed by default in multi-window modes.
96     private boolean mIsCompatFakeFocusAllowed = DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS;
97 
98     // Whether the letterbox strategy for transparent activities is allowed
99     private boolean mIsTranslucentLetterboxingAllowed =
100             DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY;
101 
102     // Whether size compat mode is disabled after an orientation change request comes from the app
103     private boolean mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
104             DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
105 
106     // Set of active device configs that need to be updated in
107     // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
108     private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
109 
LetterboxConfigurationDeviceConfig(@onNull final Executor executor)110     LetterboxConfigurationDeviceConfig(@NonNull final Executor executor) {
111         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
112                 executor, /* onPropertiesChangedListener */ this);
113     }
114 
115     @Override
onPropertiesChanged(@onNull final DeviceConfig.Properties properties)116     public void onPropertiesChanged(@NonNull final DeviceConfig.Properties properties) {
117         for (int i = mActiveDeviceConfigsSet.size() - 1; i >= 0; i--) {
118             String key = mActiveDeviceConfigsSet.valueAt(i);
119             // Reads the new configuration, if the device config properties contain the key.
120             if (properties.getKeyset().contains(key)) {
121                 readAndSaveValueFromDeviceConfig(key);
122             }
123         }
124     }
125 
126     /**
127      * Adds {@code key} to a set of flags that can be updated from the server if
128      * {@code isActive} is {@code true} and read it's current value from {@link DeviceConfig}.
129      */
updateFlagActiveStatus(boolean isActive, String key)130     void updateFlagActiveStatus(boolean isActive, String key) {
131         if (!isActive) {
132             return;
133         }
134         mActiveDeviceConfigsSet.add(key);
135         readAndSaveValueFromDeviceConfig(key);
136     }
137 
138     /**
139      * Returns values of the {@code key} flag.
140      *
141      * @throws AssertionError {@code key} isn't recognised.
142      */
getFlag(String key)143     boolean getFlag(String key) {
144         switch (key) {
145             case KEY_ENABLE_CAMERA_COMPAT_TREATMENT:
146                 return mIsCameraCompatTreatmentEnabled;
147             case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY:
148                 return mIsDisplayRotationImmersiveAppCompatPolicyEnabled;
149             case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST:
150                 return mIsAllowIgnoreOrientationRequest;
151             case KEY_ENABLE_COMPAT_FAKE_FOCUS:
152                 return mIsCompatFakeFocusAllowed;
153             case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
154                 return mIsTranslucentLetterboxingAllowed;
155             case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
156                 return mIsSizeCompatModeDisabledAfterOrientationChangeFromApp;
157             default:
158                 throw new AssertionError("Unexpected flag name: " + key);
159         }
160     }
161 
readAndSaveValueFromDeviceConfig(String key)162     private void readAndSaveValueFromDeviceConfig(String key) {
163         Boolean defaultValue = sKeyToDefaultValueMap.get(key);
164         if (defaultValue == null) {
165             throw new AssertionError("Haven't found default value for flag: " + key);
166         }
167         switch (key) {
168             case KEY_ENABLE_CAMERA_COMPAT_TREATMENT:
169                 mIsCameraCompatTreatmentEnabled = getDeviceConfig(key, defaultValue);
170                 break;
171             case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY:
172                 mIsDisplayRotationImmersiveAppCompatPolicyEnabled =
173                         getDeviceConfig(key, defaultValue);
174                 break;
175             case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST:
176                 mIsAllowIgnoreOrientationRequest = getDeviceConfig(key, defaultValue);
177                 break;
178             case KEY_ENABLE_COMPAT_FAKE_FOCUS:
179                 mIsCompatFakeFocusAllowed = getDeviceConfig(key, defaultValue);
180                 break;
181             case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
182                 mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue);
183                 break;
184             case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
185                 mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
186                         getDeviceConfig(key, defaultValue);
187                 break;
188             default:
189                 throw new AssertionError("Unexpected flag name: " + key);
190         }
191     }
192 
getDeviceConfig(String key, boolean defaultValue)193     private boolean getDeviceConfig(String key, boolean defaultValue) {
194         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
195                 key, defaultValue);
196     }
197 }
198