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

Commit 17cf3c82 authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Query standby bucket and reason in a single call.

Introduces a new method to query the app standby bucket and its
corresponding reason in a single call.

Previously, the bucket and reason were fetched in two separate calls.
This created a race condition where the bucket could change between the
calls, leading to an inconsistent state for a job.

This change adds getAppStandbyBucketAndReason() to AppStandbyInternal
and UsageStatsManagerInternal to ensure this operation is atomic.
JobSchedulerService and related classes are updated to use this new
method.

Bug: 409606405
Test: atest ./services/tests/mockingservicestests/src/com/android/server/job/controllers/*Test.java
Test: atest ./services/tests/mockingservicestests/src/com/android/server/job/*Test.java
Flag: com.android.server.job.allow_cmp_exemption_for_restricted_bucket
Change-Id: If1acc273de06d91ce81356334567176d3f6a064a
parent ffe8d252
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessState;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManager.ForcedReasons;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.Context;
@@ -135,6 +136,22 @@ public interface AppStandbyInternal {
    int getAppStandbyBucket(String packageName, int userId,
            long elapsedRealtime, boolean shouldObfuscateInstantApps);

    /**
     * Returns the app that the app is currently in and bucketing reason code.
     *
     * <p>The returned long contains two pieces of information: the standby bucket in the higher 32
     * bits and the bucketing reason in the lower 32 bits. If the app is unknown for the given
     * user, the bucket will be {@link UsageStatsManager#STANDBY_BUCKET_NEVER} and the reason
     * will be {@link UsageStatsManager#REASON_MAIN_DEFAULT}.
     *
     * @param packageName The package to query
     * @param userId The user to which the package belongs.
     * @param elapsedRealtime The current time, in the elapsedRealtime time base
     * @return A long containing the standby bucket and the reason.
     */
    long getAppStandbyBucketAndReason(String packageName, int userId,
            long elapsedRealtime);

    List<AppStandbyInfo> getAppStandbyBuckets(int userId);

    /**
+13 −9
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IntPair;
import com.android.modules.expresslog.Counter;
import com.android.modules.expresslog.Histogram;
import com.android.server.AppSchedulingModuleThread;
@@ -4725,18 +4726,21 @@ public class JobSchedulerService extends com.android.server.SystemService
        return bucket;
    }

    public static int standbyBucketReasonForPackage(String packageName, int userId,
            long elapsedNow) {
        int reason = sUsageStatsManagerInternal != null
                ? sUsageStatsManagerInternal.getAppStandbyBucketReason(packageName, userId,
    public static long standbyBucketAndReasonForPackage(String packageName,
            int userId, long elapsedNow) {
        long bucketAndReason = sUsageStatsManagerInternal != null
                ? sUsageStatsManagerInternal.getAppStandbyBucketAndReason(packageName, userId,
                        elapsedNow)
                : UsageStatsManager.REASON_MAIN_DEFAULT;

                : 0;
        long bucketIndexAndReason = IntPair.of(
                standbyBucketToBucketIndex(IntPair.first(bucketAndReason)),
                IntPair.second(bucketAndReason));
        if (DEBUG_STANDBY) {
            Slog.v(TAG, packageName + "/" + userId + " standby bucket reason: "
                    + UsageStatsManager.reasonToString(reason));
            Slog.v(TAG, packageName + "/" + userId + " standby bucket index: "
                    + IntPair.first(bucketIndexAndReason) + " and reason: "
                    + IntPair.second(bucketIndexAndReason));
        }
        return reason;
        return bucketIndexAndReason;
    }

    static int safelyScaleBytesToKBForHistogram(long bytes) {
+4 −4
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.IntPair;
import com.android.modules.expresslog.Histogram;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -1515,13 +1516,12 @@ public final class JobStore {
            }

            // And now we're done
            final int appBucket = JobSchedulerService.standbyBucketForPackage(sourcePackageName,
                    sourceUserId, nowElapsed);
            final int appBucketReason = JobSchedulerService.standbyBucketReasonForPackage(
            final long appBucketAndReason = JobSchedulerService.standbyBucketAndReasonForPackage(
                    sourcePackageName, sourceUserId, nowElapsed);
            JobStatus js = new JobStatus(
                    builtJob, uid, intern(sourcePackageName), sourceUserId,
                    appBucket, appBucketReason, namespace, sourceTag,
                    IntPair.first(appBucketAndReason), IntPair.second(appBucketAndReason),
                    namespace, sourceTag,
                    elapsedRuntimes.first, elapsedRuntimes.second,
                    lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTime,
                    (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0);
+4 −9
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IntPair;
import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.job.Flags;
@@ -887,17 +888,11 @@ public final class JobStatus {
        }
        String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName();

        int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
                sourceUserId, elapsedNow);
        // TODO: b/409606405 - It's possible for where the standby bucket changes between the
        // time the bucket is queried and the reasoning for the bucket change is queried.
        // Although the standby bucket change callback will correct this eventually, it'd be
        // ideal to query the bucket and the reasoning in a single call to prevent potential
        // temporary inconsistent state.
        int standbyBucketReason = JobSchedulerService.standbyBucketReasonForPackage(
        long standbyBucketAndReason = JobSchedulerService.standbyBucketAndReasonForPackage(
                jobPackage, sourceUserId, elapsedNow);
        return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
                standbyBucket, standbyBucketReason, namespace, tag, /* numFailures */ 0,
                IntPair.first(standbyBucketAndReason), IntPair.second(standbyBucketAndReason),
                namespace, tag, /* numFailures */ 0,
                /* mNumAbandonedFailures */ 0, /* numSystemStops */ 0,
                earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
+11 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IntPair;
import com.android.internal.util.XmlUtils;

import libcore.io.IoUtils;
@@ -618,6 +619,16 @@ public class AppIdleHistory {
        return appUsageHistory == null ? STANDBY_BUCKET_NEVER : appUsageHistory.currentBucket;
    }

    public long getAppStandbyBucketAndReason(String packageName, int userId,
            long elapsedRealtime) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory appUsageHistory =
                getPackageHistory(userHistory, packageName, elapsedRealtime, false);
        return appUsageHistory == null
                ? IntPair.of(STANDBY_BUCKET_NEVER, REASON_MAIN_DEFAULT)
                : IntPair.of(appUsageHistory.currentBucket, appUsageHistory.bucketingReason);
    }

    public ArrayList<AppStandbyInfo> getAppStandbyBuckets(int userId, boolean appIdleEnabled) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        int size = userHistory.size();
Loading