SoFunction
Updated on 2025-03-02

An example of implementation of Android background startup activity

Overview

A few days ago, the product raised a requirement and wanted to launch an activity of our APP in the background. With the update of the Android version and the unlimited transformation of various ROM manufacturers, many of these functions that affect the user experience have been restricted. There is no way. Although it is a relatively rogue function, it uses people's money to eliminate disasters, so it starts a humming research path.

Native Android ROM

First, I started with the native ROM of Android. According to the official introduction, the restrictions on the activity in the background started from Android 10 (API 29). Before that, there was no such restriction for native ROMs. So I started an emulator with Android 9 (API 28) and 10 (API 29) respectively. I found that Activity can be directly launched from the background on API 28, but on API 29, it was restricted and could not be launched directly. Refer to the officialStart Activity from the backgroundThe description of the restrictions of , gives some unrestricted exceptions. In addition, the official recommendation is to show the user a Notification instead of directly launching the Activity, and then process the corresponding logic after the user clicks on Notification. You can also add a full-screen Intent object through setFullScreenIntent when setting Notification. This method has been tested to start an Activity interface from the background on the simulator in Android 10 (requires .USE_FULL_SCREEN_INTENT permission). The code is as follows:

object NotificationUtils {
    private const val ID = "channel_1"
    private const val NAME = "notification"

    private var manager: NotificationManager? = null

    private fun getNotificationManagerManager(context: Context): NotificationManager? {
        if (manager == null) {
            manager = (Context.NOTIFICATION_SERVICE) as? NotificationManager
        }
        return manager
    }

    fun sendNotificationFullScreen(context: Context, title: String?, content: String?) {
        if (.SDK_INT >= 26) {
            clearAllNotification(context)
            val channel = NotificationChannel(ID, NAME, NotificationManager.IMPORTANCE_HIGH)
            (null, null)
            getNotificationManagerManager(context)?.createNotificationChannel(channel)
            val notification = getChannelNotificationQ(context, title, content)
            getNotificationManagerManager(context)?.notify(1, notification)
        }
    }

    private fun clearAllNotification(context: Context) {
        getNotificationManagerManager(context)?.cancelAll()
    }

    private fun getChannelNotificationQ(context: Context, title: String?, content: String?): Notification {
        val fullScreenPendingIntent = (
            context,
            0,
            (context),
            PendingIntent.FLAG_UPDATE_CURRENT
        )
        val notificationBuilder = (context, ID)
            .setSmallIcon(.ic_launcher_foreground)
            .setContentTitle(title)
            .setContentText(content)
            .setSound(null)
            .setPriority(NotificationCompat.PRIORITY_MAX)
            .setCategory(Notification.CATEGORY_CALL)
            .setOngoing(true)
            .setFullScreenIntent(fullScreenPendingIntent, true)
        return ()
    }
}

Until now, the overall feeling is still good. At this stage, the Android native ROM can normally launch the Activity interface from the background, and it is all very happy whether it is Android 9 or 10 versions.

Customized ROM

The problem began to surface. Since major manufacturers have different customizations of Android, and Android does not inherit the GPL protocol. It uses the Apache open source license agreement, that is, third-party manufacturers can close the source after modifying the code, so it is impossible to know what modifications the manufacturer's ROM source code has made. Some models have added a permission - the background pop-up interface. For example, this permission is added on MIUI and is turned off by default, unless they are added to their whitelist.Xiaomi open platformThe document explains: This permission is denied by default. The application does not allow pages to pop up in the background by default. A whitelist will be provided for special applications, such as music (lyrics display), sports, VOIP (call), etc.; once a whitelist occurs in promotion and other malicious behaviors, the whitelist will be permanently cancelled.

Detect the permissions of the background pop-up interface

On Xiaomi models, the new permissions for the background pop-up interface are expanded in AppOpsService. When viewing the AppOpsManager source code, you can see many familiar constants in it:

@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
    public static final int OP_GPS = 2;
    public static final int OP_READ_CONTACTS = 4;
    // ...
}

Therefore, you can use AppOpsService to detect whether you have permission to pop up the background interface. So what is the OpCode corresponding to this permission? An insider revealed that the Code for this permission is 10021, so you can use a series of methods such as or to detect whether the permission exists. However, these methods are identified by @hide and need to use reflection:

