blob: b9f4589fcbb39e31a2106503c1e149bdfba95d6f [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IRotationWatcher;
import java.io.PrintWriter;
import java.util.ArrayList;
/** Manages the registration and event dispatch of {@link IRotationWatcher}. */
class RotationWatcherController {
private final WindowManagerService mService;
/** The watchers that monitor the applied rotation of display. */
private final ArrayList<DisplayRotationWatcher> mDisplayRotationWatchers = new ArrayList<>();
/** The listeners that monitor the proposed rotation of the corresponding window container. */
private final ArrayList<ProposedRotationListener> mProposedRotationListeners =
new ArrayList<>();
/** A quick look up which can be checked without WM lock. */
private volatile boolean mHasProposedRotationListeners;
RotationWatcherController(WindowManagerService wms) {
mService = wms;
}
void registerDisplayRotationWatcher(IRotationWatcher watcher, int displayId) {
final IBinder watcherBinder = watcher.asBinder();
for (int i = mDisplayRotationWatchers.size() - 1; i >= 0; i--) {
if (watcherBinder == mDisplayRotationWatchers.get(i).mWatcher.asBinder()) {
throw new IllegalArgumentException("Registering existed rotation watcher");
}
}
register(watcherBinder, new DisplayRotationWatcher(mService, watcher, displayId),
mDisplayRotationWatchers);
}
void registerProposedRotationListener(IRotationWatcher listener, IBinder contextToken) {
final IBinder listenerBinder = listener.asBinder();
for (int i = mProposedRotationListeners.size() - 1; i >= 0; i--) {
final ProposedRotationListener watcher = mProposedRotationListeners.get(i);
if (contextToken == watcher.mToken || listenerBinder == watcher.mWatcher.asBinder()) {
Slog.w(TAG_WM, "Register rotation listener to a registered token, uid="
+ Binder.getCallingUid());
return;
}
}
register(listenerBinder, new ProposedRotationListener(mService, listener, contextToken),
mProposedRotationListeners);
mHasProposedRotationListeners = !mProposedRotationListeners.isEmpty();
}
private static <T extends RotationWatcher> void register(IBinder watcherBinder, T watcher,
ArrayList<T> watcherList) {
try {
watcherBinder.linkToDeath(watcher, 0);
watcherList.add(watcher);
} catch (RemoteException e) {
// Client died, no cleanup needed.
}
}
private static <T extends RotationWatcher> boolean unregister(IRotationWatcher watcher,
ArrayList<T> watcherList) {
final IBinder watcherBinder = watcher.asBinder();
for (int i = watcherList.size() - 1; i >= 0; i--) {
final RotationWatcher rotationWatcher = watcherList.get(i);
if (watcherBinder != rotationWatcher.mWatcher.asBinder()) {
continue;
}
watcherList.remove(i);
rotationWatcher.unlinkToDeath();
return true;
}
return false;
}
void removeRotationWatcher(IRotationWatcher watcher) {
final boolean removed = unregister(watcher, mProposedRotationListeners);
if (removed) {
mHasProposedRotationListeners = !mProposedRotationListeners.isEmpty();
} else {
// The un-registration shares the same interface, so look up the watchers as well.
unregister(watcher, mDisplayRotationWatchers);
}
}
/** Called when the new rotation of display is applied. */
void dispatchDisplayRotationChange(int displayId, int rotation) {
for (int i = mDisplayRotationWatchers.size() - 1; i >= 0; i--) {
final DisplayRotationWatcher rotationWatcher = mDisplayRotationWatchers.get(i);
if (rotationWatcher.mDisplayId == displayId) {
rotationWatcher.notifyRotation(rotation);
}
}
}
/** Called when the window orientation listener has a new proposed rotation. */
void dispatchProposedRotation(DisplayContent dc, int rotation) {
for (int i = mProposedRotationListeners.size() - 1; i >= 0; i--) {
final ProposedRotationListener listener = mProposedRotationListeners.get(i);
final WindowContainer<?> wc = getAssociatedWindowContainer(listener.mToken);
if (wc != null) {
if (wc.mDisplayContent == dc) {
listener.notifyRotation(rotation);
}
} else {
// Unregister if the associated window container is gone.
mProposedRotationListeners.remove(i);
mHasProposedRotationListeners = !mProposedRotationListeners.isEmpty();
listener.unlinkToDeath();
}
}
}
boolean hasProposedRotationListeners() {
return mHasProposedRotationListeners;
}
WindowContainer<?> getAssociatedWindowContainer(IBinder contextToken) {
final WindowContainer<?> wc = ActivityRecord.forTokenLocked(contextToken);
if (wc != null) {
return wc;
}
return mService.mWindowContextListenerController.getContainer(contextToken);
}
void dump(PrintWriter pw) {
if (!mDisplayRotationWatchers.isEmpty()) {
pw.print(" mDisplayRotationWatchers: [");
for (int i = mDisplayRotationWatchers.size() - 1; i >= 0; i--) {
pw.print(' ');
final DisplayRotationWatcher watcher = mDisplayRotationWatchers.get(i);
pw.print(watcher.mOwnerUid);
pw.print("->");
pw.print(watcher.mDisplayId);
}
pw.println(']');
}
if (!mProposedRotationListeners.isEmpty()) {
pw.print(" mProposedRotationListeners: [");
for (int i = mProposedRotationListeners.size() - 1; i >= 0; i--) {
pw.print(' ');
final ProposedRotationListener listener = mProposedRotationListeners.get(i);
pw.print(listener.mOwnerUid);
pw.print("->");
pw.print(getAssociatedWindowContainer(listener.mToken));
}
pw.println(']');
}
}
/** Reports new applied rotation of a display. */
private static class DisplayRotationWatcher extends RotationWatcher {
final int mDisplayId;
DisplayRotationWatcher(WindowManagerService wms, IRotationWatcher watcher, int displayId) {
super(wms, watcher);
mDisplayId = displayId;
}
}
/** Reports proposed rotation of a window token. */
private static class ProposedRotationListener extends RotationWatcher {
final IBinder mToken;
ProposedRotationListener(WindowManagerService wms, IRotationWatcher watcher,
IBinder token) {
super(wms, watcher);
mToken = token;
}
}
private static class RotationWatcher implements IBinder.DeathRecipient {
final WindowManagerService mWms;
final IRotationWatcher mWatcher;
final int mOwnerUid = Binder.getCallingUid();
RotationWatcher(WindowManagerService wms, IRotationWatcher watcher) {
mWms = wms;
mWatcher = watcher;
}
void notifyRotation(int rotation) {
try {
mWatcher.onRotationChanged(rotation);
} catch (RemoteException ignored) {
// Let binderDied() remove this.
}
}
void unlinkToDeath() {
mWatcher.asBinder().unlinkToDeath(this, 0);
}
@Override
public void binderDied() {
mWms.removeRotationWatcher(mWatcher);
}
}
}