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

Commit 938a50b4 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

Refactor SystemServicesProxy to manage ITaskStackListener locally

This reduces the IPC calls between system service and SystemUI app.

Bug: 27635150
Change-Id: I6886edbc8e3736416af06c8de204f2a3b470ccd1
parent c545f5e6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -180,7 +180,7 @@ public class Recents extends SystemUI
    @Override
    public void start() {
        sDebugFlags = new RecentsDebugFlags(mContext);
        sSystemServicesProxy = new SystemServicesProxy(mContext);
        sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
        sTaskLoader = new RecentsTaskLoader(mContext);
        sConfiguration = new RecentsConfiguration(mContext);
        mHandler = new Handler();
+5 −29
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;

import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -59,6 +58,7 @@ import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -95,37 +95,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
    public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";

    /**
     * An implementation of ITaskStackListener, that allows us to listen for changes to the system
     * An implementation of TaskStackListener, that allows us to listen for changes to the system
     * task stacks and update recents accordingly.
     */
    class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
        Handler mHandler;

        public TaskStackListenerImpl(Handler handler) {
            mHandler = handler;
        }

    class TaskStackListenerImpl extends TaskStackListener {
        @Override
        public void onTaskStackChanged() {
            // Debounce any task stack changes
            mHandler.removeCallbacks(this);
            mHandler.post(this);
        }

        @Override
        public void onActivityPinned() {
        }

        @Override
        public void onPinnedActivityRestartAttempt() {
        }

        @Override
        public void onPinnedStackAnimationEnded() {
        }

        /** Preloads the next task */
        public void run() {
            // Preloads the next task
            RecentsConfiguration config = Recents.getConfiguration();
            if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
                RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -201,7 +177,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
        ForegroundThread.get();

        // Register the task stack listener
        mTaskStackListener = new TaskStackListenerImpl(mHandler);
        mTaskStackListener = new TaskStackListenerImpl();
        SystemServicesProxy ssp = Recents.getSystemServices();
        ssp.registerTaskStackListener(mTaskStackListener);

+120 −7
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -106,6 +109,8 @@ public class SystemServicesProxy {
        sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
    }

    private static SystemServicesProxy sSystemServicesProxy;

    AccessibilityManager mAccm;
    ActivityManager mAm;
    IActivityManager mIam;
@@ -128,8 +133,58 @@ public class SystemServicesProxy {
    Paint mBgProtectionPaint;
    Canvas mBgProtectionCanvas;

    private final Handler mHandler = new H();

    /**
     * An abstract class to track task stack changes.
     * Classes should implement this instead of {@link android.app.ITaskStackListener}
     * to reduce IPC calls from system services. These callbacks will be called on the main thread.
     */
    public abstract static class TaskStackListener {
        public void onTaskStackChanged() { }
        public void onActivityPinned() { }
        public void onPinnedActivityRestartAttempt() { }
        public void onPinnedStackAnimationEnded() { }
    }

    /**
     * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
     * ActivityManagerNative.
     * This simply passes callbacks to listeners through {@link H}.
     * */
    private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() {
        @Override
        public void onTaskStackChanged() throws RemoteException {
            mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
            mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
        }

        @Override
        public void onActivityPinned() throws RemoteException {
            mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
            mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED);
        }

        @Override
        public void onPinnedActivityRestartAttempt() throws RemoteException{
            mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
            mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
        }

        @Override
        public void onPinnedStackAnimationEnded() throws RemoteException {
            mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
            mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
        }
    };

    /**
     * List of {@link TaskStackListener} registered from {@link registerTaskStackListener}.
     */
    private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();

    /** Private constructor */
    public SystemServicesProxy(Context context) {
    private SystemServicesProxy(Context context) {
        mAccm = AccessibilityManager.getInstance(context);
        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        mIam = ActivityManagerNative.getDefault();
@@ -170,6 +225,20 @@ public class SystemServicesProxy {
        }
    }

    /**
     * Returns the single instance of the {@link SystemServicesProxy}.
     * This should only be called on the main thread.
     */
    public static SystemServicesProxy getInstance(Context context) {
        if (!Looper.getMainLooper().isCurrentThread()) {
            throw new RuntimeException("Must be called on the UI thread");
        }
        if (sSystemServicesProxy == null) {
            sSystemServicesProxy = new SystemServicesProxy(context);
        }
        return sSystemServicesProxy;
    }

    /** Returns a list of the recents tasks */
    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
            boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) {
@@ -982,14 +1051,21 @@ public class SystemServicesProxy {
        }
    }

    /** Registers a task stack listener with the system. */
    public void registerTaskStackListener(ITaskStackListener listener) {
    /**
     * Registers a task stack listener with the system.
     * This should be called on the main thread.
     */
    public void registerTaskStackListener(TaskStackListener listener) {
        if (mIam == null) return;

        mTaskStackListeners.add(listener);
        if (mTaskStackListeners.size() == 1) {
            // Register mTaskStackListener to IActivityManager only once if needed.
            try {
            mIam.registerTaskStackListener(listener);
                mIam.registerTaskStackListener(mTaskStackListener);
            } catch (Exception e) {
            e.printStackTrace();
                Log.w(TAG, "Failed to call registerTaskStackListener", e);
            }
        }
    }

@@ -1039,4 +1115,41 @@ public class SystemServicesProxy {
            e.printStackTrace();
        }
    }

    private final class H extends Handler {
        private static final int ON_TASK_STACK_CHANGED = 1;
        private static final int ON_ACTIVITY_PINNED = 2;
        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case ON_TASK_STACK_CHANGED: {
                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                        mTaskStackListeners.get(i).onTaskStackChanged();
                    }
                    break;
                }
                case ON_ACTIVITY_PINNED: {
                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                        mTaskStackListeners.get(i).onActivityPinned();
                    }
                    break;
                }
                case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                        mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
                    }
                    break;
                }
                case ON_PINNED_STACK_ANIMATION_ENDED: {
                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                        mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
                    }
                    break;
                }
            }
        }
    }
}
+5 −40
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.car;