fun checkOpNoThrow(context: Context, op: Int): Boolean {
    val ops = (Context.APP_OPS_SERVICE) as AppOpsManager
    try {
        val method: Method = (
            "checkOpNoThrow", Int::, Int::, String::
        )
        val result = (ops, op, myUid(), ) as Int
        return result == AppOpsManager.MODE_ALLOWED
    } catch (e: Exception) {
        ()
    }
    return false
}

fun noteOpNoThrow(context: Context, op: Int): Int {
    val ops = (Context.APP_OPS_SERVICE) as AppOpsManager
    try {
        val method: Method = (
            "noteOpNoThrow", Int::, Int::, String::
        )
        return (ops, op, myUid(), ) as Int
    } catch (e: Exception) {
        ()
    }
    return -100
}

In addition, if you want to know other codes with newly added permissions, you can use the above method to traverse the permissions of the code in a certain range (such as 10000~10100), and then operate the mobile phone to switch the permissions you want to query. According to the results of the traversal, you can roughly get the code with the corresponding permissions.

Android P background startup permissions

Testing on Xiaomi Max3 found two ways to enable the Activity interface from the background, and its system is a MIUI system based on Android 9.

Method 1: moveTaskToFront

This method does not mean to directly start the Activity from the background, but to change the idea. Before starting the target Activity in the background, switch the application to the foreground, and then start the target Activity. If necessary, you can also use the method to re-moval the Activity that was switched to the foreground to be re-installed into the background. After testing, this method has expired on Android 10... However, versions below 10 can still be rescued (the .REORDER_TASKS permission is required).

Before starting the target Activity, first determine whether the application is in the background. The judgment method can be used to monitor the front and backends with the help of methods or . These two methods have articles on the Internet, so I won’t go into details. Directly post the code to switch to the front desk:

fun moveToFront(context: Context) {
    val activityManager = (Context.ACTIVITY_SERVICE) as? ActivityManager
    activityManager?.getRunningTasks(100)?.forEach { taskInfo ->
        if (?.packageName == ) {
            ("LLL", "Try to move to front")
            (, 0)
            return
        }
    }
}

fun startActivity(activity: Activity, intent: Intent) {
    if (!isRunningForeground(activity)) {
        ("LLL", "Now is in background")
        if (.SDK_INT < Build.VERSION_CODES.Q) {
            // TODO prevents moveToFront from failing, you can try to call it several times            moveToFront(activity)
            (intent)
            (true)
        } else {
            (activity, "", "")
        }
    } else {
        ("LLL", "Now is in foreground")
        (intent)
    }
}

Method 2: Hook

Since the MIUI system is not open source, try to study the AOSP source code again, and see if you can find any clues. First, start with the method. If you read the Activity startup source code process, you can know or call it in, and then call AMS-related methods through Binder. Permission authentication is completed in AMS. If the permissions do not meet the permissions, it will fail naturally (Android 10).

// APP processpublic ActivityResult execStartActivity(Context who, IBinder contextThread, ...) {
    // ...
    // Here you will call AMS-related code through Binder    int result = ().startActivity(whoThread, (),
            intent, (()),
            token, target != null ?  : null, requestCode, 0, null, options);
    // ...
}

// system_server process// AMS
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
    String resolvedType, IBinder resultTo, String resultWho, int requestCode,
    int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    // ...
}

Take a look at these parameters:

  • caller: After completing the relevant task, AMS will use it to call Binder to the client APP process to instantiate the Activity object and call back its lifecycle method. The caller's Binder server is located in the APP process.
  • callingPackage: This parameter identifies the caller package name.
  • ...

Here you can try some Hook system stuff. The specific code of Hook will not be given for the first time. After testing, it can be successful on the Xiaomi device of Android 9. If you are interested, you can study and talk about it yourself. It will not be made public for the time being. Students in need can leave a message to tell me. Or decompile the Xiaomi ROM source code and you can find some things from it.

Android Q background startup permissions

As mentioned above, the native system has also added the restrictions on background startup when starting the Android Q version. By notifying the fullScreenIntent, you can start the activity from the background on the native Android 10 system. Check the AOSP source code and you can find this part of the code for background permission restrictions in AMS. The above talks about the startActivity process. After the APP process initiates a request, it will call the AMS in the system_server process across the process through Binder, and then call the method. Here is the restrictions on background startup:

