Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 01bc6c0a authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Handle WM Shell crash in MultitaskingController

If the multitasking controller delegate goes away because of WM Shell
process death, report to the client that all Bubbles it requested
were removed. Also allow registering a new delegate when SysUI comes
back.

Bug: 407149510
Flag: com.android.window.flags.enable_experimental_bubbles_controller
Test: Manual, force-stop com.android.systemui while using the demo
Change-Id: I76ac090689b4ede9821123eea9b22c0ccc678775
parent 5813eb45
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -37,11 +37,11 @@ import android.window.IMultitaskingDelegate;
 */
 */
interface IMultitaskingController {
interface IMultitaskingController {
    /**
    /**
     * Method used by WMShell to register itself as a delegate that can respond to the app requests.
     * Method used by WMShell to set itself as the delegate that can respond to the app requests.
     * @return a callback used to notify the client about the changes in the managed windows.
     * @return a callback used to notify the client about the changes in the managed windows.
     */
     */
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)")
    IMultitaskingControllerCallback registerMultitaskingDelegate(in IMultitaskingDelegate delegate);
    IMultitaskingControllerCallback setMultitaskingDelegate(in IMultitaskingDelegate delegate);


    /**
    /**
     * Returns an instance of an interface for use by applications to make requests to the system.
     * Returns an instance of an interface for use by applications to make requests to the system.
+1 −1
Original line number Original line Diff line number Diff line
@@ -558,7 +558,7 @@ public class BubbleController implements ConfigurationChangeListener,
                final IMultitaskingController mtController = ActivityTaskManager.getService()
                final IMultitaskingController mtController = ActivityTaskManager.getService()
                        .getWindowOrganizerController().getMultitaskingController();
                        .getWindowOrganizerController().getMultitaskingController();
                final IMultitaskingControllerCallback callback =
                final IMultitaskingControllerCallback callback =
                        mtController.registerMultitaskingDelegate(delegate);
                        mtController.setMultitaskingDelegate(delegate);
                mBubbleMultitaskingDelegate = delegate;
                mBubbleMultitaskingDelegate = delegate;
                mBubbleMultitaskingDelegate.setControllerCallback(callback);
                mBubbleMultitaskingDelegate.setControllerCallback(callback);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
+43 −21
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.Manifest.permission.REQUEST_SYSTEM_MULTITASKING_CONTROLS;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Intent;
import android.content.Intent;
@@ -55,27 +56,32 @@ class MultitaskingController extends IMultitaskingController.Stub {
    // All proxies indexed by calling process id.
    // All proxies indexed by calling process id.
    private final SparseArray<MultitaskingControllerProxy> mProxies = new SparseArray<>();
    private final SparseArray<MultitaskingControllerProxy> mProxies = new SparseArray<>();


    @Nullable
    private IMultitaskingDelegate mShellDelegate;
    private IMultitaskingDelegate mShellDelegate;


    private final DelegateCallback mDelegateCallback = new DelegateCallback();
    private final DelegateCallback mDelegateCallback = new DelegateCallback();


    private final DeathRecipient mShellDelegateDeathRecipient = new ShellDeathRecipient();

    @Override
    @Override
    public IMultitaskingControllerCallback registerMultitaskingDelegate(
    public IMultitaskingControllerCallback setMultitaskingDelegate(
            IMultitaskingDelegate delegate) {
            IMultitaskingDelegate delegate) {
        if (DEBUG) {
        if (DEBUG) {
            Slog.d(TAG, "registerMultitaskingDelegate: " + delegate);
            Slog.d(TAG, "setMultitaskingDelegate: " + delegate);
        }
        }
        enforceTaskPermission("registerMultitaskingDelegate()");
        enforceTaskPermission("setMultitaskingDelegate()");
        Objects.requireNonNull(delegate);
        Objects.requireNonNull(delegate);


        synchronized (this) {
        synchronized (this) {
            try {
                if (mShellDelegate != null) {
                if (mShellDelegate != null) {
                throw new IllegalStateException(
                    mShellDelegate.asBinder().unlinkToDeath(mShellDelegateDeathRecipient, 0);
                        "Cannot register more than one MultitaskingDelegate.");
                }
                }
            // TODO(b/407149510): Handle the case when SysUI crashes and remove the delegate or the
            // proxy, then init again when it comes back.
                mShellDelegate = delegate;
                mShellDelegate = delegate;
                mShellDelegate.asBinder().linkToDeath(mShellDelegateDeathRecipient, 0);
            } catch (RemoteException e) {
                throw new RuntimeException("Unable to set Shell delegate", e);
            }
        }
        }
        return mDelegateCallback;
        return mDelegateCallback;
    }
    }
@@ -98,8 +104,7 @@ class MultitaskingController extends IMultitaskingController.Stub {
            }
            }
            MultitaskingControllerProxy proxy = mProxies.get(callingPid);
            MultitaskingControllerProxy proxy = mProxies.get(callingPid);
            if (proxy == null) {
            if (proxy == null) {
                proxy = new MultitaskingControllerProxy(mShellDelegate, callback, callingPid,
                proxy = new MultitaskingControllerProxy(callback, callingPid, callingUid);
                        callingUid);
                try {
                try {
                    IBinder binder = callback.asBinder();
                    IBinder binder = callback.asBinder();
                    binder.linkToDeath(proxy, 0);
                    binder.linkToDeath(proxy, 0);
@@ -126,17 +131,13 @@ class MultitaskingController extends IMultitaskingController.Stub {
        final int mPid;
        final int mPid;
        final int mUid;
        final int mUid;
        final IMultitaskingControllerCallback mCallback;
        final IMultitaskingControllerCallback mCallback;
        @NonNull
        private final IMultitaskingDelegate mDelegate;
        private final List<IBinder> mBubbleTokens = new ArrayList<>();
        private final List<IBinder> mBubbleTokens = new ArrayList<>();


        MultitaskingControllerProxy(
        MultitaskingControllerProxy(
                @NonNull IMultitaskingDelegate delegate,
                @NonNull IMultitaskingControllerCallback callback,
                @NonNull IMultitaskingControllerCallback callback,
                int pid,
                int pid,
                int uid) {
                int uid) {
            Objects.requireNonNull(delegate);
            Objects.requireNonNull(callback);
            mDelegate = delegate;
            mCallback = callback;
            mCallback = callback;
            mPid = pid;
            mPid = pid;
            mUid = uid;
            mUid = uid;
@@ -165,7 +166,7 @@ class MultitaskingController extends IMultitaskingController.Stub {
            final long origId = Binder.clearCallingIdentity();
            final long origId = Binder.clearCallingIdentity();
            try {
            try {
                // TODO: sanitize the incoming intent?
                // TODO: sanitize the incoming intent?
                mDelegate.createBubble(token, intent, collapsed);
                mShellDelegate.createBubble(token, intent, collapsed);
                mBubbleTokens.add(token);
                mBubbleTokens.add(token);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Exception creating bubble", e);
                Slog.e(TAG, "Exception creating bubble", e);
@@ -189,7 +190,7 @@ class MultitaskingController extends IMultitaskingController.Stub {


            final long origId = Binder.clearCallingIdentity();
            final long origId = Binder.clearCallingIdentity();
            try {
            try {
                mDelegate.updateBubbleState(token, collapsed);
                mShellDelegate.updateBubbleState(token, collapsed);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Exception updating bubble state", e);
                Slog.e(TAG, "Exception updating bubble state", e);
            } finally {
            } finally {
@@ -212,7 +213,7 @@ class MultitaskingController extends IMultitaskingController.Stub {


            final long origId = Binder.clearCallingIdentity();
            final long origId = Binder.clearCallingIdentity();
            try {
            try {
                mDelegate.updateBubbleMessage(token, message);
                mShellDelegate.updateBubbleMessage(token, message);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Exception updating bubble message", e);
                Slog.e(TAG, "Exception updating bubble message", e);
            } finally {
            } finally {
@@ -235,7 +236,7 @@ class MultitaskingController extends IMultitaskingController.Stub {


            final long origId = Binder.clearCallingIdentity();
            final long origId = Binder.clearCallingIdentity();
            try {
            try {
                mDelegate.removeBubble(token);
                mShellDelegate.removeBubble(token);
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                Slog.e(TAG, "Exception removing bubble", e);
                Slog.e(TAG, "Exception removing bubble", e);
            } finally {
            } finally {
@@ -254,7 +255,7 @@ class MultitaskingController extends IMultitaskingController.Stub {
                try {
                try {
                    for (IBinder token : mBubbleTokens) {
                    for (IBinder token : mBubbleTokens) {
                        try {
                        try {
                            mDelegate.removeBubble(token);
                            mShellDelegate.removeBubble(token);
                        } catch (RemoteException e) {
                        } catch (RemoteException e) {
                            Slog.e(TAG, "Exception cleaning up bubbles for a dead binder", e);
                            Slog.e(TAG, "Exception cleaning up bubbles for a dead binder", e);
                        }
                        }
@@ -305,4 +306,25 @@ class MultitaskingController extends IMultitaskingController.Stub {
        Slog.w(TAG, msg);
        Slog.w(TAG, msg);
        throw new SecurityException(msg);
        throw new SecurityException(msg);
    }
    }

    private class ShellDeathRecipient implements DeathRecipient {
        @Override
        public void binderDied() {
            synchronized (this) {
                Slog.w(TAG, "Clearing MultitaskingController state - Shell binder death");
                mShellDelegate = null;
                for (int i = 0; i < mProxies.size(); i++) {
                    MultitaskingControllerProxy proxy = mProxies.valueAt(i);
                    for (IBinder token : proxy.mBubbleTokens) {
                        try {
                            proxy.mCallback.onBubbleRemoved(token);
                        } catch (RemoteException e) {
                            Slog.e(TAG, "Exception notifying client after Shell death", e);
                        }
                    }
                    proxy.mBubbleTokens.clear();
                }
            }
        }
    };
}
}