Merge "Abstract out common code for managing a single instance remote callback" into sc-v2-dev

This commit is contained in:
Winson Chung
2021-11-03 03:04:18 +00:00
committed by Android (Google) Code Review
7 changed files with 189 additions and 156 deletions

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2021 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.wm.shell.common;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import android.util.Slog;
import androidx.annotation.BinderThread;
import java.util.function.Consumer;
/**
* Manages the lifecycle of a single instance of a remote listener, including the clean up if the
* remote process dies. All calls on this class should happen on the main shell thread.
*
* @param <C> The controller (must be RemoteCallable)
* @param <L> The remote listener interface type
*/
public class SingleInstanceRemoteListener<C extends RemoteCallable, L extends IInterface> {
private static final String TAG = SingleInstanceRemoteListener.class.getSimpleName();
/**
* Simple callable interface that throws a remote exception.
*/
public interface RemoteCall<L> {
void accept(L l) throws RemoteException;
}
private final C mCallableController;
private final Consumer<C> mOnRegisterCallback;
private final Consumer<C> mOnUnregisterCallback;
L mListener;
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final C callableController = mCallableController;
mCallableController.getRemoteCallExecutor().execute(() -> {
mListener = null;
mOnUnregisterCallback.accept(callableController);
});
}
};
/**
* @param onRegisterCallback Callback when register() is called (same thread)
* @param onUnregisterCallback Callback when unregister() is called (same thread as unregister()
* or the callableController.getRemoteCallbackExecutor() thread)
*/
public SingleInstanceRemoteListener(C callableController,
Consumer<C> onRegisterCallback,
Consumer<C> onUnregisterCallback) {
mCallableController = callableController;
mOnRegisterCallback = onRegisterCallback;
mOnUnregisterCallback = onUnregisterCallback;
}
/**
* Registers this listener, storing a reference to it and calls the provided method in the
* constructor.
*/
public void register(L listener) {
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0 /* flags */);
}
if (listener != null) {
try {
listener.asBinder().linkToDeath(mListenerDeathRecipient, 0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
}
mListener = listener;
mOnRegisterCallback.accept(mCallableController);
}
/**
* Unregisters this listener, removing all references to it and calls the provided method in the
* constructor.
*/
public void unregister() {
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0 /* flags */);
}
mListener = null;
mOnUnregisterCallback.accept(mCallableController);
}
/**
* Safely wraps a call to the remote listener.
*/
public void call(RemoteCall<L> handler) {
if (mListener == null) {
Slog.e(TAG, "Failed remote call on null listener");
return;
}
try {
handler.accept(mListener);
} catch (RemoteException e) {
Slog.e(TAG, "Failed remote call", e);
}
}
}

View File

