1 /*
2  * Copyright (C) 2023 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.wallpaper;
18 
19 import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
20 import static android.app.WallpaperManager.getRotatedOrientation;
21 import static android.view.Display.DEFAULT_DISPLAY;
22 
23 import static com.android.window.flags.Flags.multiCrop;
24 
25 import android.app.WallpaperManager;
26 import android.graphics.Point;
27 import android.graphics.Rect;
28 import android.hardware.display.DisplayManager;
29 import android.os.Binder;
30 import android.os.Debug;
31 import android.util.Pair;
32 import android.util.Slog;
33 import android.util.SparseArray;
34 import android.view.Display;
35 import android.view.DisplayInfo;
36 import android.view.WindowManager;
37 import android.view.WindowMetrics;
38 
39 import com.android.server.wm.WindowManagerInternal;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Set;
44 import java.util.function.Consumer;
45 
46 /**
47  * Internal class used to store all the display data relevant to the wallpapers
48  */
49 class WallpaperDisplayHelper {
50 
51     static final class DisplayData {
52         int mWidth = -1;
53         int mHeight = -1;
54         final Rect mPadding = new Rect(0, 0, 0, 0);
55         final int mDisplayId;
DisplayData(int displayId)56         DisplayData(int displayId) {
57             mDisplayId = displayId;
58         }
59     }
60 
61     private static final String TAG = WallpaperDisplayHelper.class.getSimpleName();
62     private static final float LARGE_SCREEN_MIN_DP = 600f;
63 
64     private final SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
65     private final DisplayManager mDisplayManager;
66     private final WindowManagerInternal mWindowManagerInternal;
67     private final SparseArray<Point> mDefaultDisplaySizes = new SparseArray<>();
68 
69     // related orientations pairs for foldable (folded orientation, unfolded orientation)
70     private final List<Pair<Integer, Integer>> mFoldableOrientationPairs = new ArrayList<>();
71 
72     private final boolean mIsFoldable;
73     private boolean mIsLargeScreen = false;
74 
WallpaperDisplayHelper( DisplayManager displayManager, WindowManager windowManager, WindowManagerInternal windowManagerInternal, boolean isFoldable)75     WallpaperDisplayHelper(
76             DisplayManager displayManager,
77             WindowManager windowManager,
78             WindowManagerInternal windowManagerInternal,
79             boolean isFoldable) {
80         mDisplayManager = displayManager;
81         mWindowManagerInternal = windowManagerInternal;
82         mIsFoldable = isFoldable;
83         if (!multiCrop()) return;
84         Set<WindowMetrics> metrics = windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
85         boolean populateOrientationPairs = isFoldable && metrics.size() == 2;
86         float surface = 0;
87         int firstOrientation = -1;
88         for (WindowMetrics metric: metrics) {
89             Rect bounds = metric.getBounds();
90             Point displaySize = new Point(bounds.width(), bounds.height());
91             Point reversedDisplaySize = new Point(displaySize.y, displaySize.x);
92             for (Point point : List.of(displaySize, reversedDisplaySize)) {
93                 int orientation = WallpaperManager.getOrientation(point);
94                 // don't add an entry if there is already a larger display of the same orientation
95                 Point display = mDefaultDisplaySizes.get(orientation);
96                 if (display == null || display.x * display.y < point.x * point.y) {
97                     mDefaultDisplaySizes.put(orientation, point);
98                 }
99             }
100 
101             mIsLargeScreen |= (displaySize.x / metric.getDensity() >= LARGE_SCREEN_MIN_DP);
102 
103             if (populateOrientationPairs) {
104                 int orientation = WallpaperManager.getOrientation(displaySize);
105                 float newSurface = displaySize.x * displaySize.y
106                         / (metric.getDensity() * metric.getDensity());
107                 if (surface <= 0) {
108                     surface = newSurface;
109                     firstOrientation = orientation;
110                 } else {
111                     Pair<Integer, Integer> pair = (newSurface > surface)
112                             ? new Pair<>(firstOrientation, orientation)
113                             : new Pair<>(orientation, firstOrientation);
114                     Pair<Integer, Integer> rotatedPair = new Pair<>(
115                             getRotatedOrientation(pair.first), getRotatedOrientation(pair.second));
116                     mFoldableOrientationPairs.add(pair);
117                     mFoldableOrientationPairs.add(rotatedPair);
118                 }
119             }
120         }
121     }
122 
getDisplayDataOrCreate(int displayId)123     DisplayData getDisplayDataOrCreate(int displayId) {
124         DisplayData wpdData = mDisplayDatas.get(displayId);
125         if (wpdData == null) {
126             wpdData = new DisplayData(displayId);
127             ensureSaneWallpaperDisplaySize(wpdData, displayId);
128             mDisplayDatas.append(displayId, wpdData);
129         }
130         return wpdData;
131     }
132 
getDefaultDisplayCurrentOrientation()133     int getDefaultDisplayCurrentOrientation() {
134         Point displaySize = new Point();
135         mDisplayManager.getDisplay(DEFAULT_DISPLAY).getSize(displaySize);
136         return WallpaperManager.getOrientation(displaySize);
137     }
138 
removeDisplayData(int displayId)139     void removeDisplayData(int displayId) {
140         mDisplayDatas.remove(displayId);
141     }
142 
ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId)143     void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
144         // We always want to have some reasonable width hint.
145         final int baseSize = getMaximumSizeDimension(displayId);
146         if (wpdData.mWidth < baseSize) {
147             wpdData.mWidth = baseSize;
148         }
149         if (wpdData.mHeight < baseSize) {
150             wpdData.mHeight = baseSize;
151         }
152     }
153 
getMaximumSizeDimension(int displayId)154     int getMaximumSizeDimension(int displayId) {
155         Display display = mDisplayManager.getDisplay(displayId);
156         if (display == null) {
157             Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
158             display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
159         }
160         return display.getMaximumSizeDimension();
161     }
162 
forEachDisplayData(Consumer<DisplayData> action)163     void forEachDisplayData(Consumer<DisplayData> action) {
164         for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
165             final DisplayData wpdData = mDisplayDatas.valueAt(i);
166             action.accept(wpdData);
167         }
168     }
169 
getDisplays()170     Display[] getDisplays() {
171         return mDisplayManager.getDisplays();
172     }
173 
getDisplayInfo(int displayId)174     DisplayInfo getDisplayInfo(int displayId) {
175         final DisplayInfo displayInfo = new DisplayInfo();
176         mDisplayManager.getDisplay(displayId).getDisplayInfo(displayInfo);
177         return displayInfo;
178     }
179 
isUsableDisplay(int displayId, int clientUid)180     boolean isUsableDisplay(int displayId, int clientUid) {
181         return isUsableDisplay(mDisplayManager.getDisplay(displayId), clientUid);
182     }
183 
isUsableDisplay(Display display, int clientUid)184     boolean isUsableDisplay(Display display, int clientUid) {
185         if (display == null || !display.hasAccess(clientUid)) {
186             return false;
187         }
188         final int displayId = display.getDisplayId();
189         if (displayId == DEFAULT_DISPLAY) {
190             return true;
191         }
192 
193         final long ident = Binder.clearCallingIdentity();
194         try {
195             return mWindowManagerInternal.isHomeSupportedOnDisplay(displayId);
196         } finally {
197             Binder.restoreCallingIdentity(ident);
198         }
199     }
200 
isValidDisplay(int displayId)201     boolean isValidDisplay(int displayId) {
202         return mDisplayManager.getDisplay(displayId) != null;
203     }
204 
getDefaultDisplaySizes()205     SparseArray<Point> getDefaultDisplaySizes() {
206         return mDefaultDisplaySizes;
207     }
208 
209     /** Return the number of pixel of the largest dimension of the default display */
getDefaultDisplayLargestDimension()210     int getDefaultDisplayLargestDimension() {
211         int result = -1;
212         for (int i = 0; i < mDefaultDisplaySizes.size(); i++) {
213             Point size = mDefaultDisplaySizes.valueAt(i);
214             result = Math.max(result, Math.max(size.x, size.y));
215         }
216         return result;
217     }
218 
isFoldable()219     boolean isFoldable() {
220         return mIsFoldable;
221     }
222 
223     /**
224      * Return true if any of the screens of the default display is considered large (DP >= 600)
225      */
isLargeScreen()226     boolean isLargeScreen() {
227         return mIsLargeScreen;
228     }
229 
230     /**
231      * If a given orientation corresponds to an unfolded orientation on foldable, return the
232      * corresponding folded orientation. Otherwise, return UNKNOWN. Always return UNKNOWN if the
233      * device is not a foldable.
234      */
getFoldedOrientation(int orientation)235     int getFoldedOrientation(int orientation) {
236         for (Pair<Integer, Integer> pair : mFoldableOrientationPairs) {
237             if (pair.second.equals(orientation)) return pair.first;
238         }
239         return ORIENTATION_UNKNOWN;
240     }
241 
242     /**
243      * If a given orientation corresponds to a folded orientation on foldable, return the
244      * corresponding unfolded orientation. Otherwise, return UNKNOWN. Always return UNKNOWN if the
245      * device is not a foldable.
246      */
getUnfoldedOrientation(int orientation)247     int getUnfoldedOrientation(int orientation) {
248         for (Pair<Integer, Integer> pair : mFoldableOrientationPairs) {
249             if (pair.first.equals(orientation)) return pair.second;
250         }
251         return ORIENTATION_UNKNOWN;
252     }
253 }
254