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

Commit 4d0ef042 authored by James Cook's avatar James Cook
Browse files

Fix SysUI crash when launching Chrome from app shelf

* Store entire ActivityManager.RecentTaskInfo for each icon,
  not just the ComponentName.
* Use RecentTaskInfo.persistentId to check if an icon already
  exists.
* Extend RecentTaskInfo to include the actual activity that
  started the task.
* On dragging an icon to the pinned area, use the RecentTaskInfo
  to pin the primary launch intent of the app.

Bug: 22117860
Change-Id: Ia4412ea8e259e53cd7b5babcb79a292b92629db0
parent fa28d939
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -835,6 +835,13 @@ public class ActivityManager {
         */
        public ComponentName origActivity;

        /**
         * The actual activity component that started the task.
         * @hide
         */
        @Nullable
        public ComponentName realActivity;

        /**
         * Description of the task's last state.
         */
@@ -918,6 +925,7 @@ public class ActivityManager {
                dest.writeInt(0);
            }
            ComponentName.writeToParcel(origActivity, dest);
            ComponentName.writeToParcel(realActivity, dest);
            TextUtils.writeToParcel(description, dest,
                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            if (taskDescription != null) {
@@ -942,6 +950,7 @@ public class ActivityManager {
            persistentId = source.readInt();
            baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null;
            origActivity = ComponentName.readFromParcel(source);
            realActivity = ComponentName.readFromParcel(source);
            description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
            taskDescription = source.readInt() > 0 ?
                    TaskDescription.CREATOR.createFromParcel(source) : null;
+72 −59
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;

import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
@@ -27,9 +28,9 @@ import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -38,12 +39,10 @@ import android.widget.LinearLayout;
import com.android.systemui.R;

import java.util.List;
import java.util.Set;

/**
 * Recent task icons appearing in the navigation bar. Touching an icon brings the activity to the
 * front. There is a fixed set of icons and icons are hidden if there are fewer recent tasks than
 * icons.
 * front. The tag for each icon's View contains the RecentTaskInfo.
 */
class NavigationBarRecents extends LinearLayout {
    private final static boolean DEBUG = false;
@@ -56,15 +55,16 @@ class NavigationBarRecents extends LinearLayout {
    private final ActivityManager mActivityManager;
    private final PackageManager mPackageManager;
    private final LayoutInflater mLayoutInflater;
    // All icons share the same long-click listener.
    private final AppLongClickListener mAppLongClickListener;
    private final TaskStackListenerImpl mTaskStackListener;
    // Recent tasks being displayed in the shelf.
    private final Set<ComponentName> mCurrentTasks = new ArraySet<ComponentName>(MAX_RECENTS);

    public NavigationBarRecents(Context context, AttributeSet attrs) {
        super(context, attrs);
        mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        mPackageManager = getContext().getPackageManager();
        mLayoutInflater = LayoutInflater.from(context);
        mAppLongClickListener = new AppLongClickListener(context);

        // Listen for task stack changes and refresh when they happen. Update notifications happen
        // on an IPC thread, so use Handler to handle the message on the main thread.
@@ -84,7 +84,7 @@ class NavigationBarRecents extends LinearLayout {
    private void updateRecentApps() {
        // TODO: Should this be getRunningTasks?
        // TODO: Query other UserHandles?
        List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasksForUser(
        List<RecentTaskInfo> recentTasks = mActivityManager.getRecentTasksForUser(
                MAX_RECENTS,
                ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
@@ -96,89 +96,92 @@ class NavigationBarRecents extends LinearLayout {
        addNewRecents(recentTasks);
    }

    // Remove any icons that disappeared from recents.
    private void removeMissingRecents(List<ActivityManager.RecentTaskInfo> recentTasks) {
        // Extract the component names.
        Set<ComponentName> recentComponents = new ArraySet<ComponentName>(recentTasks.size());
        for (ActivityManager.RecentTaskInfo task : recentTasks) {
            ComponentName component = task.baseIntent.getComponent();
            if (component == null) {  // It's unclear if this can happen in practice.
                continue;
            }
            recentComponents.add(component);
        }

        // Start with a copy of the currently displayed tasks.
        Set<ComponentName> removed = new ArraySet<ComponentName>(mCurrentTasks);
        // Remove all the entries that still exist in recents.
        removed.removeAll(recentComponents);
        // The remaining entries no longer exist in recents, so remove their icons.
        for (ComponentName task : removed) {
            removeIcon(task);
        }
    // Removes any icons that disappeared from recents.
    private void removeMissingRecents(List<RecentTaskInfo> recentTasks) {
        // Build a set of the new task ids.
        SparseBooleanArray newTaskIds = new SparseBooleanArray();
        for (RecentTaskInfo task : recentTasks) {
            newTaskIds.put(task.persistentId, true);
        }

    // Removes the icon for a task.
    private void removeIcon(ComponentName task) {
        for (int i = 0; i < getChildCount(); i++) {
            ComponentName childTask = (ComponentName) getChildAt(i).getTag();
            if (childTask.equals(task)) {
                if (DEBUG) Slog.d(TAG, "removing missing " + task);
        // Iterate through the currently displayed tasks. If they no longer exist in recents,
        // remove them.
        int i = 0;
        while (i < getChildCount()) {
            RecentTaskInfo currentTask = (RecentTaskInfo) getChildAt(i).getTag();
            if (!newTaskIds.get(currentTask.persistentId)) {
                if (DEBUG) Slog.d(TAG, "Removing " + currentTask.baseIntent);
                removeViewAt(i);
                mCurrentTasks.remove(task);
                return;
            } else {
                i++;
            }
        }
    }

    // Adds new tasks at the end of the icon list.
    private void addNewRecents(List<ActivityManager.RecentTaskInfo> recentTasks) {
        for (ActivityManager.RecentTaskInfo task : recentTasks) {
    private void addNewRecents(List<RecentTaskInfo> recentTasks) {
        // Build a set of the current task ids.
        SparseBooleanArray currentTaskIds = new SparseBooleanArray();
        for (int i = 0; i < getChildCount(); i++) {
            RecentTaskInfo task = (RecentTaskInfo) getChildAt(i).getTag();
            currentTaskIds.put(task.persistentId, true);
        }

        // Add tasks that don't currently exist to the end of the view.
        for (RecentTaskInfo task : recentTasks) {
            // Don't overflow the list.
            if (getChildCount() >= MAX_RECENTS) {
                return;
            }
            ComponentName component = task.baseIntent.getComponent();
            if (component == null) {  // It's unclear if this can happen in practice.
                continue;
            }
            // Don't add tasks that are already being shown.
            if (mCurrentTasks.contains(component)) {
            if (currentTaskIds.get(task.persistentId)) {
                continue;
            }
            addRecentAppButton(task);
        }
    }

    // Adds an icon at the end of the list to represent an activity for a given component.
    private void addRecentAppButton(ActivityManager.RecentTaskInfo task) {
        // Add this task to the currently-shown set.
        ComponentName component = task.baseIntent.getComponent();
        mCurrentTasks.add(component);
        if (DEBUG) Slog.d(TAG, "adding " + component);
    // Adds an icon at the end of the shelf.
    private void addRecentAppButton(RecentTaskInfo task) {
        if (DEBUG) Slog.d(TAG, "Adding " + task.baseIntent);

        // Add an icon for the task.
        ImageView button = (ImageView) mLayoutInflater.inflate(
                R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
        button.setOnLongClickListener(AppLongClickListener.getInstance());
        button.setOnLongClickListener(mAppLongClickListener);
        addView(button);

        // Use the View's tag to store metadata for drag and drop.
        button.setTag(component);
        button.setTag(task);

        button.setVisibility(View.VISIBLE);
        // Load the activity icon on a background thread.
        new GetActivityIconTask(mPackageManager, button).execute(component);
        new GetActivityIconTask(mPackageManager, button).execute(getRealActivityForTask(task));

        final Intent baseIntent = task.baseIntent;
        final int taskPersistentId = task.persistentId;
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO: Consider using ActivityManager.moveTaskToFront(). This will need a task id.
                v.getContext().startActivity(baseIntent);
                // Launch or bring the activity to front.
                IActivityManager manager = ActivityManagerNative.getDefault();
                try {
                    manager.startActivityFromRecents(taskPersistentId, null /* options */);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private static ComponentName getRealActivityForTask(RecentTaskInfo task) {
        // Prefer the activity that started the task.
        if (task.realActivity != null) {
            return task.realActivity;
        }
        // This should not happen, but fall back to the base intent's activity component name.
        return task.baseIntent.getComponent();
    }

    /**
     * A listener that updates the app buttons whenever the recents task stack changes.
     * NOTE: This is not the right way to do this.
@@ -205,17 +208,27 @@ class NavigationBarRecents extends LinearLayout {

    /** Starts a drag on long-click on an app icon. */
    private static class AppLongClickListener implements View.OnLongClickListener {
        private static AppLongClickListener INSTANCE = new AppLongClickListener();
        private final Context mContext;

        public static AppLongClickListener getInstance() {
            return INSTANCE;
        public AppLongClickListener(Context context) {
            mContext = context;
        }

        @Override
        public boolean onLongClick(View v) {
            ImageView icon = (ImageView) v;
            ComponentName activityName = (ComponentName) v.getTag();
            NavigationBarApps.startAppDrag(icon, activityName);

            // The drag will go to the pinned section, which wants to launch the main activity
            // for the task's package.
            RecentTaskInfo task = (RecentTaskInfo) v.getTag();
            String packageName = getRealActivityForTask(task).getPackageName();
            Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
            if (intent == null) {
                return false;
            }

            if (DEBUG) Slog.d(TAG, "Start drag with " + intent);
            NavigationBarApps.startAppDrag(icon, intent.getComponent());
            return true;
        }
    }
+1 −0
Original line number Diff line number Diff line
@@ -8258,6 +8258,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        rti.persistentId = tr.taskId;
        rti.baseIntent = new Intent(tr.getBaseIntent());
        rti.origActivity = tr.origActivity;
        rti.realActivity = tr.realActivity;
        rti.description = tr.lastDescription;
        rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
        rti.userId = tr.userId;