@@ -43,7 +43,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
@@ -69,6 +68,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController;
@@ -117,13 +117,28 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private final Rect mTmpInsetBounds = new Rect(); private final Rect mTmpInsetBounds = new Rect();
private boolean mIsInFixedRotation; private boolean mIsInFixedRotation;
private IPipAnimationListener mPinnedStackAnimationRecentsCallback; private PipAnimationListener mPinnedStackAnimationRecentsCallback;
protected PhonePipMenuController mMenuController; protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer; protected PipTaskOrganizer mPipTaskOrganizer;
protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener = protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener =
new PipControllerPinnedTaskListener(); new PipControllerPinnedTaskListener();
private interface PipAnimationListener {
/**
* Notifies the listener that the Pip animation is started.
*/
void onPipAnimationStarted();
/**
* Notifies the listener about PiP round corner radius changes.
* Listener can expect an immediate callback the first time they attach.
*
* @param cornerRadius the pixel value of the corner radius, zero means it's disabled.
*/
void onPipCornerRadiusChanged(int cornerRadius);
}
/** /**
* Handler for display rotation changes. * Handler for display rotation changes.
*/ */
@@ -551,7 +566,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
animationType == PipAnimationController.ANIM_TYPE_BOUNDS); animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
} }
private void setPinnedStackAnimationListener(IPipAnimationListener callback) { private void setPinnedStackAnimationListener(PipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback; mPinnedStackAnimationRecentsCallback = callback;
onPipCornerRadiusChanged(); onPipCornerRadiusChanged();
} }
@@ -560,11 +575,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
if (mPinnedStackAnimationRecentsCallback != null) { if (mPinnedStackAnimationRecentsCallback != null) {
final int cornerRadius = final int cornerRadius =
mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius); mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
try { mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onPipCornerRadiusChanged", e);
}
} }
} }
@@ -623,11 +634,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// Disable touches while the animation is running // Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false); mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) { if (mPinnedStackAnimationRecentsCallback != null) {
try { mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e);
}
} }
} }
@@ -866,22 +873,25 @@ public class PipController implements PipTransitionController.PipTransitionCallb
@BinderThread @BinderThread
private static class IPipImpl extends IPip.Stub { private static class IPipImpl extends IPip.Stub {
private PipController mController; private PipController mController;
private IPipAnimationListener mListener; private final SingleInstanceRemoteListener<PipController,
private final IBinder.DeathRecipient mListenerDeathRecipient = IPipAnimationListener> mListener;
new IBinder.DeathRecipient() { private final PipAnimationListener mPipAnimationListener = new PipAnimationListener() {
@Override @Override
@BinderThread public void onPipAnimationStarted() {
public void binderDied() { mListener.call(l -> l.onPipAnimationStarted());
final PipController controller = mController; }
controller.getRemoteCallExecutor().execute(() -> {
mListener = null; @Override
controller.setPinnedStackAnimationListener(null); public void onPipCornerRadiusChanged(int cornerRadius) {
}); mListener.call(l -> l.onPipCornerRadiusChanged(cornerRadius));
} }
}; };
IPipImpl(PipController controller) { IPipImpl(PipController controller) {
mController = controller; mController = controller;
mListener = new SingleInstanceRemoteListener<>(mController,
c -> c.setPinnedStackAnimationListener(mPipAnimationListener),
c -> c.setPinnedStackAnimationListener(null));
} }
/** /**
@@ -925,23 +935,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
public void setPinnedStackAnimationListener(IPipAnimationListener listener) { public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener", executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener",
(controller) -> { (controller) -> {
if (mListener != null) {
// Reset the old death recipient
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) { if (listener != null) {
// Register the death recipient for the new listener to clear the listener mListener.register(listener);
try { } else {
listener.asBinder().linkToDeath(mListenerDeathRecipient, mListener.unregister();
0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
} }
mListener = listener;
controller.setPinnedStackAnimationListener(listener);
}); });
} }
} }

View File

@@ -34,7 +34,6 @@ import android.content.Intent;
import android.content.pm.LauncherApps; import android.content.pm.LauncherApps;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -61,6 +60,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ExternalThread;
@@ -433,46 +433,26 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@BinderThread @BinderThread
private static class ISplitScreenImpl extends ISplitScreen.Stub { private static class ISplitScreenImpl extends ISplitScreen.Stub {
private SplitScreenController mController; private SplitScreenController mController;
private ISplitScreenListener mListener; private final SingleInstanceRemoteListener<SplitScreenController,
ISplitScreenListener> mListener;
private final SplitScreen.SplitScreenListener mSplitScreenListener = private final SplitScreen.SplitScreenListener mSplitScreenListener =
new SplitScreen.SplitScreenListener() { new SplitScreen.SplitScreenListener() {
@Override @Override
public void onStagePositionChanged(int stage, int position) { public void onStagePositionChanged(int stage, int position) {
try { mListener.call(l -> l.onStagePositionChanged(stage, position));
if (mListener != null) {
mListener.onStagePositionChanged(stage, position);
}
} catch (RemoteException e) {
Slog.e(TAG, "onStagePositionChanged", e);
}
} }
@Override @Override
public void onTaskStageChanged(int taskId, int stage, boolean visible) { public void onTaskStageChanged(int taskId, int stage, boolean visible) {
try { mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
if (mListener != null) {
mListener.onTaskStageChanged(taskId, stage, visible);
}
} catch (RemoteException e) {
Slog.e(TAG, "onTaskStageChanged", e);
}
}
};
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final SplitScreenController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.unregisterSplitScreenListener(mSplitScreenListener);
});
} }
}; };
public ISplitScreenImpl(SplitScreenController controller) { public ISplitScreenImpl(SplitScreenController controller) {
mController = controller; mController = controller;
mListener = new SingleInstanceRemoteListener<>(controller,
c -> c.registerSplitScreenListener(mSplitScreenListener),
c -> c.unregisterSplitScreenListener(mSplitScreenListener));
} }
/** /**
@@ -485,36 +465,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override @Override
public void registerSplitScreenListener(ISplitScreenListener listener) { public void registerSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener", executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
(controller) -> { (controller) -> mListener.register(listener));
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) {
try {
listener.asBinder().linkToDeath(mListenerDeathRecipient,
0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
}
mListener = listener;
controller.registerSplitScreenListener(mSplitScreenListener);
});
} }
@Override @Override
public void unregisterSplitScreenListener(ISplitScreenListener listener) { public void unregisterSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener", executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
(controller) -> { (controller) -> mListener.unregister());
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
mListener = null;
controller.unregisterSplitScreenListener(mSplitScreenListener);
});
} }
@Override @Override

View File

@@ -46,6 +46,7 @@ import com.android.internal.util.function.TriConsumer;
import com.android.launcher3.icons.IconProvider; import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.TransactionPool;
/** /**
@@ -237,24 +238,19 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
@BinderThread @BinderThread
private static class IStartingWindowImpl extends IStartingWindow.Stub { private static class IStartingWindowImpl extends IStartingWindow.Stub {
private StartingWindowController mController; private StartingWindowController mController;
private IStartingWindowListener mListener; private SingleInstanceRemoteListener<StartingWindowController,
IStartingWindowListener> mListener;
private final TriConsumer<Integer, Integer, Integer> mStartingWindowListener = private final TriConsumer<Integer, Integer, Integer> mStartingWindowListener =
this::notifyIStartingWindowListener; (taskId, supportedType, startingWindowBackgroundColor) -> {
private final IBinder.DeathRecipient mListenerDeathRecipient = mListener.call(l -> l.onTaskLaunching(taskId, supportedType,
new IBinder.DeathRecipient() { startingWindowBackgroundColor));
@Override
@BinderThread
public void binderDied() {
final StartingWindowController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.setStartingWindowListener(null);
});
}
}; };
public IStartingWindowImpl(StartingWindowController controller) { public IStartingWindowImpl(StartingWindowController controller) {
mController = controller; mController = controller;
mListener = new SingleInstanceRemoteListener<>(controller,
c -> c.setStartingWindowListener(mStartingWindowListener),
c -> c.setStartingWindowListener(null));
} }
/** /**
@@ -268,36 +264,12 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
public void setStartingWindowListener(IStartingWindowListener listener) { public void setStartingWindowListener(IStartingWindowListener listener) {
executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener", executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener",
(controller) -> { (controller) -> {
if (mListener != null) {
// Reset the old death recipient
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) { if (listener != null) {
try { mListener.register(listener);
listener.asBinder().linkToDeath(mListenerDeathRecipient, } else {
0 /* flags */); mListener.unregister();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
} }
mListener = listener;
controller.setStartingWindowListener(mStartingWindowListener);
}); });
} }
private void notifyIStartingWindowListener(int taskId, int supportedType,
int startingWindowBackgroundColor) {
if (mListener == null) {
return;
}
try {
mListener.onTaskLaunching(taskId, supportedType, startingWindowBackgroundColor);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify task launching", e);
}
}
} }
} }

