Android, how to clear the recent task list which could get from Home button in most phone? Reflection is a possible way?

Red Zhen picture Red Zhen · Oct 15, 2012 · Viewed 18.7k times · Source

I'm writing a Launcher, it need clear the recent app/task list from system, not "didn't show my apps in recent tasks list", but I have no idea about it now. I have searched in the stackoverflow and only this issue is matched but the answer has no any help. Some other guy has asked the same questions, he metioned the RemoveTask which comes from Android 4.0. Yes, I have checked the source code of Android 2.3.7 and Android 4.0, at a round estimated, I think I could almost reach the end point if I can erase the list of mRecentTasks which defined in ActivityMangerService.Java :

final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();

And another maybe useful definition:

static ActivityManagerService mSelf;
public static ActivityManagerService self() {
    return mSelf;
}

Because I'm not familiar with Java&refelction, I need help about the way which clear this list, below is the code of my:

    public static <T> void clearRecentTaskList(Launcher launcher){
    ActivityManager am = (ActivityManager) launcher.getSystemService(Context.ACTIVITY_SERVICE);
    Object systemRecentTask = new ArrayList<T>();

    Object receiver = null;
    Field recentTaskList = null;
    Class<?> service = null;
    Field self = null;        


    try {
        service = Class.forName("com.android.server.am.ActivityManagerService");
        Log.d(LOG_TAG, "clearRecentTaskList, service gotton"+service.getName());
    } catch (ClassNotFoundException e2) {
        Log.d(LOG_TAG, "clearRecentTaskList, class service not found");
    }

    try {
        self = service.getDeclaredField("mSelf");
    } catch (SecurityException e2) {
        Log.d(LOG_TAG, "clearRecentTaskList, SecurityException during get mSelf");

    } catch (NoSuchFieldException e2) {
        Log.d(LOG_TAG, "clearRecentTaskList, NoSuchFieldException during get mSelf");
    }
    Log.d(LOG_TAG, "clearRecentTaskList, self  gotton " + self.toGenericString());

    try {
        self.setAccessible(true);
        receiver = self.get(null);
    } catch (IllegalArgumentException e2) {
        Log.d(LOG_TAG, "clearRecentTaskList, IllegalArgumentException during use self to get the receiver");
    } catch (IllegalAccessException e2) {
        Log.d(LOG_TAG, "clearRecentTaskList, IllegalAccessException during use self to get the receiver");
    }

    if ( receiver != null){
        Log.d(LOG_TAG, "clearRecentTaskList, receiver is : "+receiver.toString());
    } else {
        Log.d(LOG_TAG, "clearRecentTaskList, receiver is NULL");
    }


    try {
        recentTaskList = service.getDeclaredField("mRecentTasks");
        recentTaskList.setAccessible(true);
        Log.d(LOG_TAG, "clearRecentTaskList, recentTaskList gotton"+recentTaskList.toGenericString());

        try {
            systemRecentTask = recentTaskList.get(receiver);
        } catch (IllegalArgumentException e1) {
            Log.d(LOG_TAG, "IllegalArgumentException during try to clearRecentTask");
        } catch (IllegalAccessException e1) {
            Log.d(LOG_TAG, "IllegalAccessException during try to clearRecentTask");
        }

        Log.d(LOG_TAG, "Try to print the size of recent task: "+((ArrayList<T>) systemRecentTask).size());

    } catch (SecurityException e) {
        Log.d(LOG_TAG, "SecurityException during try to clearRecentTask");
    } catch (NoSuchFieldException e) {
        Log.d(LOG_TAG, "NoSuchFieldException during try to clearRecentTask");
    }   
}

With this function, I always meet the "NullPointerException" because receiver is null which got by self. And I have tried another way like this(If I remove the all try/catch):

self = service.getDeclaredMethod("mSelf", null);
receiver = self.invoke(null, null); // mSelf is a static method which in ActivityMangerService class

The same result, I can't get the instance of ActivityManagerService and then I can't get the mRecentTasks. Any comments is appreciated and although I don't know "how to remove all items in this list", but it could be another questions.

Thanks.

Answer

karora picture karora · Apr 28, 2013

While I could have edited my earlier answer, this solution I have used in a different project is so comprehensively different (and easier) that it is better to start afresh...

In this case I have simply used reflection to access the removeTask() method of the ActivityManager, and I've wrapped all of that into a convenience class of MyActivityManager as follows:

import java.lang.reflect.Method;

public class MyActivityManager {


    private ActivityManager mActivityManager = null;
    private Method mRemoveTask;

    public MyActivityManager(Context context) {
        try {
            Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
            mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

            mRemoveTask = activityManagerClass.getMethod("removeTask", new Class[] { int.class, int.class });
            mRemoveTask.setAccessible(true);

        }
        catch ( ClassNotFoundException e ) {
            Log.i("MyActivityManager", "No Such Class Found Exception", e);
        }
        catch ( Exception e ) {
            Log.i("MyActivityManager", "General Exception occurred", e);
        }
    }

    /**
     * If set, the process of the root activity of the task will be killed
     * as part of removing the task.
     */
    public static final int REMOVE_TASK_KILL_PROCESS = 0x0001;

    /**
     * Completely remove the given task.
     *
     * @param taskId Identifier of the task to be removed.
     * @param flags Additional operational flags.  May be 0 or
     * {@link #REMOVE_TASK_KILL_PROCESS}.
     * @return Returns true if the given task was found and removed.
     */
    public boolean removeTask(int taskId, int flags) {
        try {
            return (Boolean) mRemoveTask.invoke(mActivityManager, Integer.valueOf(taskId), Integer.valueOf(flags) );
        } catch (Exception ex) {
            Log.i("MyActivityManager", "Task removal failed", ex);
        }
        return false;
    }

    public void clearRecentTasks() {
        List<RecentTaskInfo> recents = mActivityManager.getRecentTasks(1000, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
        // Start from 1, since we don't want to kill ourselves!
        for( int i=1; i < recents.size(); i++ ) {
            removeTask( recents.get(i).persistentId, 0);
        }
    }

}

Then, when I want to clear the recent tasks I do the following:

new MyActivityManager(this).clearRecentTasks();

It's possible that one or both of these are needed for this to work:

<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />

In my case the application is installed with system permissions.