// Good guy, there are more than twenty parameters, hehe, heheprivate int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
        String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
        String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
        SafeActivityOptions options,
        boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
        TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
        PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
    // ...
    boolean abort = !(intent, aInfo, resultWho,
        requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
        inTask != null, callerApp, resultRecord, resultStack);
    abort |= !(intent, callingUid,
            callingPid, resolvedType, );
    abort |= !().checkStartActivity(intent, callingUid,
            callingPackage);

    boolean restrictedBgActivity = false;
    if (!abort) {
        restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                originatingPendingIntent, allowBackgroundActivityStart, intent);
    }
    // ...
}

The shouldAbortBackgroundActivityStart call here is newly added in Android Q. You can use the method name to search for the kitchen knife. This is for the background startup:

boolean shouldAbortBackgroundActivityStart(...) {
    final int callingAppId = (callingUid);
    if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
            || callingAppId == Process.NFC_UID) {
        return false;
    }
    if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
        return false;
    }
    // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
    if ((START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
            == PERMISSION_GRANTED) {
        return false;
    }
    // don't abort if the caller has the same uid as the recents component
    if ((callingUid)) {
        return false;
    }
    // don't abort if the callingUid is the device owner
    if ((callingUid)) {
        return false;
    }
    // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
    if ((callingUid, callingPid, callingPackage)) {
        (TAG, "Background activity start for " + callingPackage
                + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
        return false;
    }
    // ...
}

From this method, you can see the restrictions and official documents of background startupStart Activity from the backgroundThe description in the limitations of , can be used to determine permissions for uid , and is completed in the system process system_server , and simply changing the package name is useless. . .

On some ROMs that do not have separate restrictions on background startup, the background Activity page can be successfully popped up through full-screen notifications, such as Xiaomi A3, and also a vivo and a Samsung phone. I forgot the specific model; on devices that have been restricted, it cannot pop up, such as Redmi Note 8 Pro.

I have tried many methods for the hard bones of Redmi Note 8 Pro, but in fact I was just trying my luck, because I couldn't get the source code of MIUI. Later, I wanted to change my mind. Can I try to pull out the relevant package from this phone and decompile it? Maybe you will get something! However, it is necessary to have a Root mobile phone, which is easy to deal with. Xiaomi itself has a development version system that can be used for Root, so I went to the MIUI official website and found that this Redmi Note 8 Pro model does not provide a development version system (laughing and crying). I think it seems that I had said before that Xiaomi no longer provides a development version for the low-end phone. . . Well, there is no other phone to try in my hand.

Then think about it, can you directly download the stable version of the ROM package? Is there any tool that can get some traces of source code after decompression? So after downloading one, I unzipped it and saw that there were only some system image img files and . files. I don’t quite understand this. I guess even if I can get what I want, the time cost of the entire process will probably exceed expectations, so I can only put down this idea for the time being. There is enough time to study in depth in the future.

Summarize

Native Android ROM

Android native ROM can launch the Activity interface from the background normally, whether it is Android 9 (directly booted) or version 10 (with full screen notifications).

Customized ROM

Detect the background pop-up interface permissions:

  • Detect the permissions of the corresponding opCode through the reflection AppOpsManager-related methods;
  • opCode = 10021 (Xiaomi model);
  • Other models can try to traverse to get opCode;

Android P version of Xiaomi:

  • Use Hook-related parameters to start the activity in the background. The code cannot be given for some reasons. Students in need can leave a message to tell me;
  • Only Xiaomi models have been tested, other models may not be available;
  • In theory, Xiaomi below version P should also support it;

Android P version model:

  • Switch the application to the foreground through the moveTaskToFront method;
  • This approach is an official API after all, so compatibility may be better;
  • If the switch fails, you can try to call the moveTaskToFront method several times;
  • In theory, models below P version should also support them;

Android Q version model:

  • Adjust the backend activity through full-screen notification of the system;
  • The relay may fail on some ROMs that have been otherwise restricted;

As for the way to decompile MIUI code, it is just a guess, and time reasons failed to take action. It seems that the product brother’s needs cannot be fully realized for the time being. I wonder if anyone who has done relevant research (or knows the inside story) can provide some reference ideas. Although it is a relatively rogue function, the code is innocent. Hey, looking towards a demand goal, thinking about solutions for this and researching from all directions. I think it is an interesting and improved thing in itself! Students who have had relevant research are welcome to make suggestions in the comment area and make good demands.

The above is the detailed content of the implementation example of the Android background startup Activity. For more information about Android background startup Activity, please follow my other related articles!