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