import android.app.ActivityManager;
import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,6 +32,7 @@ import android.view.WindowManager;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.PhoneStatusBar;

@@ -40,9 +40,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar;
 * A status bar (and navigation bar) tailored for the automotive use case.
 */
public class CarStatusBar extends PhoneStatusBar {
    private SystemServicesProxy mSystemServicesProxy;
    private TaskStackListenerImpl mTaskStackListener;
    private Handler mHandler;

    private CarNavigationBarView mCarNavigationBar;
    private CarNavigationBarController mController;
@@ -51,10 +49,8 @@ public class CarStatusBar extends PhoneStatusBar {
    @Override
    public void start() {
        super.start();
        mHandler = new Handler();
        mTaskStackListener = new TaskStackListenerImpl(mHandler);
        mSystemServicesProxy = new SystemServicesProxy(mContext);
        mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
        mTaskStackListener = new TaskStackListenerImpl();
        SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
        registerPackageChangeReceivers();
    }

@@ -114,47 +110,16 @@ public class CarStatusBar extends PhoneStatusBar {
    }

    /**
     * An implementation of ITaskStackListener, that listens for changes in the system task
     * An implementation of TaskStackListener, that listens for changes in the system task
     * stack and notifies the navigation bar.
     */
    private class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
        private Handler mHandler;

        public TaskStackListenerImpl(Handler handler) {
            this.mHandler = handler;
        }

        @Override
        public void onActivityPinned() {
        }

        @Override
        public void onPinnedActivityRestartAttempt() {
        }

        @Override
        public void onPinnedStackAnimationEnded() {
        }

    private class TaskStackListenerImpl extends TaskStackListener {
        @Override
        public void onTaskStackChanged() {
            mHandler.removeCallbacks(this);
            mHandler.post(this);
        }

        @Override
        public void run() {
            ensureMainThread();
            SystemServicesProxy ssp = Recents.getSystemServices();
            ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
            mController.taskChanged(runningTaskInfo.baseActivity.getPackageName());
        }

        private void ensureMainThread() {
            if (!Looper.getMainLooper().isCurrentThread()) {
                throw new RuntimeException("Must be called on the UI thread");
            }
        }
    }

    @Override
+69 −102
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -38,6 +37,8 @@ import android.os.SystemProperties;
import android.util.Log;

import com.android.systemui.Prefs;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;

import java.util.ArrayList;
import java.util.List;
@@ -95,89 +96,6 @@ public class PipManager {
    private boolean mIsRecentsShown;
    private boolean mIsPipFocusedInRecent;

    private final Runnable mOnActivityPinnedRunnable = new Runnable() {
        @Override
        public void run() {
            StackInfo stackInfo = null;
            try {
                stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
                if (stackInfo == null) {
                    Log.w(TAG, "Cannot find pinned stack");
                    return;
                }
            } catch (RemoteException e) {
                Log.e(TAG, "getStackInfo failed", e);
                return;
            }
            if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
            mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
            mPipComponentName = ComponentName.unflattenFromString(
                    stackInfo.taskNames[stackInfo.taskNames.length - 1]);
            // Set state to overlay so we show it when the pinned stack animation ends.
            mState = STATE_PIP_OVERLAY;
            mCurrentPipBounds = mPipBounds;
            launchPipOnboardingActivityIfNeeded();
            mMediaSessionManager.addOnActiveSessionsChangedListener(
                    mActiveMediaSessionListener, null);
            updateMediaController(mMediaSessionManager.getActiveSessions(null));
            if (mIsRecentsShown) {
                // If an activity becomes PIPed again after the fullscreen, the Recents is shown
                // behind so we need to resize the pinned stack and show the correct overlay.
                resizePinnedStack(STATE_PIP_OVERLAY);
            }
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                mListeners.get(i).onPipEntered();
            }
        }
    };
    private final Runnable mOnTaskStackChanged = new Runnable() {
        @Override
        public void run() {
            if (mState != STATE_NO_PIP) {
                StackInfo stackInfo = null;
                try {
                    stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
                    if (stackInfo == null) {
                        Log.w(TAG, "There is no pinned stack");
                        closePipInternal(false);
                        return;
                    }
                } catch (RemoteException e) {
                    Log.e(TAG, "getStackInfo failed", e);
                    return;
                }
                for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
                    if (stackInfo.taskIds[i] == mPipTaskId) {
                        // PIP task is still alive.
                        return;
                    }
                }
                // PIP task doesn't exist anymore in PINNED_STACK.
                closePipInternal(true);
            }
        }
    };
    private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
        @Override
        public void run() {
            // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
            movePipToFullscreen();
        }
    };
    private final Runnable mOnPinnedStackAnimationEnded = new Runnable() {
        @Override
        public void run() {
            switch (mState) {
                case STATE_PIP_OVERLAY:
                    showPipOverlay();
                    break;
                case STATE_PIP_MENU:
                    showPipMenu();
                    break;
            }
        }
    };

    private final Runnable mResizePinnedStackRunnable = new Runnable() {
        @Override
        public void run() {
@@ -241,13 +159,7 @@ public class PipManager {
                (int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));

        mActivityManager = ActivityManagerNative.getDefault();
        TaskStackListener taskStackListener = new TaskStackListener();
        IActivityManager iam = ActivityManagerNative.getDefault();
        try {
            iam.registerTaskStackListener(taskStackListener);
        } catch (RemoteException e) {
            Log.e(TAG, "registerTaskStackListener failed", e);
        }
        SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
@@ -566,33 +478,88 @@ public class PipManager {
        return mPipMediaController;
    }

    private class TaskStackListener extends ITaskStackListener.Stub {
    TaskStackListener mTaskStackListener = new TaskStackListener() {
        @Override
        public void onTaskStackChanged() throws RemoteException {
            // Post the message back to the UI thread.
            mHandler.post(mOnTaskStackChanged);
        public void onTaskStackChanged() {
            if (mState != STATE_NO_PIP) {
                StackInfo stackInfo = null;
                try {
                    stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
                    if (stackInfo == null) {
                        Log.w(TAG, "There is no pinned stack");
                        closePipInternal(false);
                        return;
                    }
                } catch (RemoteException e) {
                    Log.e(TAG, "getStackInfo failed", e);
                    return;
                }
                for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
                    if (stackInfo.taskIds[i] == mPipTaskId) {
                        // PIP task is still alive.
                        return;
                    }
                }
                // PIP task doesn't exist anymore in PINNED_STACK.
                closePipInternal(true);
            }
        }

        @Override
        public void onActivityPinned()  throws RemoteException {
            // Post the message back to the UI thread.
        public void onActivityPinned() {
            if (DEBUG) Log.d(TAG, "onActivityPinned()");
            mHandler.post(mOnActivityPinnedRunnable);
            StackInfo stackInfo = null;
            try {
                stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
                if (stackInfo == null) {
                    Log.w(TAG, "Cannot find pinned stack");
                    return;
                }
            } catch (RemoteException e) {
                Log.e(TAG, "getStackInfo failed", e);
                return;
            }
            if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
            mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
            mPipComponentName = ComponentName.unflattenFromString(
                    stackInfo.taskNames[stackInfo.taskNames.length - 1]);
            // Set state to overlay so we show it when the pinned stack animation ends.
            mState = STATE_PIP_OVERLAY;
            mCurrentPipBounds = mPipBounds;
            launchPipOnboardingActivityIfNeeded();
            mMediaSessionManager.addOnActiveSessionsChangedListener(
                    mActiveMediaSessionListener, null);
            updateMediaController(mMediaSessionManager.getActiveSessions(null));
            if (mIsRecentsShown) {
                // If an activity becomes PIPed again after the fullscreen, the Recents is shown
                // behind so we need to resize the pinned stack and show the correct overlay.
                resizePinnedStack(STATE_PIP_OVERLAY);
            }
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                mListeners.get(i).onPipEntered();
            }
        }

        @Override
        public void onPinnedActivityRestartAttempt() {
            // Post the message back to the UI thread.
            if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
            mHandler.post(mOnPinnedActivityRestartAttempt);
            // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
            movePipToFullscreen();
        }

        @Override
        public void onPinnedStackAnimationEnded() {
            if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
            mHandler.post(mOnPinnedStackAnimationEnded);
            switch (mState) {
                case STATE_PIP_OVERLAY:
                    showPipOverlay();
                    break;
                case STATE_PIP_MENU:
                    showPipMenu();
                    break;
            }
        }
    };

    /**
     * A listener interface to receive notification on changes in PIP.