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.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
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.RemoteCallable;
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.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController;
@@ -117,13 +117,28 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private final Rect mTmpInsetBounds = new Rect();
private boolean mIsInFixedRotation;
private IPipAnimationListener mPinnedStackAnimationRecentsCallback;
private PipAnimationListener mPinnedStackAnimationRecentsCallback;
protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener =
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.
*/
@@ -551,7 +566,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
}
private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
private void setPinnedStackAnimationListener(PipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback;
onPipCornerRadiusChanged();
}
@@ -560,11 +575,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
if (mPinnedStackAnimationRecentsCallback != null) {
final int cornerRadius =
mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
try {
mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onPipCornerRadiusChanged", e);
}
mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
}
}
@@ -623,11 +634,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) {
try {
mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e);
}
mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
}
}
@@ -866,22 +873,25 @@ public class PipController implements PipTransitionController.PipTransitionCallb
@BinderThread
private static class IPipImpl extends IPip.Stub {
private PipController mController;
private IPipAnimationListener mListener;
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final PipController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.setPinnedStackAnimationListener(null);
});
}
};
private final SingleInstanceRemoteListener<PipController,
IPipAnimationListener> mListener;
private final PipAnimationListener mPipAnimationListener = new PipAnimationListener() {
@Override
public void onPipAnimationStarted() {
mListener.call(l -> l.onPipAnimationStarted());
}
@Override
public void onPipCornerRadiusChanged(int cornerRadius) {
mListener.call(l -> l.onPipCornerRadiusChanged(cornerRadius));
}
};
IPipImpl(PipController 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) {
executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener",
(controller) -> {
if (mListener != null) {
// Reset the old death recipient
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) {
// Register the death recipient for the new listener to clear the listener
try {
listener.asBinder().linkToDeath(mListenerDeathRecipient,
0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
mListener.register(listener);
} else {
mListener.unregister();
}
mListener = listener;
controller.setPinnedStackAnimationListener(listener);
});
}
}

View File

@@ -34,7 +34,6 @@ import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
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.RemoteCallable;
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.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
@@ -433,46 +433,26 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@BinderThread
private static class ISplitScreenImpl extends ISplitScreen.Stub {
private SplitScreenController mController;
private ISplitScreenListener mListener;
private final SingleInstanceRemoteListener<SplitScreenController,
ISplitScreenListener> mListener;
private final SplitScreen.SplitScreenListener mSplitScreenListener =
new SplitScreen.SplitScreenListener() {
@Override
public void onStagePositionChanged(int stage, int position) {
try {
if (mListener != null) {
mListener.onStagePositionChanged(stage, position);
}
} catch (RemoteException e) {
Slog.e(TAG, "onStagePositionChanged", e);
}
mListener.call(l -> l.onStagePositionChanged(stage, position));
}
@Override
public void onTaskStageChanged(int taskId, int stage, boolean visible) {
try {
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);
});
mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
}
};
public ISplitScreenImpl(SplitScreenController 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
public void registerSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
(controller) -> {
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);
});
(controller) -> mListener.register(listener));
}
@Override
public void unregisterSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
(controller) -> {
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
mListener = null;
controller.unregisterSplitScreenListener(mSplitScreenListener);
});
(controller) -> mListener.unregister());
}
@Override

View File

@@ -46,6 +46,7 @@ import com.android.internal.util.function.TriConsumer;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TransactionPool;
/**
@@ -237,24 +238,19 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
@BinderThread
private static class IStartingWindowImpl extends IStartingWindow.Stub {
private StartingWindowController mController;
private IStartingWindowListener mListener;
private SingleInstanceRemoteListener<StartingWindowController,
IStartingWindowListener> mListener;
private final TriConsumer<Integer, Integer, Integer> mStartingWindowListener =
this::notifyIStartingWindowListener;
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final StartingWindowController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.setStartingWindowListener(null);
});
}
(taskId, supportedType, startingWindowBackgroundColor) -> {
mListener.call(l -> l.onTaskLaunching(taskId, supportedType,
startingWindowBackgroundColor));
};
public IStartingWindowImpl(StartingWindowController 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) {
executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener",
(controller) -> {
if (mListener != null) {
// Reset the old death recipient
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.register(listener);
} else {
mListener.unregister();
}
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.
*/
void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition);
default void registerRemote(@NonNull TransitionFilter filter,
@NonNull RemoteTransition remoteTransition) {}
/**
* 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 */
public void register(ShellTaskOrganizer taskOrganizer) {
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.navigationbar.gestural.BackGestureTfClassifierProvider;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -133,7 +134,7 @@ public class SystemUIFactory {
.setShellCommandHandler(Optional.ofNullable(null))
.setAppPairs(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null))
.setTransitions(Transitions.createEmptyForTesting())
.setTransitions(new ShellTransitions() {})
.setDisplayAreaHelper(Optional.ofNullable(null))
.setStartingSurface(Optional.ofNullable(null))
.setTaskSurfaceHelper(Optional.ofNullable(null));