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

Commit e9d9b4b9 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issue #77230164: Add app op to control foreground services

New app op added.

Bug: 77230164
Test: atest CtsAppTestCases:ActivityManagerProcessStateTest\#testForegroundServiceAppOp
Change-Id: I59f2f03850da4b9f5550e82ba28f175e4779e783
parent b5dcf7d3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -300,6 +300,7 @@ package android.app {
    field public static final java.lang.String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
    field public static final java.lang.String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
    field public static final java.lang.String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
    field public static final java.lang.String OPSTR_START_FOREGROUND = "android:start_foreground";
    field public static final java.lang.String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
    field public static final java.lang.String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
    field public static final java.lang.String OPSTR_TOAST_WINDOW = "android:toast_window";
+1 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ package android.app {
    field public static final java.lang.String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
    field public static final java.lang.String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
    field public static final java.lang.String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
    field public static final java.lang.String OPSTR_START_FOREGROUND = "android:start_foreground";
    field public static final java.lang.String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
    field public static final java.lang.String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
    field public static final java.lang.String OPSTR_TOAST_WINDOW = "android:toast_window";
+16 −2
Original line number Diff line number Diff line
@@ -272,8 +272,10 @@ public class AppOpsManager {
    public static final int OP_ACCEPT_HANDOVER = 74;
    /** @hide Create and Manage IPsec Tunnels */
    public static final int OP_MANAGE_IPSEC_TUNNELS = 75;
    /** @hide Any app start foreground service. */
    public static final int OP_START_FOREGROUND = 76;
    /** @hide */
    public static final int _NUM_OP = 76;
    public static final int _NUM_OP = 77;

    /** Access to coarse location information. */
    public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -512,6 +514,9 @@ public class AppOpsManager {
    /** @hide */
    @SystemApi @TestApi
    public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
    /** @hide */
    @SystemApi @TestApi
    public static final String OPSTR_START_FOREGROUND = "android:start_foreground";

    // Warning: If an permission is added here it also has to be added to
    // com.android.packageinstaller.permission.utils.EventLogger
@@ -560,6 +565,7 @@ public class AppOpsManager {
            OP_SYSTEM_ALERT_WINDOW,
            OP_WRITE_SETTINGS,
            OP_REQUEST_INSTALL_PACKAGES,
            OP_START_FOREGROUND,
    };

    /**
@@ -647,6 +653,7 @@ public class AppOpsManager {
            OP_BIND_ACCESSIBILITY_SERVICE,
            OP_ACCEPT_HANDOVER,
            OP_MANAGE_IPSEC_TUNNELS,
            OP_START_FOREGROUND,
    };

    /**
@@ -729,6 +736,7 @@ public class AppOpsManager {
            OPSTR_BIND_ACCESSIBILITY_SERVICE,
            OPSTR_ACCEPT_HANDOVER,
            OPSTR_MANAGE_IPSEC_TUNNELS,
            OPSTR_START_FOREGROUND,
    };

    /**
@@ -812,6 +820,7 @@ public class AppOpsManager {
            "BIND_ACCESSIBILITY_SERVICE",
            "ACCEPT_HANDOVER",
            "MANAGE_IPSEC_TUNNELS",
            "START_FOREGROUND",
    };

    /**
@@ -895,6 +904,7 @@ public class AppOpsManager {
            Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
            Manifest.permission.ACCEPT_HANDOVER,
            null, // no permission for OP_MANAGE_IPSEC_TUNNELS
            Manifest.permission.FOREGROUND_SERVICE,
    };

    /**
@@ -979,6 +989,7 @@ public class AppOpsManager {
            null, // OP_BIND_ACCESSIBILITY_SERVICE
            null, // ACCEPT_HANDOVER
            null, // MANAGE_IPSEC_TUNNELS
            null, // START_FOREGROUND
    };

    /**
@@ -1062,6 +1073,7 @@ public class AppOpsManager {
            false, // OP_BIND_ACCESSIBILITY_SERVICE
            false, // ACCEPT_HANDOVER
            false, // MANAGE_IPSEC_HANDOVERS
            false, // START_FOREGROUND
    };

    /**
@@ -1144,6 +1156,7 @@ public class AppOpsManager {
            AppOpsManager.MODE_ALLOWED,  // OP_BIND_ACCESSIBILITY_SERVICE
            AppOpsManager.MODE_ALLOWED,  // ACCEPT_HANDOVER
            AppOpsManager.MODE_ERRORED,  // MANAGE_IPSEC_TUNNELS
            AppOpsManager.MODE_ALLOWED,  // OP_START_FOREGROUND
    };

    /**
@@ -1230,6 +1243,7 @@ public class AppOpsManager {
            false, // OP_BIND_ACCESSIBILITY_SERVICE
            false, // ACCEPT_HANDOVER
            false, // MANAGE_IPSEC_TUNNELS
            false, // START_FOREGROUND
    };

    /**
+126 −45
Original line number Diff line number Diff line
@@ -420,12 +420,36 @@ public final class ActiveServices {
        boolean forcedStandby = false;
        if (appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
            if (DEBUG_FOREGROUND_SERVICE) {
                Slog.d(TAG, "Forcing bg-only service start only for "
                        + r.name.flattenToShortString());
                Slog.d(TAG, "Forcing bg-only service start only for " + r.shortName);
            }
            forcedStandby = true;
        }

        // If this is a direct-to-foreground start, make sure it is allowed as per the app op.
        boolean forceSilentAbort = false;
        if (fgRequired) {
            final int mode = mAm.mAppOpsService.checkOperation(
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            switch (mode) {
                case AppOpsManager.MODE_ALLOWED:
                case AppOpsManager.MODE_DEFAULT:
                    // All okay.
                    break;
                case AppOpsManager.MODE_IGNORED:
                    // Not allowed, fall back to normal start service, failing siliently
                    // if background check restricts that.
                    Slog.w(TAG, "startForegroundService not allowed due to app op: service "
                            + service + " to " + r.name.flattenToShortString()
                            + " from pid=" + callingPid + " uid=" + callingUid
                            + " pkg=" + callingPackage);
                    fgRequired = false;
                    forceSilentAbort = true;
                    break;
                default:
                    return new ComponentName("!!", "foreground not allowed as per app op");
            }
        }

        // If this isn't a direct-to-foreground start, check our ability to kick off an
        // arbitrary service
        if (forcedStandby || (!r.startRequested && !fgRequired)) {
@@ -438,7 +462,7 @@ public final class ActiveServices {
                        + service + " to " + r.name.flattenToShortString()
                        + " from pid=" + callingPid + " uid=" + callingUid
                        + " pkg=" + callingPackage);
                if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
                if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
                    // In this case we are silently disabling the app, to disrupt as
                    // little as possible existing apps.
                    return null;
@@ -458,6 +482,7 @@ public final class ActiveServices {
        // is in the foreground passing it a pending intent to start the service when
        // review is completed.
        if (mAm.mPermissionReviewRequired) {
            // XXX This is not dealing with fgRequired!
            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
                    callingUid, service, callerFg, userId)) {
                return null;
@@ -474,6 +499,12 @@ public final class ActiveServices {
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants, callingUid));

        if (fgRequired) {
            // We are now effectively running a foreground service.
            mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
        }

        final ServiceMap smap = getServiceMapLocked(r.userId);
        boolean addToStarting = false;
        if (!callerFg && !fgRequired && r.app == null
@@ -1116,21 +1147,50 @@ public final class ActiveServices {
                        android.Manifest.permission.FOREGROUND_SERVICE,
                        r.app.pid, r.appInfo.uid, "startForeground");
            }
            boolean alreadyStartedOp = false;
            if (r.fgRequired) {
                if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
                    Slog.i(TAG, "Service called startForeground() as required: " + r);
                }
                r.fgRequired = false;
                r.fgWaiting = false;
                alreadyStartedOp = true;
                mAm.mHandler.removeMessages(
                        ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
            }

            try {
                boolean ignoreForeground = false;
                final int mode = mAm.mAppOpsService.checkOperation(
                        AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                switch (mode) {
                    case AppOpsManager.MODE_ALLOWED:
                    case AppOpsManager.MODE_DEFAULT:
                        // All okay.
                        break;
                    case AppOpsManager.MODE_IGNORED:
                        // Whoops, silently ignore this.
                        Slog.w(TAG, "Service.startForeground() not allowed due to app op: service "
                                + r.shortName);
                        ignoreForeground = true;
                        break;
                    default:
                        throw new SecurityException("Foreground not allowed as per app op");
                }

                if (!ignoreForeground &&
                        appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
                    ignoreForeground = true;
                    Slog.w(TAG,
                            "Service.startForeground() not allowed due to bg restriction: service "
                            + r.shortName);
                }

                // Apps under strict background restrictions simply don't get to have foreground
                // services, so now that we've enforced the startForegroundService() contract
                // we only do the machinery of making the service foreground when the app
                // is not restricted.
            if (!appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
                if (!ignoreForeground) {
                    if (r.foregroundId != id) {
                        cancelForegroundNotificationLocked(r);
                        r.foregroundId = id;
@@ -1159,6 +1219,10 @@ public final class ActiveServices {
                            active.mNumActive++;
                        }
                        r.isForeground = true;
                        mAm.mAppOpsService.startOperation(
                                AppOpsManager.getToken(mAm.mAppOpsService),
                                AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
                                true);
                        StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                                r.appInfo.uid, r.shortName,
                                StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
@@ -1175,6 +1239,15 @@ public final class ActiveServices {
                        Slog.d(TAG, "Suppressing startForeground() for FAS " + r);
                    }
                }
            } finally {
                if (alreadyStartedOp) {
                    // If we had previously done a start op for direct foreground start,
                    // we have cleared the flag so can now drop it.
                    mAm.mAppOpsService.finishOperation(
                            AppOpsManager.getToken(mAm.mAppOpsService),
                            AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                }
            }
        } else {
            if (r.isForeground) {
                final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -1182,6 +1255,9 @@ public final class ActiveServices {
                    decActiveForegroundAppLocked(smap, r);
                }
                r.isForeground = false;
                mAm.mAppOpsService.finishOperation(
                        AppOpsManager.getToken(mAm.mAppOpsService),
                        AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                        r.appInfo.uid, r.shortName,
                        StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
@@ -2561,6 +2637,8 @@ public final class ActiveServices {
                    + r);
            r.fgRequired = false;
            r.fgWaiting = false;
            mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
            if (r.app != null) {
@@ -2609,6 +2687,9 @@ public final class ActiveServices {
        cancelForegroundNotificationLocked(r);
        if (r.isForeground) {
            decActiveForegroundAppLocked(smap, r);
            mAm.mAppOpsService.finishOperation(
                    AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
                    StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
        }
+164 −57

File changed.

Preview size limit exceeded, changes collapsed.

Loading