Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: programmatically open Overview/Recents screen in API 24+

Tags:

android

I am trying to open the Overview/Recents screen in Android. From Android: Programmatically open "Recent Apps" dialog, I can use the following code:

try {
    Class serviceManagerClass = Class.forName("android.os.ServiceManager");
    Method getService = serviceManagerClass.getMethod("getService", String.class);
    IBinder retbinder = (IBinder) getService.invoke(null, "statusbar");
    Class statusBarClass = Class.forName(retbinder.getInterfaceDescriptor());
    Object statusBarObject = statusBarClass.getClasses()[0]
            .getMethod("asInterface", IBinder.class).invoke(null, retbinder);
    Method toggleRecentApps = statusBarClass.getMethod("toggleRecentApps");
    toggleRecentApps.setAccessible(true);
    toggleRecentApps.invoke(statusBarObject);
} catch (InvocationTargetException| NoSuchMethodException | RemoteException | IllegalAccessException   | ClassNotFoundException e){
    handleError(e);
}

However, toggleRecentApps() is no longer in IStatusBarService since Nougat, and it causes a NoSuchMethodException.

Is there any other way (excluding AccessibilityService) that can be used to open the Overview/Recents screen?


EDIT: The class that implements IStatusBarService seems to be StatusBarManagerService. However, calling statusBarObject.getClass().getCanonicalName() returns com.android.internal.statusbar.IStatusBarService.Stub.Proxy, so getting an IStatusBar (which does implement toggleRecentApps()) through the private field mBar doesn't seem to be a working way to get it.


EDIT 2: Seeing that StatusBarManagerInternal.java is an interface that contains void toggleRecentApps(), I tried to find its implementation, which I found in StatusBarManagerService:

private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { ... }

So it's an anonymous class in StatusManagerService. >:(.

However, I also found:

/**
 * Construct the service, add the status bar view to the window manager
 */
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
    mContext = context;
    mWindowManager = windowManager;

    LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
}

So it apparently registers it in LocalServices, which in turn manages it in an ArrayMap:

private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
        new ArrayMap<Class<?>, Object>();

Therefore, I tried to access it:

try {
    Field services = Class.forName("com.android.server.LocalServices")
            .getDeclaredField("sLocalServiceObjects");
    services.setAccessible(true);
    ArrayMap<Class<?>, Object> serviceMap = (ArrayMap<Class<?>, Object>) services.get(null);
    Set<Map.Entry<Class<?>, Object>> serviceSet = serviceMap.entrySet();

    for (Map.Entry<Class<?>, Object> serviceEntry : serviceSet) {
        if (serviceEntry.getKey().isInterface()) {
            if ("com.android.server.statusbar.StatusBarManagerInternal"
                    .equals(serviceEntry.getKey().getName())) {
                Object statusBarInternalObject = serviceEntry.getValue();
                Class statusBarInternalClass = serviceEntry.getKey();
                Method openRecents = statusBarInternalClass.getMethod("toggleRecentApps");
                openRecents.setAccessible(true);
                openRecents.invoke(statusBarInternalObject);
                return;
            }
        }
    }
} catch (Exception e) {
    handleError(e);
}

However:

/**
 * This class is used in a similar way as ServiceManager, except the services registered here
 * are not Binder objects and are only available in the same process.
 *
 * Once all services are converted to the SystemService interface, this class can be absorbed
 * into SystemServiceManager.
 *
 * {@hide}
 */

Alas, I have to be in the SystemUI process (or so it seems) to be able to access it. (Effectively, the ArrayMap is empty, rendering my attempt useless.)

Any guidance on how to proceed from here would be appreciated.

like image 429
Jonathan Precise Avatar asked Jan 05 '17 19:01

Jonathan Precise


People also ask

What is the overview screen on Android?

One option a lot of developers overlook is the Overview Screen. As the developer docs state: The overview screen (also referred to as the recents screen, recent task list, or recent apps) is a system-level UI that lists recently accessed activities and tasks.

What is Samsung recents button?

The Recents key — the square you see in the bottom-right corner of most Android phones — is a staple of the OS, letting you see a card-deck of recently-used apps.

What is stack Android?

The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack.


1 Answers

Create service extended from AccessibilityService:

class ExampleAccessService:AccessibilityService() {
    override fun onInterrupt() {
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
    }

    fun doAction(){
        performGlobalAction(GLOBAL_ACTION_RECENTS)
//        performGlobalAction(GLOBAL_ACTION_BACK)
//        performGlobalAction(GLOBAL_ACTION_HOME)
//        performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS)
//        performGlobalAction(GLOBAL_ACTION_POWER_DIALOG)
//        performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS)
//        performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)
    }
}

Call doAction() where you want action

Add to Manifest:

<application
...
    <service
        android:name=".ExampleAccessService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:label="Name of servise" // it will be viewed in Settings->Accessibility->Services
        android:enabled="true"
        android:exported="false" >
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibility_service_config"/>
    </service>
...
</application>

accessibility_service_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="false"
    android:description="your description"
    android:notificationTimeout="100"
    android:packageNames="your app package, ex: ex: com.example.android"
    android:settingsActivity="your settings activity ex: com.example.android.MainActivity" />

for more info look at https://developer.android.com/guide/topics/ui/accessibility/services.html

like image 172
Evgenii Vorobei Avatar answered Oct 20 '22 12:10

Evgenii Vorobei