1 /*
2  * Copyright (C) 2021 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.wm.shell.common;
18 
19 import android.annotation.Nullable;
20 import android.content.ComponentName;
21 import android.os.RemoteException;
22 import android.util.Slog;
23 import android.util.SparseArray;
24 import android.view.IDisplayWindowInsetsController;
25 import android.view.IWindowManager;
26 import android.view.InsetsSourceControl;
27 import android.view.InsetsState;
28 import android.view.WindowInsets.Type.InsetsType;
29 import android.view.inputmethod.ImeTracker;
30 
31 import androidx.annotation.BinderThread;
32 
33 import com.android.wm.shell.shared.annotations.ShellMainThread;
34 import com.android.wm.shell.sysui.ShellInit;
35 
36 import java.util.concurrent.CopyOnWriteArrayList;
37 
38 /**
39  * Manages insets from the core.
40  */
41 public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener {
42     private static final String TAG = "DisplayInsetsController";
43 
44     private final IWindowManager mWmService;
45     private final ShellExecutor mMainExecutor;
46     private final DisplayController mDisplayController;
47     private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>();
48     private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners =
49             new SparseArray<>();
50 
DisplayInsetsController(IWindowManager wmService, ShellInit shellInit, DisplayController displayController, ShellExecutor mainExecutor)51     public DisplayInsetsController(IWindowManager wmService,
52             ShellInit shellInit,
53             DisplayController displayController,
54             ShellExecutor mainExecutor) {
55         mWmService = wmService;
56         mDisplayController = displayController;
57         mMainExecutor = mainExecutor;
58         shellInit.addInitCallback(this::onInit, this);
59     }
60 
61     /**
62      * Starts listening for insets for each display.
63      **/
onInit()64     public void onInit() {
65         mDisplayController.addDisplayWindowListener(this);
66     }
67 
68     /**
69      * Adds a callback to listen for insets changes for a particular display.  Note that the
70      * listener will not be updated with the existing state of the insets on that display.
71      */
addInsetsChangedListener(int displayId, OnInsetsChangedListener listener)72     public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
73         CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
74         if (listeners == null) {
75             listeners = new CopyOnWriteArrayList<>();
76             mListeners.put(displayId, listeners);
77         }
78         if (!listeners.contains(listener)) {
79             listeners.add(listener);
80         }
81     }
82 
83     /**
84      * Removes a callback listening for insets changes from a particular display.
85      */
removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener)86     public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
87         CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
88         if (listeners == null) {
89             return;
90         }
91         listeners.remove(listener);
92     }
93 
94     @Override
onDisplayAdded(int displayId)95     public void onDisplayAdded(int displayId) {
96         PerDisplay pd = new PerDisplay(displayId);
97         pd.register();
98         mInsetsPerDisplay.put(displayId, pd);
99     }
100 
101     @Override
onDisplayRemoved(int displayId)102     public void onDisplayRemoved(int displayId) {
103         PerDisplay pd = mInsetsPerDisplay.get(displayId);
104         if (pd == null) {
105             return;
106         }
107         pd.unregister();
108         mInsetsPerDisplay.remove(displayId);
109     }
110 
111     /**
112      * An implementation of {@link IDisplayWindowInsetsController} for a given display id.
113      **/
114     public class PerDisplay {
115         private final int mDisplayId;
116         private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
117                 new DisplayWindowInsetsControllerImpl();
118 
PerDisplay(int displayId)119         public PerDisplay(int displayId) {
120             mDisplayId = displayId;
121         }
122 
register()123         public void register() {
124             try {
125                 mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
126             } catch (RemoteException e) {
127                 Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
128             }
129         }
130 
unregister()131         public void unregister() {
132             try {
133                 mWmService.setDisplayWindowInsetsController(mDisplayId, null);
134             } catch (RemoteException e) {
135                 Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId);
136             }
137         }
138 
insetsChanged(InsetsState insetsState)139         private void insetsChanged(InsetsState insetsState) {
140             CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
141             if (listeners == null) {
142                 return;
143             }
144             mDisplayController.updateDisplayInsets(mDisplayId, insetsState);
145             for (OnInsetsChangedListener listener : listeners) {
146                 listener.insetsChanged(insetsState);
147             }
148         }
149 
insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)150         private void insetsControlChanged(InsetsState insetsState,
151                 InsetsSourceControl[] activeControls) {
152             CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
153             if (listeners == null) {
154                 return;
155             }
156             for (OnInsetsChangedListener listener : listeners) {
157                 listener.insetsControlChanged(insetsState, activeControls);
158             }
159         }
160 
showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)161         private void showInsets(@InsetsType int types, boolean fromIme,
162                 @Nullable ImeTracker.Token statsToken) {
163             CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
164             if (listeners == null) {
165                 ImeTracker.forLogging().onFailed(
166                         statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
167                 return;
168             }
169             ImeTracker.forLogging().onProgress(
170                     statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
171             for (OnInsetsChangedListener listener : listeners) {
172                 listener.showInsets(types, fromIme, statsToken);
173             }
174         }
175 
hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)176         private void hideInsets(@InsetsType int types, boolean fromIme,
177                 @Nullable ImeTracker.Token statsToken) {
178             CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
179             if (listeners == null) {
180                 ImeTracker.forLogging().onFailed(
181                         statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
182                 return;
183             }
184             ImeTracker.forLogging().onProgress(
185                     statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
186             for (OnInsetsChangedListener listener : listeners) {
187                 listener.hideInsets(types, fromIme, statsToken);
188             }
189         }
190 
topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)191         private void topFocusedWindowChanged(ComponentName component,
192                 @InsetsType int requestedVisibleTypes) {
193             CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
194             if (listeners == null) {
195                 return;
196             }
197             for (OnInsetsChangedListener listener : listeners) {
198                 listener.topFocusedWindowChanged(component, requestedVisibleTypes);
199             }
200         }
201 
setImeInputTargetRequestedVisibility(boolean visible)202         private void setImeInputTargetRequestedVisibility(boolean visible) {
203             CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
204             if (listeners == null) {
205                 return;
206             }
207             for (OnInsetsChangedListener listener : listeners) {
208                 listener.setImeInputTargetRequestedVisibility(visible);
209             }
210         }
211 
212         @BinderThread
213         private class DisplayWindowInsetsControllerImpl
214                 extends IDisplayWindowInsetsController.Stub {
215             @Override
topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)216             public void topFocusedWindowChanged(ComponentName component,
217                     @InsetsType int requestedVisibleTypes) throws RemoteException {
218                 mMainExecutor.execute(() -> {
219                     PerDisplay.this.topFocusedWindowChanged(component, requestedVisibleTypes);
220                 });
221             }
222 
223             @Override
insetsChanged(InsetsState insetsState)224             public void insetsChanged(InsetsState insetsState) throws RemoteException {
225                 mMainExecutor.execute(() -> {
226                     PerDisplay.this.insetsChanged(insetsState);
227                 });
228             }
229 
230             @Override
insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)231             public void insetsControlChanged(InsetsState insetsState,
232                     InsetsSourceControl[] activeControls) throws RemoteException {
233                 mMainExecutor.execute(() -> {
234                     PerDisplay.this.insetsControlChanged(insetsState, activeControls);
235                 });
236             }
237 
238             @Override
showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)239             public void showInsets(@InsetsType int types, boolean fromIme,
240                     @Nullable ImeTracker.Token statsToken) throws RemoteException {
241                 mMainExecutor.execute(() -> {
242                     PerDisplay.this.showInsets(types, fromIme, statsToken);
243                 });
244             }
245 
246             @Override
hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)247             public void hideInsets(@InsetsType int types, boolean fromIme,
248                     @Nullable ImeTracker.Token statsToken) throws RemoteException {
249                 mMainExecutor.execute(() -> {
250                     PerDisplay.this.hideInsets(types, fromIme, statsToken);
251                 });
252             }
253 
254             @Override
setImeInputTargetRequestedVisibility(boolean visible)255             public void setImeInputTargetRequestedVisibility(boolean visible)
256                     throws RemoteException {
257                 mMainExecutor.execute(() -> {
258                     PerDisplay.this.setImeInputTargetRequestedVisibility(visible);
259                 });
260             }
261         }
262     }
263 
264     /**
265      * Gets notified whenever the insets change.
266      *
267      * @see IDisplayWindowInsetsController
268      */
269     @ShellMainThread
270     public interface OnInsetsChangedListener {
271         /**
272          * Called when top focused window changes to determine whether or not to take over insets
273          * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
274          *
275          * @param component The application component that is open in the top focussed window.
276          * @param requestedVisibleTypes The {@link InsetsType} requested visible by the focused
277          *                              window.
278          */
topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)279         default void topFocusedWindowChanged(ComponentName component,
280                 @InsetsType int requestedVisibleTypes) {}
281 
282         /**
283          * Called when the window insets configuration has changed.
284          */
insetsChanged(InsetsState insetsState)285         default void insetsChanged(InsetsState insetsState) {}
286 
287         /**
288          * Called when this window retrieved control over a specified set of insets sources.
289          */
insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)290         default void insetsControlChanged(InsetsState insetsState,
291                 InsetsSourceControl[] activeControls) {}
292 
293         /**
294          * Called when a set of insets source window should be shown by policy.
295          *
296          * @param types {@link InsetsType} to show
297          * @param fromIme true if this request originated from IME (InputMethodService).
298          * @param statsToken the token tracking the current IME request or {@code null} otherwise.
299          */
showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)300         default void showInsets(@InsetsType int types, boolean fromIme,
301                 @Nullable ImeTracker.Token statsToken) {}
302 
303         /**
304          * Called when a set of insets source window should be hidden by policy.
305          *
306          * @param types {@link InsetsType} to hide
307          * @param fromIme true if this request originated from IME (InputMethodService).
308          * @param statsToken the token tracking the current IME request or {@code null} otherwise.
309          */
hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)310         default void hideInsets(@InsetsType int types, boolean fromIme,
311                 @Nullable ImeTracker.Token statsToken) {}
312 
313         /**
314          * Called to set the requested visibility of the IME in DisplayImeController. Invoked by
315          * {@link com.android.server.wm.DisplayContent.RemoteInsetsControlTarget}.
316          * @param visible requested status of the IME
317          */
setImeInputTargetRequestedVisibility(boolean visible)318         default void setImeInputTargetRequestedVisibility(boolean visible) {}
319     }
320 }
321