1 /* 2 * Copyright (C) 2023 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.server.wm; 18 19 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 20 21 import android.os.Binder; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.util.Slog; 25 import android.view.IRotationWatcher; 26 27 import java.io.PrintWriter; 28 import java.util.ArrayList; 29 30 /** Manages the registration and event dispatch of {@link IRotationWatcher}. */ 31 class RotationWatcherController { 32 33 private final WindowManagerService mService; 34 35 /** The watchers that monitor the applied rotation of display. */ 36 private final ArrayList<DisplayRotationWatcher> mDisplayRotationWatchers = new ArrayList<>(); 37 38 /** The listeners that monitor the proposed rotation of the corresponding window container. */ 39 private final ArrayList<ProposedRotationListener> mProposedRotationListeners = 40 new ArrayList<>(); 41 42 /** A quick look up which can be checked without WM lock. */ 43 private volatile boolean mHasProposedRotationListeners; 44 RotationWatcherController(WindowManagerService wms)45 RotationWatcherController(WindowManagerService wms) { 46 mService = wms; 47 } 48 registerDisplayRotationWatcher(IRotationWatcher watcher, int displayId)49 void registerDisplayRotationWatcher(IRotationWatcher watcher, int displayId) { 50 final IBinder watcherBinder = watcher.asBinder(); 51 for (int i = mDisplayRotationWatchers.size() - 1; i >= 0; i--) { 52 if (watcherBinder == mDisplayRotationWatchers.get(i).mWatcher.asBinder()) { 53 throw new IllegalArgumentException("Registering existed rotation watcher"); 54 } 55 } 56 register(watcherBinder, new DisplayRotationWatcher(mService, watcher, displayId), 57 mDisplayRotationWatchers); 58 } 59 registerProposedRotationListener(IRotationWatcher listener, IBinder contextToken)60 void registerProposedRotationListener(IRotationWatcher listener, IBinder contextToken) { 61 final IBinder listenerBinder = listener.asBinder(); 62 for (int i = mProposedRotationListeners.size() - 1; i >= 0; i--) { 63 final ProposedRotationListener watcher = mProposedRotationListeners.get(i); 64 if (contextToken == watcher.mToken || listenerBinder == watcher.mWatcher.asBinder()) { 65 Slog.w(TAG_WM, "Register rotation listener to a registered token, uid=" 66 + Binder.getCallingUid()); 67 return; 68 } 69 } 70 register(listenerBinder, new ProposedRotationListener(mService, listener, contextToken), 71 mProposedRotationListeners); 72 mHasProposedRotationListeners = !mProposedRotationListeners.isEmpty(); 73 } 74 register(IBinder watcherBinder, T watcher, ArrayList<T> watcherList)75 private static <T extends RotationWatcher> void register(IBinder watcherBinder, T watcher, 76 ArrayList<T> watcherList) { 77 try { 78 watcherBinder.linkToDeath(watcher, 0); 79 watcherList.add(watcher); 80 } catch (RemoteException e) { 81 // Client died, no cleanup needed. 82 } 83 } 84 unregister(IRotationWatcher watcher, ArrayList<T> watcherList)85 private static <T extends RotationWatcher> boolean unregister(IRotationWatcher watcher, 86 ArrayList<T> watcherList) { 87 final IBinder watcherBinder = watcher.asBinder(); 88 for (int i = watcherList.size() - 1; i >= 0; i--) { 89 final RotationWatcher rotationWatcher = watcherList.get(i); 90 if (watcherBinder != rotationWatcher.mWatcher.asBinder()) { 91 continue; 92 } 93 watcherList.remove(i); 94 rotationWatcher.unlinkToDeath(); 95 return true; 96 } 97 return false; 98 } 99 removeRotationWatcher(IRotationWatcher watcher)100 void removeRotationWatcher(IRotationWatcher watcher) { 101 final boolean removed = unregister(watcher, mProposedRotationListeners); 102 if (removed) { 103 mHasProposedRotationListeners = !mProposedRotationListeners.isEmpty(); 104 } else { 105 // The un-registration shares the same interface, so look up the watchers as well. 106 unregister(watcher, mDisplayRotationWatchers); 107 } 108 } 109 110 /** Called when the new rotation of display is applied. */ dispatchDisplayRotationChange(int displayId, int rotation)111 void dispatchDisplayRotationChange(int displayId, int rotation) { 112 for (int i = mDisplayRotationWatchers.size() - 1; i >= 0; i--) { 113 final DisplayRotationWatcher rotationWatcher = mDisplayRotationWatchers.get(i); 114 if (rotationWatcher.mDisplayId == displayId) { 115 rotationWatcher.notifyRotation(rotation); 116 } 117 } 118 } 119 120 /** Called when the window orientation listener has a new proposed rotation. */ dispatchProposedRotation(DisplayContent dc, int rotation)121 void dispatchProposedRotation(DisplayContent dc, int rotation) { 122 for (int i = mProposedRotationListeners.size() - 1; i >= 0; i--) { 123 final ProposedRotationListener listener = mProposedRotationListeners.get(i); 124 final WindowContainer<?> wc = getAssociatedWindowContainer(listener.mToken); 125 if (wc != null) { 126 if (wc.mDisplayContent == dc) { 127 listener.notifyRotation(rotation); 128 } 129 } else { 130 // Unregister if the associated window container is gone. 131 mProposedRotationListeners.remove(i); 132 mHasProposedRotationListeners = !mProposedRotationListeners.isEmpty(); 133 listener.unlinkToDeath(); 134 } 135 } 136 } 137 hasProposedRotationListeners()138 boolean hasProposedRotationListeners() { 139 return mHasProposedRotationListeners; 140 } 141 getAssociatedWindowContainer(IBinder contextToken)142 WindowContainer<?> getAssociatedWindowContainer(IBinder contextToken) { 143 final WindowContainer<?> wc = ActivityRecord.forTokenLocked(contextToken); 144 if (wc != null) { 145 return wc; 146 } 147 return mService.mWindowContextListenerController.getContainer(contextToken); 148 } 149 dump(PrintWriter pw)150 void dump(PrintWriter pw) { 151 if (!mDisplayRotationWatchers.isEmpty()) { 152 pw.print(" mDisplayRotationWatchers: ["); 153 for (int i = mDisplayRotationWatchers.size() - 1; i >= 0; i--) { 154 pw.print(' '); 155 final DisplayRotationWatcher watcher = mDisplayRotationWatchers.get(i); 156 pw.print(watcher.mOwnerUid); 157 pw.print("->"); 158 pw.print(watcher.mDisplayId); 159 } 160 pw.println(']'); 161 } 162 if (!mProposedRotationListeners.isEmpty()) { 163 pw.print(" mProposedRotationListeners: ["); 164 for (int i = mProposedRotationListeners.size() - 1; i >= 0; i--) { 165 pw.print(' '); 166 final ProposedRotationListener listener = mProposedRotationListeners.get(i); 167 pw.print(listener.mOwnerUid); 168 pw.print("->"); 169 pw.print(getAssociatedWindowContainer(listener.mToken)); 170 } 171 pw.println(']'); 172 } 173 } 174 175 /** Reports new applied rotation of a display. */ 176 private static class DisplayRotationWatcher extends RotationWatcher { 177 final int mDisplayId; 178 DisplayRotationWatcher(WindowManagerService wms, IRotationWatcher watcher, int displayId)179 DisplayRotationWatcher(WindowManagerService wms, IRotationWatcher watcher, int displayId) { 180 super(wms, watcher); 181 mDisplayId = displayId; 182 } 183 } 184 185 /** Reports proposed rotation of a window token. */ 186 private static class ProposedRotationListener extends RotationWatcher { 187 final IBinder mToken; 188 ProposedRotationListener(WindowManagerService wms, IRotationWatcher watcher, IBinder token)189 ProposedRotationListener(WindowManagerService wms, IRotationWatcher watcher, 190 IBinder token) { 191 super(wms, watcher); 192 mToken = token; 193 } 194 } 195 196 private static class RotationWatcher implements IBinder.DeathRecipient { 197 final WindowManagerService mWms; 198 final IRotationWatcher mWatcher; 199 final int mOwnerUid = Binder.getCallingUid(); 200 RotationWatcher(WindowManagerService wms, IRotationWatcher watcher)201 RotationWatcher(WindowManagerService wms, IRotationWatcher watcher) { 202 mWms = wms; 203 mWatcher = watcher; 204 } 205 notifyRotation(int rotation)206 void notifyRotation(int rotation) { 207 try { 208 mWatcher.onRotationChanged(rotation); 209 } catch (RemoteException ignored) { 210 // Let binderDied() remove this. 211 } 212 } 213 unlinkToDeath()214 void unlinkToDeath() { 215 mWatcher.asBinder().unlinkToDeath(this, 0); 216 } 217 218 @Override binderDied()219 public void binderDied() { 220 mWms.removeRotationWatcher(mWatcher); 221 } 222 } 223 } 224