• 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 static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21 
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.os.Environment;
26 import android.util.AtomicFile;
27 import android.util.Slog;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.server.wm.AppCompatConfiguration.LetterboxHorizontalReachabilityPosition;
31 import com.android.server.wm.AppCompatConfiguration.LetterboxVerticalReachabilityPosition;
32 import com.android.server.wm.nano.WindowManagerProtos;
33 
34 import java.io.ByteArrayOutputStream;
35 import java.io.File;
36 import java.io.FileInputStream;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.util.function.Consumer;
41 import java.util.function.Supplier;
42 
43 /**
44  * Persists the values of letterboxPositionForHorizontalReachability and
45  * letterboxPositionForVerticalReachability for {@link AppCompatConfiguration}.
46  */
47 class AppCompatConfigurationPersister {
48 
49     private static final String TAG =
50             TAG_WITH_CLASS_NAME ? "AppCompatConfigurationPersister" : TAG_WM;
51 
52     private static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
53 
54     private final Supplier<Integer> mDefaultHorizontalReachabilitySupplier;
55     private final Supplier<Integer> mDefaultVerticalReachabilitySupplier;
56     private final Supplier<Integer> mDefaultBookModeReachabilitySupplier;
57     private final Supplier<Integer> mDefaultTabletopModeReachabilitySupplier;
58 
59     // Horizontal position of a center of the letterboxed app window which is global to prevent
60     // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
61     // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
62     // LetterboxUiController#getHorizontalPositionMultiplier which is called from
63     // ActivityRecord#updateResolvedBoundsPosition.
64     @LetterboxHorizontalReachabilityPosition
65     private volatile int mLetterboxPositionForHorizontalReachability;
66 
67     // The same as mLetterboxPositionForHorizontalReachability but used when the device is
68     // half-folded.
69     @LetterboxHorizontalReachabilityPosition
70     private volatile int mLetterboxPositionForBookModeReachability;
71 
72     // Vertical position of a center of the letterboxed app window which is global to prevent
73     // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
74     // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
75     // LetterboxUiController#getVerticalPositionMultiplier which is called from
76     // ActivityRecord#updateResolvedBoundsPosition.
77     @LetterboxVerticalReachabilityPosition
78     private volatile int mLetterboxPositionForVerticalReachability;
79 
80     // The same as mLetterboxPositionForVerticalReachability but used when the device is
81     // half-folded.
82     @LetterboxVerticalReachabilityPosition
83     private volatile int mLetterboxPositionForTabletopModeReachability;
84 
85     @NonNull
86     private final AtomicFile mConfigurationFile;
87 
88     @Nullable
89     private final Consumer<String> mCompletionCallback;
90 
91     @NonNull
92     private final PersisterQueue mPersisterQueue;
93 
AppCompatConfigurationPersister( @onNull Supplier<Integer> defaultHorizontalReachabilitySupplier, @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier, @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier, @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier)94     AppCompatConfigurationPersister(
95             @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
96             @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
97             @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
98             @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
99         this(defaultHorizontalReachabilitySupplier, defaultVerticalReachabilitySupplier,
100                 defaultBookModeReachabilitySupplier, defaultTabletopModeReachabilitySupplier,
101                 Environment.getDataSystemDirectory(), new PersisterQueue(),
102                 /* completionCallback */ null, LETTERBOX_CONFIGURATION_FILENAME);
103     }
104 
105     @VisibleForTesting
AppCompatConfigurationPersister( @onNull Supplier<Integer> defaultHorizontalReachabilitySupplier, @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier, @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier, @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier, @NonNull File configFolder, @NonNull PersisterQueue persisterQueue, @Nullable Consumer<String> completionCallback, @NonNull String letterboxConfigurationFileName)106     AppCompatConfigurationPersister(
107             @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
108             @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
109             @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
110             @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier,
111             @NonNull File configFolder, @NonNull PersisterQueue persisterQueue,
112             @Nullable Consumer<String> completionCallback,
113             @NonNull String letterboxConfigurationFileName) {
114         mDefaultHorizontalReachabilitySupplier = defaultHorizontalReachabilitySupplier;
115         mDefaultVerticalReachabilitySupplier = defaultVerticalReachabilitySupplier;
116         mDefaultBookModeReachabilitySupplier = defaultBookModeReachabilitySupplier;
117         mDefaultTabletopModeReachabilitySupplier = defaultTabletopModeReachabilitySupplier;
118         mCompletionCallback = completionCallback;
119         final File prefFiles = new File(configFolder, letterboxConfigurationFileName);
120         mConfigurationFile = new AtomicFile(prefFiles);
121         mPersisterQueue = persisterQueue;
122         readCurrentConfiguration();
123     }
124 
125     /**
126      * Startes the persistence queue
127      */
start()128     void start() {
129         mPersisterQueue.startPersisting();
130     }
131 
132     /*
133      * Gets the horizontal position of the letterboxed app window when horizontal reachability is
134      * enabled.
135      */
136     @LetterboxHorizontalReachabilityPosition
getLetterboxPositionForHorizontalReachability(boolean forBookMode)137     int getLetterboxPositionForHorizontalReachability(boolean forBookMode) {
138         if (forBookMode) {
139             return mLetterboxPositionForBookModeReachability;
140         } else {
141             return mLetterboxPositionForHorizontalReachability;
142         }
143     }
144 
145     /*
146      * Gets the vertical position of the letterboxed app window when vertical reachability is
147      * enabled.
148      */
149     @LetterboxVerticalReachabilityPosition
getLetterboxPositionForVerticalReachability(boolean forTabletopMode)150     int getLetterboxPositionForVerticalReachability(boolean forTabletopMode) {
151         if (forTabletopMode) {
152             return mLetterboxPositionForTabletopModeReachability;
153         } else {
154             return mLetterboxPositionForVerticalReachability;
155         }
156     }
157 
158     /**
159      * Updates letterboxPositionForVerticalReachability if different from the current value
160      */
setLetterboxPositionForHorizontalReachability(boolean forBookMode, int letterboxPositionForHorizontalReachability)161     void setLetterboxPositionForHorizontalReachability(boolean forBookMode,
162             int letterboxPositionForHorizontalReachability) {
163         if (forBookMode) {
164             if (mLetterboxPositionForBookModeReachability
165                     != letterboxPositionForHorizontalReachability) {
166                 mLetterboxPositionForBookModeReachability =
167                         letterboxPositionForHorizontalReachability;
168                 updateConfiguration();
169             }
170         } else {
171             if (mLetterboxPositionForHorizontalReachability
172                     != letterboxPositionForHorizontalReachability) {
173                 mLetterboxPositionForHorizontalReachability =
174                         letterboxPositionForHorizontalReachability;
175                 updateConfiguration();
176             }
177         }
178     }
179 
180     /**
181      * Updates letterboxPositionForVerticalReachability if different from the current value
182      */
setLetterboxPositionForVerticalReachability(boolean forTabletopMode, int letterboxPositionForVerticalReachability)183     void setLetterboxPositionForVerticalReachability(boolean forTabletopMode,
184             int letterboxPositionForVerticalReachability) {
185         if (forTabletopMode) {
186             if (mLetterboxPositionForTabletopModeReachability
187                     != letterboxPositionForVerticalReachability) {
188                 mLetterboxPositionForTabletopModeReachability =
189                         letterboxPositionForVerticalReachability;
190                 updateConfiguration();
191             }
192         } else {
193             if (mLetterboxPositionForVerticalReachability
194                     != letterboxPositionForVerticalReachability) {
195                 mLetterboxPositionForVerticalReachability =
196                         letterboxPositionForVerticalReachability;
197                 updateConfiguration();
198             }
199         }
200     }
201 
202     @VisibleForTesting
useDefaultValue()203     void useDefaultValue() {
204         mLetterboxPositionForHorizontalReachability = mDefaultHorizontalReachabilitySupplier.get();
205         mLetterboxPositionForVerticalReachability = mDefaultVerticalReachabilitySupplier.get();
206         mLetterboxPositionForBookModeReachability =
207                 mDefaultBookModeReachabilitySupplier.get();
208         mLetterboxPositionForTabletopModeReachability =
209                 mDefaultTabletopModeReachabilitySupplier.get();
210     }
211 
212     @MainThread
readCurrentConfiguration()213     private void readCurrentConfiguration() {
214         if (!mConfigurationFile.exists()) {
215             useDefaultValue();
216             return;
217         }
218         FileInputStream fis = null;
219         try {
220             fis = mConfigurationFile.openRead();
221             byte[] protoData = readInputStream(fis);
222             final WindowManagerProtos.LetterboxProto letterboxData =
223                     WindowManagerProtos.LetterboxProto.parseFrom(protoData);
224             mLetterboxPositionForHorizontalReachability =
225                     letterboxData.letterboxPositionForHorizontalReachability;
226             mLetterboxPositionForVerticalReachability =
227                     letterboxData.letterboxPositionForVerticalReachability;
228             mLetterboxPositionForBookModeReachability =
229                     letterboxData.letterboxPositionForBookModeReachability;
230             mLetterboxPositionForTabletopModeReachability =
231                     letterboxData.letterboxPositionForTabletopModeReachability;
232         } catch (IOException ioe) {
233             Slog.e(TAG,
234                     "Error reading from AppCompatConfigurationPersister. "
235                             + "Using default values!", ioe);
236             useDefaultValue();
237         } finally {
238             if (fis != null) {
239                 try {
240                     fis.close();
241                 } catch (IOException e) {
242                     useDefaultValue();
243                     Slog.e(TAG, "Error reading from AppCompatConfigurationPersister ", e);
244                 }
245             }
246         }
247     }
248 
updateConfiguration()249     private void updateConfiguration() {
250         mPersisterQueue.addItem(new UpdateValuesCommand(mConfigurationFile,
251                 mLetterboxPositionForHorizontalReachability,
252                 mLetterboxPositionForVerticalReachability,
253                 mLetterboxPositionForBookModeReachability,
254                 mLetterboxPositionForTabletopModeReachability,
255                 mCompletionCallback), /* flush */ true);
256     }
257 
readInputStream(InputStream in)258     private static byte[] readInputStream(InputStream in) throws IOException {
259         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
260         try {
261             byte[] buffer = new byte[1024];
262             int size = in.read(buffer);
263             while (size > 0) {
264                 outputStream.write(buffer, 0, size);
265                 size = in.read(buffer);
266             }
267             return outputStream.toByteArray();
268         } finally {
269             outputStream.close();
270         }
271     }
272 
273     private static class UpdateValuesCommand implements
274             PersisterQueue.WriteQueueItem<UpdateValuesCommand> {
275 
276         @NonNull
277         private final AtomicFile mFileToUpdate;
278         @Nullable
279         private final Consumer<String> mOnComplete;
280 
281 
282         private final int mHorizontalReachability;
283         private final int mVerticalReachability;
284         private final int mBookModeReachability;
285         private final int mTabletopModeReachability;
286 
UpdateValuesCommand(@onNull AtomicFile fileToUpdate, int horizontalReachability, int verticalReachability, int bookModeReachability, int tabletopModeReachability, @Nullable Consumer<String> onComplete)287         UpdateValuesCommand(@NonNull AtomicFile fileToUpdate,
288                 int horizontalReachability, int verticalReachability,
289                 int bookModeReachability, int tabletopModeReachability,
290                 @Nullable Consumer<String> onComplete) {
291             mFileToUpdate = fileToUpdate;
292             mHorizontalReachability = horizontalReachability;
293             mVerticalReachability = verticalReachability;
294             mBookModeReachability = bookModeReachability;
295             mTabletopModeReachability = tabletopModeReachability;
296             mOnComplete = onComplete;
297         }
298 
299         @Override
process()300         public void process() {
301             final WindowManagerProtos.LetterboxProto letterboxData =
302                     new WindowManagerProtos.LetterboxProto();
303             letterboxData.letterboxPositionForHorizontalReachability = mHorizontalReachability;
304             letterboxData.letterboxPositionForVerticalReachability = mVerticalReachability;
305             letterboxData.letterboxPositionForBookModeReachability =
306                     mBookModeReachability;
307             letterboxData.letterboxPositionForTabletopModeReachability =
308                     mTabletopModeReachability;
309             final byte[] bytes = WindowManagerProtos.LetterboxProto.toByteArray(letterboxData);
310 
311             FileOutputStream fos = null;
312             try {
313                 fos = mFileToUpdate.startWrite();
314                 fos.write(bytes);
315                 mFileToUpdate.finishWrite(fos);
316             } catch (IOException ioe) {
317                 mFileToUpdate.failWrite(fos);
318                 Slog.e(TAG,
319                         "Error writing to AppCompatConfigurationPersister. "
320                                 + "Using default values!", ioe);
321             } finally {
322                 if (mOnComplete != null) {
323                     mOnComplete.accept("UpdateValuesCommand");
324                 }
325             }
326         }
327     }
328 }
329