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

5

9

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.

Allium answered 15/10, 2012 at 6:34 Comment(8)
I don't understand what kind of launcher you are writing...why should you delete the recent programs list? It just sounds to me like a security exploit, or at least like you are overwriting Android default behavior in a very unpleasant way. For example a malware program could hide itself...Ethnocentrism
Non exposed API is likely to change with every release/update, which is going to break your app. Better have your own recent apps list component.Biopsy
Hopefully, this is protected by a permission that only firmware apps can hold, even if there is a lame script-kiddie way to hack your way into it.Beastly
@Radu, some task manager allow user clean up the trace, the target APPs of Launcher just like Go Launcher, it could really clear the "recent task list". I need clear other apps history but not hide myself although I knew how to do it:). The current issue is how to get the instance of ActivityManagerService, I did some tests but still got negative result.Allium
@Beastly it seems to me that this is more on a par with removing browser history, or clearing cookies than "a permission that only firmware apps could hold". The "kiddie lock" situation is one that is inadequately handled in Android generally.Simple
@Ethnocentrism A malicious application hardly needs to use this technique to hide itself. When a program starts it gets to choose whether it appears in this list in the first place! Not to mention services that run in background, or which register themselves with AlarmManager, or in response to other broadcasts, in order to start periodically without appearing in the "Running Apps" list...Simple
@RedZhen Is there any possible way to do this?Bistro
@TimeManx Not found better way, we had given up the project.Allium
S
1

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.

Simple answered 27/4, 2013 at 23:43 Comment(4)
According to this link: #19350458 Your answer will not actually remove the tasks. You still need to add the permission to remove tasks, which does not work unless you are a signed developer of the ROM.Womanhood
Your method does not work for Android 6.0: removeTask() is no longer available.Wormeaten
removeTask() is no longer available for higher android versions, I guess after 6. But still, you can make use of the same method from android.app.ActivityTaskManager using reflection api'sNonagon
@BudhdiSharma Would you please post an answer with the new AcivityTaskManager? How do you get an instance of this service?Colewort
W
1

The best way I have found is to do this:

public class BaseActivity extends Activity {
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

            Log.d("Focus debug", "Focus changed !");

        if(!hasFocus) {
            Log.d("Focus debug", "Lost focus !");

            Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
            sendBroadcast(closeDialog);
        }
    }
}// all credit goes here: http://www.juliencavandoli.com/how-to-disable-recent-apps-dialog-on-long-press-home-button/

This is not my own code, but this just hides the recent apps list from showing.

Womanhood answered 6/12, 2013 at 9:57 Comment(1)
how can I call it in other activity?Quadrivial
S
0

There are some good clues regarding how to do this on this question here:

Altering the result of getRecentTasks

Following these, I have taken the following approach for my own purposes (a similar situation, where tablets are loaned to people and then someone has to "clear the history" before handing them to the next person).

Firstly, in my manifest, I created 18 tasks like this:

<activity
  android:name=".nul.Null0"
  android:enabled="false"
  android:excludeFromRecents="false"
  android:exported="false"
  android:label="n0"
  android:launchMode="singleInstance"
  android:stateNotNeeded="true"
  android:taskAffinity=""
  android:theme="@android:style/Theme.Translucent.NoTitleBar" >
  <intent-filter>
    <action android:name="com.morphoss.blah.welcome.nul.Null0" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

Secondly, in an AsyncTask that I start when the assistant start the "Welcome Screen" for the next person to use the tablet, after I clear the browser history and the cookies, I do the following:

try {
    PackageManager pm = getPackageManager();
    ComponentName cn;
    Intent nullActivity;
    for( int i=0; i<19; i++ ) {
        cn = new ComponentName("com.morphoss.blah.welcome","com.morphoss.blah.welcome.nul.Null"+i);
        pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
        nullActivity = new Intent();
        nullActivity.setComponent(cn);
        nullActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(nullActivity);
        Thread.sleep(20);
    }
    Thread.sleep(100);
    for( int i=0; i<18; i++ ) {
        cn = new ComponentName("com.morphoss.blah.welcome","com.morphoss.blah.welcome.nul.Null"+i);
        pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    }
}
catch( Exception e) {
    if ( Constants.LOG_DEBUG ) Log.d(TAG,Log.getStackTraceString(e));
}

While I have used "Thread.sleep(20)" between the launching of each task I have not fully examined exactly what is needed there, and perhaps a shorter time might work. For my purposes a 1 second delay while these are deleted is not problematic.

The final ingredients are the eighteen (very simple) activities that are being started, which look like this:

package com.morphoss.blah.welcome.nul;

import android.app.Activity;
import android.os.Bundle;

public class Null0 extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.finish();
    }
}

Hope this is helpful.

Simple answered 21/10, 2012 at 8:40 Comment(1)
Thanks, Karora, it can be a workable solution although our team don't think it's good enough. We hope the way of reflection can workAllium
B
0

I just met with the same problem. Okay, I don`t know how clear the recent list, but I know, how not to put your app there. start it with: FLAG_ACTIVITY_NO_HISTORY flag .

This worked for me, I hope I can help with it for you. Cheers, details: min SDK level 14

Branton answered 17/6, 2014 at 22:18 Comment(0)
L
0

To clear from all recent tasks:

ActivityTaskManager.getService().removeAllVisibleRecentTasks();

Lucullus answered 25/6, 2021 at 9:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.