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.wm.shell.common;
18 
19 import android.annotation.Nullable;
20 import android.os.RemoteException;
21 import android.util.Slog;
22 import android.view.IDisplayChangeWindowCallback;
23 import android.view.IDisplayChangeWindowController;
24 import android.view.IWindowManager;
25 import android.window.DisplayAreaInfo;
26 import android.window.WindowContainerTransaction;
27 
28 import androidx.annotation.BinderThread;
29 
30 import com.android.wm.shell.common.annotations.ShellMainThread;
31 import com.android.wm.shell.sysui.ShellInit;
32 
33 import java.util.concurrent.CopyOnWriteArrayList;
34 
35 /**
36  * This module deals with display rotations coming from WM. When WM starts a rotation: after it has
37  * frozen the screen, it will call into this class. This will then call all registered local
38  * controllers and give them a chance to queue up task changes to be applied synchronously with that
39  * rotation.
40  */
41 public class DisplayChangeController {
42     private static final String TAG = DisplayChangeController.class.getSimpleName();
43 
44     private final ShellExecutor mMainExecutor;
45     private final IWindowManager mWmService;
46     private final IDisplayChangeWindowController mControllerImpl;
47 
48     private final CopyOnWriteArrayList<OnDisplayChangingListener> mDisplayChangeListener =
49             new CopyOnWriteArrayList<>();
50 
DisplayChangeController(IWindowManager wmService, ShellInit shellInit, ShellExecutor mainExecutor)51     public DisplayChangeController(IWindowManager wmService, ShellInit shellInit,
52             ShellExecutor mainExecutor) {
53         mMainExecutor = mainExecutor;
54         mWmService = wmService;
55         mControllerImpl = new DisplayChangeWindowControllerImpl();
56         shellInit.addInitCallback(this::onInit, this);
57     }
58 
onInit()59     private void onInit() {
60         try {
61             mWmService.setDisplayChangeWindowController(mControllerImpl);
62         } catch (RemoteException e) {
63             throw new RuntimeException("Unable to register rotation controller");
64         }
65     }
66 
67     /**
68      * Adds a display rotation controller.
69      */
addDisplayChangeListener(OnDisplayChangingListener listener)70     public void addDisplayChangeListener(OnDisplayChangingListener listener) {
71         mDisplayChangeListener.add(listener);
72     }
73 
74     /**
75      * Removes a display rotation controller.
76      */
removeDisplayChangeListener(OnDisplayChangingListener listener)77     public void removeDisplayChangeListener(OnDisplayChangingListener listener) {
78         mDisplayChangeListener.remove(listener);
79     }
80 
81     /** Query all listeners for changes that should happen on display change. */
dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId, int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo)82     void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
83             int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) {
84         for (OnDisplayChangingListener c : mDisplayChangeListener) {
85             c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct);
86         }
87     }
88 
onDisplayChange(int displayId, int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback)89     private void onDisplayChange(int displayId, int fromRotation, int toRotation,
90             DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
91         WindowContainerTransaction t = new WindowContainerTransaction();
92         dispatchOnDisplayChange(t, displayId, fromRotation, toRotation, newDisplayAreaInfo);
93         try {
94             callback.continueDisplayChange(t);
95         } catch (RemoteException e) {
96             Slog.e(TAG, "Failed to continue handling display change", e);
97         }
98     }
99 
100     @BinderThread
101     private class DisplayChangeWindowControllerImpl
102             extends IDisplayChangeWindowController.Stub {
103         @Override
onDisplayChange(int displayId, int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback)104         public void onDisplayChange(int displayId, int fromRotation, int toRotation,
105                 DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
106             mMainExecutor.execute(() -> DisplayChangeController.this
107                     .onDisplayChange(displayId, fromRotation, toRotation,
108                             newDisplayAreaInfo, callback));
109         }
110     }
111 
112     /**
113      * Give a listener a chance to queue up configuration changes to execute as part of a
114      * display rotation. The contents of {@link #onDisplayChange} must run synchronously.
115      */
116     @ShellMainThread
117     public interface OnDisplayChangingListener {
118         /**
119          * Called before the display size has changed.
120          * Contents of this method must run synchronously.
121          * @param displayId display id of the display that is under the change
122          * @param fromRotation rotation before the change
123          * @param toRotation rotation after the change
124          * @param newDisplayAreaInfo display area info after applying the update
125          * @param t A task transaction to populate.
126          */
onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t)127         void onDisplayChange(int displayId, int fromRotation, int toRotation,
128                 @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t);
129     }
130 }
131