1 /* 2 * Copyright (C) 2019 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.policy; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.graphics.Rect; 22 import android.hardware.ICameraService; 23 import android.hardware.devicestate.DeviceStateManager; 24 import android.hardware.devicestate.DeviceStateManager.FoldStateListener; 25 import android.hardware.display.DisplayManagerInternal; 26 import android.os.Handler; 27 import android.os.HandlerExecutor; 28 import android.os.RemoteCallbackList; 29 import android.os.RemoteException; 30 import android.util.Slog; 31 import android.view.DisplayInfo; 32 import android.view.IDisplayFoldListener; 33 34 import com.android.server.DisplayThread; 35 import com.android.server.LocalServices; 36 import com.android.server.camera.CameraServiceProxy; 37 import com.android.server.wm.WindowManagerInternal; 38 39 /** 40 * Controls the behavior of foldable devices whose screen can literally bend and fold. 41 * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy. 42 */ 43 class DisplayFoldController { 44 private static final String TAG = "DisplayFoldController"; 45 46 private final WindowManagerInternal mWindowManagerInternal; 47 private final DisplayManagerInternal mDisplayManagerInternal; 48 // Camera service proxy can be disabled through a config. 49 @Nullable 50 private final CameraServiceProxy mCameraServiceProxy; 51 private final int mDisplayId; 52 private final Handler mHandler; 53 54 /** The display area while device is folded. */ 55 private final Rect mFoldedArea; 56 /** The display area to override the original folded area. */ 57 private Rect mOverrideFoldedArea = new Rect(); 58 59 private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); 60 private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>(); 61 private Boolean mFolded; 62 private String mFocusedApp; 63 private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger(); 64 DisplayFoldController( Context context, WindowManagerInternal windowManagerInternal, DisplayManagerInternal displayManagerInternal, @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea, Handler handler)65 DisplayFoldController( 66 Context context, WindowManagerInternal windowManagerInternal, 67 DisplayManagerInternal displayManagerInternal, 68 @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea, 69 Handler handler) { 70 mWindowManagerInternal = windowManagerInternal; 71 mDisplayManagerInternal = displayManagerInternal; 72 mCameraServiceProxy = cameraServiceProxy; 73 mDisplayId = displayId; 74 mFoldedArea = new Rect(foldedArea); 75 mHandler = handler; 76 77 DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class); 78 deviceStateManager.registerCallback(new HandlerExecutor(handler), 79 new FoldStateListener(context, folded -> setDeviceFolded(folded))); 80 } 81 finishedGoingToSleep()82 void finishedGoingToSleep() { 83 mDurationLogger.onFinishedGoingToSleep(); 84 } 85 finishedWakingUp()86 void finishedWakingUp() { 87 mDurationLogger.onFinishedWakingUp(mFolded); 88 } 89 setDeviceFolded(boolean folded)90 private void setDeviceFolded(boolean folded) { 91 if (mFolded != null && mFolded == folded) { 92 return; 93 } 94 95 final Rect foldedArea; 96 if (!mOverrideFoldedArea.isEmpty()) { 97 foldedArea = mOverrideFoldedArea; 98 } else if (!mFoldedArea.isEmpty()) { 99 foldedArea = mFoldedArea; 100 } else { 101 foldedArea = null; 102 } 103 104 // Only do display scaling/cropping if it has been configured to do so 105 if (foldedArea != null) { 106 if (folded) { 107 108 mDisplayManagerInternal.getNonOverrideDisplayInfo( 109 mDisplayId, mNonOverrideDisplayInfo); 110 final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2 111 - foldedArea.left; 112 final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2 113 - foldedArea.top; 114 115 // Bypass scaling otherwise LogicalDisplay will scale contents by default. 116 mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, true); 117 mWindowManagerInternal.setForcedDisplaySize(mDisplayId, 118 foldedArea.width(), foldedArea.height()); 119 mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); 120 } else { 121 mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, false); 122 mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); 123 mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0); 124 } 125 } 126 127 if (mCameraServiceProxy != null) { 128 if (folded) { 129 mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); 130 } else { 131 mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); 132 } 133 } else { 134 Slog.w(TAG, "Camera service unavailable to toggle folded state."); 135 } 136 137 mDurationLogger.setDeviceFolded(folded); 138 mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp); 139 mFolded = folded; 140 141 final int n = mListeners.beginBroadcast(); 142 for (int i = 0; i < n; i++) { 143 try { 144 mListeners.getBroadcastItem(i).onDisplayFoldChanged(mDisplayId, folded); 145 } catch (RemoteException e) { 146 // Listener died. 147 } 148 } 149 mListeners.finishBroadcast(); 150 } 151 registerDisplayFoldListener(IDisplayFoldListener listener)152 void registerDisplayFoldListener(IDisplayFoldListener listener) { 153 mListeners.register(listener); 154 if (mFolded == null) { 155 return; 156 } 157 mHandler.post(() -> { 158 try { 159 listener.onDisplayFoldChanged(mDisplayId, mFolded); 160 } catch (RemoteException e) { 161 // Listener died. 162 } 163 }); 164 } 165 unregisterDisplayFoldListener(IDisplayFoldListener listener)166 void unregisterDisplayFoldListener(IDisplayFoldListener listener) { 167 mListeners.unregister(listener); 168 } 169 setOverrideFoldedArea(Rect area)170 void setOverrideFoldedArea(Rect area) { 171 mOverrideFoldedArea.set(area); 172 } 173 getFoldedArea()174 Rect getFoldedArea() { 175 if (!mOverrideFoldedArea.isEmpty()) { 176 return mOverrideFoldedArea; 177 } else { 178 return mFoldedArea; 179 } 180 } 181 onDefaultDisplayFocusChanged(String pkg)182 void onDefaultDisplayFocusChanged(String pkg) { 183 mFocusedApp = pkg; 184 } 185 create(Context context, int displayId)186 static DisplayFoldController create(Context context, int displayId) { 187 final WindowManagerInternal windowManagerService = 188 LocalServices.getService(WindowManagerInternal.class); 189 final DisplayManagerInternal displayService = 190 LocalServices.getService(DisplayManagerInternal.class); 191 final CameraServiceProxy cameraServiceProxy = 192 LocalServices.getService(CameraServiceProxy.class); 193 194 final String configFoldedArea = context.getResources().getString( 195 com.android.internal.R.string.config_foldedArea); 196 final Rect foldedArea; 197 if (configFoldedArea == null || configFoldedArea.isEmpty()) { 198 foldedArea = new Rect(); 199 } else { 200 foldedArea = Rect.unflattenFromString(configFoldedArea); 201 } 202 203 return new DisplayFoldController(context, windowManagerService, displayService, 204 cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler()); 205 } 206 } 207