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

Commit e069b002 authored by Michal Karpinski's avatar Michal Karpinski
Browse files

Add new BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS binding flag

and the mechanism to use it

Since both starts and bindings might be contributing to the service
being whitelisted, track the state in ServiceRecord and update
the parent process accordingly when changes happen.

While we're at it, add that flag for bindings to accessibility
services, voice interaction services and QSTiles.

Also, make the timeout constant configurable for start-based
whitelisting.

Bug: 124298522
Bug: 123495976 (accessibility services)
Bug: 123337181 (paying via NFC, like Google Pay)
Bug: 122887611 (binding to InCallService)
Bug: 127774234 (binding to QSTile)
Bug: 124288681 (binding to voice interaction services)
Test: atest WmTests:ActivityStarterTests
Test: atest AccessibilityServiceConnectionTest
Test: manual with:
1) notifications from calendar and dynamite to test service starts
whitelisting
2) enabling talkback and livetranscribe to test service bindings
whitelisting
3) adding the LastPass QSTile and clicking on it
Change-Id: I8b6f23f999683920411ca40ab5b25c688a4f7055
parent 794d22d0
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -337,6 +337,15 @@ public abstract class Context {
     */
    public static final int BIND_ADJUST_BELOW_PERCEPTIBLE = 0x0100;

    /**
     * Flag for {@link #bindService}: allow background activity starts from the bound service's
     * process.
     * This flag is only respected if the caller is holding
     * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}.
     * @hide
     */
    public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000;

    /**
     * @hide Flag for {@link #bindService}: the service being bound to represents a
     * protected system component, so must have association restrictions applied to it.
+1 −0
Original line number Diff line number Diff line
@@ -11508,6 +11508,7 @@ public final class Settings {
         * service_min_restart_time_between     (long)
         * service_max_inactivity               (long)
         * service_bg_start_timeout             (long)
         * service_bg_activity_start_timeout    (long)
         * process_start_async                  (boolean)
         * </pre>
         *
+2 −2
Original line number Diff line number Diff line
@@ -159,8 +159,8 @@ public class TileLifecycleManager extends BroadcastReceiver implements
            mBindTryCount++;
            try {
                mIsBound = mContext.bindServiceAsUser(mIntent, this,
                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                        mUser);
                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, mUser);
            } catch (SecurityException e) {
                Log.e(TAG, "Failed to bind to service", e);
                mIsBound = false;
+2 −1
Original line number Diff line number Diff line
@@ -91,7 +91,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
        if (userState == null) return;
        final long identity = Binder.clearCallingIdentity();
        try {
            int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE;
            int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
                    | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
            if (userState.getBindInstantServiceAllowed()) {
                flags |= Context.BIND_ALLOW_INSTANT;
            }
+49 −44
Original line number Diff line number Diff line
@@ -131,9 +131,6 @@ public final class ActiveServices {
    // calling startForeground() before we ANR + stop it.
    static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

    // For how long after a whitelisted service's start its process can start a background activity
    private static final int SERVICE_BG_ACTIVITY_START_TIMEOUT_MS = 10*1000;

    final ActivityManagerService mAm;

    // Maximum number of services that we allow to start in the background
@@ -333,8 +330,8 @@ public final class ActiveServices {
                            + " delayedStop=" + r.delayedStop);
                } else {
                    try {
                        startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true,
                                false);
                        startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false,
                                true);
                    } catch (TransactionTooLargeException e) {
                        // Ignore, nobody upstack cares.
                    }
@@ -637,26 +634,26 @@ public final class ActiveServices {
        }

        if (allowBackgroundActivityStarts) {
            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
            if (proc != null) {
                proc.addAllowBackgroundActivityStartsToken(r);
                // schedule removal of the whitelisting token after the timeout
                removeAllowBackgroundActivityStartsServiceToken(proc, r,
                        SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
            r.hasStartedWhitelistingBgActivityStarts = true;
            scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(r);
        }
        }
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
                allowBackgroundActivityStarts);

        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        return cmp;
    }

    private void removeAllowBackgroundActivityStartsServiceToken(ProcessRecord proc,
            ServiceRecord r, int delayMillis) {
        mAm.mHandler.postDelayed(() -> {
            if (proc != null) {
                proc.removeAllowBackgroundActivityStartsToken(r);
    private void scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(ServiceRecord r) {
        // if there's a request pending from the past, drop it before scheduling a new one
        if (r.startedWhitelistingBgActivityStartsCleanUp == null) {
            r.startedWhitelistingBgActivityStartsCleanUp = () -> {
                synchronized(mAm) {
                    r.setHasStartedWhitelistingBgActivityStarts(false);
                }
        }, delayMillis);
            };
        }
        mAm.mHandler.removeCallbacks(r.startedWhitelistingBgActivityStartsCleanUp);
        mAm.mHandler.postDelayed(r.startedWhitelistingBgActivityStartsCleanUp,
                mAm.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
    }

    private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
@@ -705,8 +702,7 @@ public final class ActiveServices {
    }

    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting, boolean allowBackgroundActivityStarts)
            throws TransactionTooLargeException {
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
@@ -717,8 +713,7 @@ public final class ActiveServices {
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false,
                allowBackgroundActivityStarts);
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        if (error != null) {
            return new ComponentName("!!", error);
        }
@@ -765,6 +760,12 @@ public final class ActiveServices {
                    SystemClock.uptimeMillis());
        }
        service.callStart = false;

        // the service will not necessarily be brought down, so only clear the whitelisting state
        // for start-based bg activity starts now, and drop any existing future cleanup callback
        service.setHasStartedWhitelistingBgActivityStarts(false);
        mAm.mHandler.removeCallbacks(service.startedWhitelistingBgActivityStartsCleanUp);

        bringDownServiceIfNeededLocked(service, false, false);
    }

@@ -788,9 +789,6 @@ public final class ActiveServices {
            if (r.record != null) {
                final long origId = Binder.clearCallingIdentity();
                try {
                    // immediately remove bg activity whitelisting token if there was one
                    removeAllowBackgroundActivityStartsServiceToken(callerApp, r.record,
                            0 /* delayMillis */);
                    stopServiceLocked(r.record);
                } finally {
                    Binder.restoreCallingIdentity(origId);
@@ -1614,6 +1612,12 @@ public final class ActiveServices {
                            + ") set BIND_ALLOW_INSTANT when binding service " + service);
        }

        if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
            mAm.enforceCallingPermission(
                    android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
                    "BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS");
        }

        final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
        final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
        final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
