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