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