@@ -1672,7 +1676,7 @@ public final class ActiveServices {
                                try {
                                    bringUpServiceLocked(serviceRecord,
                                            serviceIntent.getFlags(),
                                            callerFg, false, false, false);
                                            callerFg, false, false);
                                } catch (RemoteException e) {
                                    /* ignore - local call */
                                }
@@ -1762,6 +1766,9 @@ public final class ActiveServices {
            if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
                s.whitelistManager = true;
            }
            if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                s.setHasBindingWhitelistingBgActivityStarts(true);
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app, c, true);
            }
@@ -1775,7 +1782,7 @@ public final class ActiveServices {
            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired, false) != null) {
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }
@@ -2445,8 +2452,7 @@ public final class ActiveServices {
            return;
        }
        try {
            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false,
                    false);
            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
        } catch (TransactionTooLargeException e) {
            // Ignore, it's been logged and nothing upstack cares.
        }
@@ -2491,11 +2497,8 @@ public final class ActiveServices {
    }

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired,
            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
        //Slog.i(TAG, "Bring up service:");
        //r.dump("  ");

            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
        if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
@@ -2603,13 +2606,6 @@ public final class ActiveServices {
            }
        }

        if (app != null && allowBackgroundActivityStarts) {
            app.addAllowBackgroundActivityStartsToken(r);
            // schedule removal of the whitelisting token after the timeout
            removeAllowBackgroundActivityStartsServiceToken(app, r,
                    SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
        }

        if (r.fgRequired) {
            if (DEBUG_FOREGROUND_SERVICE) {
                Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
@@ -2646,6 +2642,11 @@ public final class ActiveServices {
        }
    }

    /**
     * Note the name of this method should not be confused with the started services concept.
     * The "start" here means bring up the instance in the client, and this method is called
     * from bindService() as well.
     */
    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
@@ -3085,6 +3086,10 @@ public final class ActiveServices {
                    updateWhitelistManagerLocked(s.app);
                }
            }
            // And do the same for bg activity starts whitelisting.
            if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                s.updateHasBindingWhitelistingBgActivityStarts();
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app, c, true);
            }
Loading