View File

@@ -38,11 +38,11 @@ public interface ShellTransitions {
/** /**
* Registers a remote transition. * Registers a remote transition.
*/ */
void registerRemote(@NonNull TransitionFilter filter, default void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition); @NonNull RemoteTransition remoteTransition) {}
/** /**
* Unregisters a remote transition. * Unregisters a remote transition.
*/ */
void unregisterRemote(@NonNull RemoteTransition remoteTransition); default void unregisterRemote(@NonNull RemoteTransition remoteTransition) {}
} }

View File

@@ -171,24 +171,6 @@ public class Transitions implements RemoteCallable<Transitions> {
} }
} }
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
public static ShellTransitions createEmptyForTesting() {
return new ShellTransitions() {
@Override
public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
@androidx.annotation.NonNull RemoteTransition remoteTransition) {
// Do nothing
}
@Override
public void unregisterRemote(
@androidx.annotation.NonNull RemoteTransition remoteTransition) {
// Do nothing
}
};
}
/** Register this transition handler with Core */ /** Register this transition handler with Core */
public void register(ShellTaskOrganizer taskOrganizer) { public void register(ShellTaskOrganizer taskOrganizer) {
if (mPlayerImpl == null) return; if (mPlayerImpl == null) return;

View File

@@ -30,6 +30,7 @@ import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMComponent;
import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider; import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import java.util.Optional; import java.util.Optional;
@@ -133,7 +134,7 @@ public class SystemUIFactory {
.setShellCommandHandler(Optional.ofNullable(null)) .setShellCommandHandler(Optional.ofNullable(null))
.setAppPairs(Optional.ofNullable(null)) .setAppPairs(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null)) .setTaskViewFactory(Optional.ofNullable(null))
.setTransitions(Transitions.createEmptyForTesting()) .setTransitions(new ShellTransitions() {})
.setDisplayAreaHelper(Optional.ofNullable(null)) .setDisplayAreaHelper(Optional.ofNullable(null))
.setStartingSurface(Optional.ofNullable(null)) .setStartingSurface(Optional.ofNullable(null))
.setTaskSurfaceHelper(Optional.ofNullable(null)); .setTaskSurfaceHelper(Optional.ofNullable(null));