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.common.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 
202         @BinderThread
203         private class DisplayWindowInsetsControllerImpl
204                 extends IDisplayWindowInsetsController.Stub {
205             @Override
topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)206             public void topFocusedWindowChanged(ComponentName component,
207                     @InsetsType int requestedVisibleTypes) throws RemoteException {
208                 mMainExecutor.execute(() -> {
209                     PerDisplay.this.topFocusedWindowChanged(component, requestedVisibleTypes);
210                 });
211             }
212 
213             @Override
insetsChanged(InsetsState insetsState)214             public void insetsChanged(InsetsState insetsState) throws RemoteException {
215                 mMainExecutor.execute(() -> {
216                     PerDisplay.this.insetsChanged(insetsState);
217                 });
218             }
219 
220             @Override
insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)221             public void insetsControlChanged(InsetsState insetsState,
222                     InsetsSourceControl[] activeControls) throws RemoteException {
223                 mMainExecutor.execute(() -> {
224                     PerDisplay.this.insetsControlChanged(insetsState, activeControls);
225                 });
226             }
227 
228             @Override
showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)229             public void showInsets(@InsetsType int types, boolean fromIme,
230                     @Nullable ImeTracker.Token statsToken) throws RemoteException {
231                 mMainExecutor.execute(() -> {
232                     PerDisplay.this.showInsets(types, fromIme, statsToken);
233                 });
234             }
235 
236             @Override
hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)237             public void hideInsets(@InsetsType int types, boolean fromIme,
238                     @Nullable ImeTracker.Token statsToken) throws RemoteException {
239                 mMainExecutor.execute(() -> {
240                     PerDisplay.this.hideInsets(types, fromIme, statsToken);
241                 });
242             }
243         }
244     }
245 
246     /**
247      * Gets notified whenever the insets change.
248      *
249      * @see IDisplayWindowInsetsController
250      */
251     @ShellMainThread
252     public interface OnInsetsChangedListener {
253         /**
254          * Called when top focused window changes to determine whether or not to take over insets
255          * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
256          *
257          * @param component The application component that is open in the top focussed window.
258          * @param requestedVisibleTypes The {@link InsetsType} requested visible by the focused
259          *                              window.
260          */
topFocusedWindowChanged(ComponentName component, @InsetsType int requestedVisibleTypes)261         default void topFocusedWindowChanged(ComponentName component,
262                 @InsetsType int requestedVisibleTypes) {}
263 
264         /**
265          * Called when the window insets configuration has changed.
266          */
insetsChanged(InsetsState insetsState)267         default void insetsChanged(InsetsState insetsState) {}
268 
269         /**
270          * Called when this window retrieved control over a specified set of insets sources.
271          */
insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)272         default void insetsControlChanged(InsetsState insetsState,
273                 InsetsSourceControl[] activeControls) {}
274 
275         /**
276          * Called when a set of insets source window should be shown by policy.
277          *
278          * @param types {@link InsetsType} to show
279          * @param fromIme true if this request originated from IME (InputMethodService).
280          * @param statsToken the token tracking the current IME show request
281          *                   or {@code null} otherwise.
282          */
showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)283         default void showInsets(@InsetsType int types, boolean fromIme,
284                 @Nullable ImeTracker.Token statsToken) {}
285 
286         /**
287          * Called when a set of insets source window should be hidden by policy.
288          *
289          * @param types {@link InsetsType} to hide
290          * @param fromIme true if this request originated from IME (InputMethodService).
291          * @param statsToken the token tracking the current IME hide request
292          *                   or {@code null} otherwise.
293          */
hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)294         default void hideInsets(@InsetsType int types, boolean fromIme,
295                 @Nullable ImeTracker.Token statsToken) {}
296     }
297 }
298