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

Commit 8783793c authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Added FGS logic change detection with WTF" into udc-qpr-dev

parents 7710f80a 37298b68
Loading
Loading
Loading
Loading
+68 −44
Original line number Diff line number Diff line
@@ -256,7 +256,7 @@ import java.util.function.Predicate;
public final class ActiveServices {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM;
    private static final String TAG_MU = TAG + POSTFIX_MU;
    private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
    static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
    private static final String TAG_SERVICE_EXECUTING = TAG + POSTFIX_SERVICE_EXECUTING;

    private static final boolean DEBUG_DELAYED_SERVICE = DEBUG_SERVICE;
@@ -850,8 +850,7 @@ public final class ActiveServices {
        // Service.startForeground()), at that point we will consult the BFSL check and the timeout
        // and make the necessary decisions.
        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
                backgroundStartPrivileges, false /* isBindService */,
                !fgRequired /* isStartService */);
                backgroundStartPrivileges, false /* isBindService */);

        if (!mAm.mUserController.exists(r.userId)) {
            Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
@@ -894,7 +893,7 @@ public final class ActiveServices {

        if (fgRequired) {
            logFgsBackgroundStart(r);
            if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) {
            if (!r.isFgsAllowedStart() && isBgFgsRestrictionEnabled(r)) {
                String msg = "startForegroundService() not allowed due to "
                        + "mAllowStartForeground false: service "
                        + r.shortInstanceName;
@@ -1060,7 +1059,7 @@ public final class ActiveServices {
            // Use that as a shortcut if possible to avoid having to recheck all the conditions.
            final boolean whileInUseAllowsUiJobScheduling =
                    ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs(
                            r.mAllowWhileInUsePermissionInFgsReason);
                            r.getFgsAllowWIU());
            r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling
                    || mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage));
        } else {
@@ -2178,12 +2177,12 @@ public final class ActiveServices {
                        // on a SHORT_SERVICE FGS.

                        // See if the app could start an FGS or not.
                        r.mAllowStartForeground = REASON_DENIED;
                        r.clearFgsAllowStart();
                        setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
                                r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                BackgroundStartPrivileges.NONE,
                                false /* isBindService */, false /* isStartService */);
                        if (r.mAllowStartForeground == REASON_DENIED) {
                                false /* isBindService */);
                        if (!r.isFgsAllowedStart()) {
                            Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
                                    + " BFSL DENIED.");
                        } else {
@@ -2191,13 +2190,13 @@ public final class ActiveServices {
                                Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
                                        + " BFSL Allowed: "
                                        + PowerExemptionManager.reasonCodeToString(
                                                r.mAllowStartForeground));
                                                r.getFgsAllowStart()));
                            }
                        }

                        final boolean fgsStartAllowed =
                                !isBgFgsRestrictionEnabledForService
                                        || (r.mAllowStartForeground != REASON_DENIED);
                                        || r.isFgsAllowedStart();

                        if (fgsStartAllowed) {
                            if (isNewTypeShortFgs) {
@@ -2246,7 +2245,7 @@ public final class ActiveServices {
                                setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
                                        r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                        BackgroundStartPrivileges.NONE,
                                        false /* isBindService */, false /* isStartService */);
                                        false /* isBindService */);
                                final String temp = "startForegroundDelayMs:" + delayMs;
                                if (r.mInfoAllowStartForeground != null) {
                                    r.mInfoAllowStartForeground += "; " + temp;
@@ -2266,20 +2265,21 @@ public final class ActiveServices {
                        setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
                                r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                BackgroundStartPrivileges.NONE,
                                false /* isBindService */, false /* isStartService */);
                                false /* isBindService */);
                    }

                    // If the foreground service is not started from TOP process, do not allow it to
                    // have while-in-use location/camera/microphone access.
                    if (!r.mAllowWhileInUsePermissionInFgs) {
                    if (!r.isFgsAllowedWIU()) {
                        Slog.w(TAG,
                                "Foreground service started from background can not have "
                                        + "location/camera/microphone access: service "
                                        + r.shortInstanceName);
                    }
                    r.maybeLogFgsLogicChange();
                    if (!bypassBfslCheck) {
                        logFgsBackgroundStart(r);
                        if (r.mAllowStartForeground == REASON_DENIED
                        if (!r.isFgsAllowedStart()
                                && isBgFgsRestrictionEnabledForService) {
                            final String msg = "Service.startForeground() not allowed due to "
                                    + "mAllowStartForeground false: service "
@@ -2378,9 +2378,9 @@ public final class ActiveServices {
                        // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
                        // be deferred, make a copy of mAllowStartForeground and
                        // mAllowWhileInUsePermissionInFgs.
                        r.mAllowStartForegroundAtEntering = r.mAllowStartForeground;
                        r.mAllowStartForegroundAtEntering = r.getFgsAllowStart();
                        r.mAllowWhileInUsePermissionInFgsAtEntering =
                                r.mAllowWhileInUsePermissionInFgs;
                                r.isFgsAllowedWIU();
                        r.mStartForegroundCount++;
                        r.mFgsEnterTime = SystemClock.uptimeMillis();
                        if (!stopProcStatsOp) {
@@ -2558,7 +2558,7 @@ public final class ActiveServices {
                policy.getForegroundServiceTypePolicyInfo(type, defaultToType);
        final @ForegroundServicePolicyCheckCode int code = policy.checkForegroundServiceTypePolicy(
                mAm.mContext, r.packageName, r.app.uid, r.app.getPid(),
                r.mAllowWhileInUsePermissionInFgs, policyInfo);
                r.isFgsAllowedWIU(), policyInfo);
        RuntimeException exception = null;
        switch (code) {
            case FGS_TYPE_POLICY_CHECK_DEPRECATED: {
@@ -3744,8 +3744,7 @@ public final class ActiveServices {
                }
            }
            setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
                    BackgroundStartPrivileges.NONE, true /* isBindService */,
                    false /* isStartService */);
                    BackgroundStartPrivileges.NONE, true /* isBindService */);

            if (s.app != null) {
                ProcessServiceRecord servicePsr = s.app.mServices;
@@ -7443,54 +7442,80 @@ public final class ActiveServices {
     * @param callingUid caller app's uid.
     * @param intent intent to start/bind service.
     * @param r the service to start.
     * @param isStartService True if it's called from Context.startService().
     *                       False if it's called from Context.startForegroundService() or
     *                       Service.startForeground().
     * @param isBindService True if it's called from bindService().
     * @return true if allow, false otherwise.
     */
    private void setFgsRestrictionLocked(String callingPackage,
            int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
            boolean isStartService) {
            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {

        @ReasonCode int allowWIU;
        @ReasonCode int allowStart;

        // If called from bindService(), do not update the actual fields, but instead
        // keep it in a separate set of fields.
        if (isBindService) {
            allowWIU = r.mAllowWIUInBindService;
            allowStart = r.mAllowStartInBindService;
        } else {
            allowWIU = r.mAllowWhileInUsePermissionInFgsReasonNoBinding;
            allowStart = r.mAllowStartForegroundNoBinding;
        }

        // Check DeviceConfig flag.
        if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
            if (!r.mAllowWhileInUsePermissionInFgs) {
            if (allowWIU == REASON_DENIED) {
                // BGFGS start restrictions are disabled. We're allowing while-in-use permissions.
                // Note REASON_OTHER since there's no other suitable reason.
                r.mAllowWhileInUsePermissionInFgsReason = REASON_OTHER;
                allowWIU = REASON_OTHER;
            }
            r.mAllowWhileInUsePermissionInFgs = true;
        }

        if (!r.mAllowWhileInUsePermissionInFgs
                || (r.mAllowStartForeground == REASON_DENIED)) {
        if ((allowWIU == REASON_DENIED)
                || (allowStart == REASON_DENIED)) {
            @ReasonCode final int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                    callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges);
            // We store them to compare the old and new while-in-use logics to each other.
            // (They're not used for any other purposes.)
            if (!r.mAllowWhileInUsePermissionInFgs) {
                r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
                r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
            if (allowWIU == REASON_DENIED) {
                allowWIU = allowWhileInUse;
            }
            if (r.mAllowStartForeground == REASON_DENIED) {
                r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
            if (allowStart == REASON_DENIED) {
                allowStart = shouldAllowFgsStartForegroundWithBindingCheckLocked(
                        allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
                        backgroundStartPrivileges, isBindService);
            }
        }

        if (isBindService) {
            r.mAllowWIUInBindService = allowWIU;
            r.mAllowStartInBindService = allowStart;
        } else {
            r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
            r.mAllowStartForegroundNoBinding = allowStart;

            // Also do a binding client check, unless called from bindService().
            if (r.mAllowWIUByBindings == REASON_DENIED) {
                r.mAllowWIUByBindings =
                        shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
            }
            if (r.mAllowStartByBindings == REASON_DENIED) {
                r.mAllowStartByBindings = r.mAllowWIUByBindings;
            }
        }
    }

    /**
     * Reset various while-in-use and BFSL related information.
     */
    void resetFgsRestrictionLocked(ServiceRecord r) {
        r.mAllowWhileInUsePermissionInFgs = false;
        r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
        r.mAllowStartForeground = REASON_DENIED;
        r.clearFgsAllowWIU();
        r.clearFgsAllowStart();

        r.mInfoAllowStartForeground = null;
        r.mInfoTempFgsAllowListReason = null;
        r.mLoggedInfoAllowStartForeground = false;
        r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs);
        r.updateAllowUiJobScheduling(r.isFgsAllowedWIU());
    }

    boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
@@ -8062,10 +8087,10 @@ public final class ActiveServices {
        */
        if (!r.mLoggedInfoAllowStartForeground) {
            final String msg = "Background started FGS: "
                    + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ")
                    + (r.isFgsAllowedStart() ? "Allowed " : "Disallowed ")
                    + r.mInfoAllowStartForeground
                    + (r.isShortFgs() ? " (Called on SHORT_SERVICE)" : "");
            if (r.mAllowStartForeground != REASON_DENIED) {
            if (r.isFgsAllowedStart()) {
                if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
                        mAm.mConstants.mFgsStartAllowedLogSampleRate)) {
                    Slog.wtfQuiet(TAG, msg);
@@ -8105,8 +8130,8 @@ public final class ActiveServices {
            allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering;
            fgsStartReasonCode = r.mAllowStartForegroundAtEntering;
        } else {
            allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgs;
            fgsStartReasonCode = r.mAllowStartForeground;
            allowWhileInUsePermissionInFgs = r.isFgsAllowedWIU();
            fgsStartReasonCode = r.getFgsAllowStart();
        }
        final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null
                ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0;
@@ -8295,8 +8320,7 @@ public final class ActiveServices {
        r.mFgsEnterTime = SystemClock.uptimeMillis();
        r.foregroundServiceType = options.mForegroundServiceTypes;
        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
                BackgroundStartPrivileges.NONE,  false /* isBindService */,
                false /* isStartService */);
                BackgroundStartPrivileges.NONE,  false /* isBindService */);
        final ProcessServiceRecord psr = callerApp.mServices;
        final boolean newService = psr.startService(r);
        // updateOomAdj.
+2 −2
Original line number Diff line number Diff line
@@ -479,8 +479,8 @@ public class ForegroundServiceTypeLoggerModule {
                r.appInfo.uid,
                r.shortInstanceName,
                fgsState, // FGS State
                r.mAllowWhileInUsePermissionInFgs, // allowWhileInUsePermissionInFgs
                r.mAllowStartForeground, // fgsStartReasonCode
                r.isFgsAllowedWIU(), // allowWhileInUsePermissionInFgs
                r.getFgsAllowStart(), // fgsStartReasonCode
                r.appInfo.targetSdkVersion,
                r.mRecentCallingUid,
                0, // callerTargetSdkVersion
+1 −1
Original line number Diff line number Diff line
@@ -2222,7 +2222,7 @@ public class OomAdjuster {

            if (s.isForeground) {
                final int fgsType = s.foregroundServiceType;
                if (s.mAllowWhileInUsePermissionInFgs) {
                if (s.isFgsAllowedWIU()) {
                    capabilityFromFGS |=
                            (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
                                    != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
+120 −8
Original line number Diff line number Diff line
@@ -21,9 +21,11 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE;
import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.reasonCodeToString;
import static android.os.Process.INVALID_UID;

import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActiveServices.TAG_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -172,11 +174,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
    private BackgroundStartPrivileges mBackgroundStartPrivilegesByStartMerged =
            BackgroundStartPrivileges.NONE;

    // allow while-in-use permissions in foreground service or not.
    // Reason code for allow while-in-use permissions in foreground service.
    // If it's not DENIED, while-in-use permissions are allowed.
    // while-in-use permissions in FGS started from background might be restricted.
    boolean mAllowWhileInUsePermissionInFgs;
    @PowerExemptionManager.ReasonCode
    int mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
    int mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED;

    // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
    boolean mAllowWhileInUsePermissionInFgsAtEntering;
@@ -205,15 +207,114 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN

    // allow the service becomes foreground service? Service started from background may not be
    // allowed to become a foreground service.
    @PowerExemptionManager.ReasonCode int mAllowStartForeground = REASON_DENIED;
    @PowerExemptionManager.ReasonCode
    int mAllowStartForegroundNoBinding = REASON_DENIED;
    // A copy of mAllowStartForeground's value when the service is entering FGS state.
    @PowerExemptionManager.ReasonCode int mAllowStartForegroundAtEntering = REASON_DENIED;
    @PowerExemptionManager.ReasonCode
    int mAllowStartForegroundAtEntering = REASON_DENIED;
    // Debug info why mAllowStartForeground is allowed or denied.
    String mInfoAllowStartForeground;
    // Debug info if mAllowStartForeground is allowed because of a temp-allowlist.
    ActivityManagerService.FgsTempAllowListItem mInfoTempFgsAllowListReason;
    // Is the same mInfoAllowStartForeground string has been logged before? Used for dedup.
    boolean mLoggedInfoAllowStartForeground;

    @PowerExemptionManager.ReasonCode
    int mAllowWIUInBindService = REASON_DENIED;

    @PowerExemptionManager.ReasonCode
    int mAllowWIUByBindings = REASON_DENIED;

    @PowerExemptionManager.ReasonCode
    int mAllowStartInBindService = REASON_DENIED;

    @PowerExemptionManager.ReasonCode
    int mAllowStartByBindings = REASON_DENIED;

    @PowerExemptionManager.ReasonCode
    int getFgsAllowWIU() {
        return mAllowWhileInUsePermissionInFgsReasonNoBinding != REASON_DENIED
                ? mAllowWhileInUsePermissionInFgsReasonNoBinding
                : mAllowWIUInBindService;
    }

    boolean isFgsAllowedWIU() {
        return getFgsAllowWIU() != REASON_DENIED;
    }

    @PowerExemptionManager.ReasonCode
    int getFgsAllowStart() {
        return mAllowStartForegroundNoBinding != REASON_DENIED
                ? mAllowStartForegroundNoBinding
                : mAllowStartInBindService;
    }

    boolean isFgsAllowedStart() {
        return getFgsAllowStart() != REASON_DENIED;
    }

    void clearFgsAllowWIU() {
        mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED;
        mAllowWIUInBindService = REASON_DENIED;
        mAllowWIUByBindings = REASON_DENIED;
    }

    void clearFgsAllowStart() {
        mAllowStartForegroundNoBinding = REASON_DENIED;
        mAllowStartInBindService = REASON_DENIED;
        mAllowStartByBindings = REASON_DENIED;
    }

    @PowerExemptionManager.ReasonCode
    int reasonOr(@PowerExemptionManager.ReasonCode int first,
            @PowerExemptionManager.ReasonCode int second) {
        return first != REASON_DENIED ? first : second;
    }

    boolean allowedChanged(@PowerExemptionManager.ReasonCode int first,
            @PowerExemptionManager.ReasonCode int second) {
        return (first == REASON_DENIED) != (second == REASON_DENIED);
    }

    String changeMessage(@PowerExemptionManager.ReasonCode int first,
            @PowerExemptionManager.ReasonCode int second) {
        return reasonOr(first, second) == REASON_DENIED ? "DENIED"
                : ("ALLOWED ("
                + reasonCodeToString(first)
                + "+"
                + reasonCodeToString(second)
                + ")");
    }

    void maybeLogFgsLogicChange() {
        final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
                mAllowWIUInBindService);
        final int newWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
                mAllowWIUByBindings);
        final int origStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartInBindService);
        final int newStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartByBindings);

        final boolean wiuChanged = allowedChanged(origWiu, newWiu);
        final boolean startChanged = allowedChanged(origStart, newStart);

        if (!wiuChanged && !startChanged) {
            return;
        }
        final String message = "FGS logic changed:"
                + (wiuChanged ? " [WIU changed]" : "")
                + (startChanged ? " [BFSL changed]" : "")
                + " OW:" // Orig-WIU
                + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding,
                        mAllowWIUInBindService)
                + " NW:" // New-WIU
                + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, mAllowWIUByBindings)
                + " OS:" // Orig-start
                + changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService)
                + " NS:" // New-start
                + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings);
        Slog.wtf(TAG_SERVICE, message);
    }

    // The number of times Service.startForeground() is called, after this service record is
    // created. (i.e. due to "bound" or "start".) It never decreases, even when stopForeground()
    // is called.
@@ -502,7 +603,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
        ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
        proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
        proto.write(ServiceRecordProto.ALLOW_WHILE_IN_USE_PERMISSION_IN_FGS,
                mAllowWhileInUsePermissionInFgs);
                isFgsAllowedWIU());

        if (startRequested || delayedStop || lastStartId != 0) {
            long startToken = proto.start(ServiceRecordProto.START);
@@ -618,7 +719,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
            pw.println(mBackgroundStartPrivilegesByStartMerged);
        }
        pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowWhileInUsePermissionInFgsReason));
        pw.println(PowerExemptionManager.reasonCodeToString(
                mAllowWhileInUsePermissionInFgsReasonNoBinding));

        pw.print(prefix); pw.print("mAllowWIUInBindService=");
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUInBindService));
        pw.print(prefix); pw.print("mAllowWIUByBindings=");
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUByBindings));

        pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
        pw.print(prefix); pw.print("recentCallingPackage=");
@@ -626,7 +733,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
        pw.print(prefix); pw.print("recentCallingUid=");
        pw.println(mRecentCallingUid);
        pw.print(prefix); pw.print("allowStartForeground=");
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForeground));
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForegroundNoBinding));
        pw.print(prefix); pw.print("mAllowStartInBindService=");
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartInBindService));
        pw.print(prefix); pw.print("mAllowStartByBindings=");
        pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartByBindings));

        pw.print(prefix); pw.print("startForegroundCount=");
        pw.println(mStartForegroundCount);
        pw.print(prefix); pw.print("infoAllowStartForeground=");