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