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