• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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