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

Commit 88844c43 authored by Kweku Adams's avatar Kweku Adams
Browse files

Give installers exemptions after first setup.

Temporarily grant system installers VIP status for some time after
TARE's first setup so they can install and update all the apps they
need to. The temporary grant will be reapplied after reboots until
the allotted time has transpired. The VIP status means they won't
use any of their credits and will be able to do work even if the
stock/consumption limit is depleted. The installer exemption is set to
expire after the device has been on for 7 days, at which point,
the installer's maximum balance will be lowered back to normal levels
and the balance will eventually stabilize to normal levels. We will also
give installers on a newly added user the temporary exemptions.

Bug: 243588926
Test: reset device and check TARE dump
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/tare
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/tare
Change-Id: Ic468f652e30cc59a41e8034304ea90c54d60781e
parent 813816a4
Loading
Loading
Loading
Loading
+5 −6
Original line number Diff line number Diff line
@@ -286,7 +286,7 @@ class Agent {

        for (int i = 0; i < pkgNames.size(); ++i) {
            final String pkgName = pkgNames.valueAt(i);
            final boolean isVip = mIrs.isVip(userId, pkgName);
            final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed);
            SparseArrayMap<String, OngoingEvent> ongoingEvents =
                    mCurrentOngoingEvents.get(userId, pkgName);
            if (ongoingEvents != null) {
@@ -321,7 +321,7 @@ class Agent {
        final long nowElapsed = SystemClock.elapsedRealtime();
        final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();

        final boolean isVip = mIrs.isVip(userId, pkgName);
        final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed);
        SparseArrayMap<String, OngoingEvent> ongoingEvents =
                mCurrentOngoingEvents.get(userId, pkgName);
        if (ongoingEvents != null) {
@@ -397,7 +397,7 @@ class Agent {
                if (actionAffordabilityNotes != null) {
                    final int size = actionAffordabilityNotes.size();
                    final long newBalance = getBalanceLocked(userId, pkgName);
                    final boolean isVip = mIrs.isVip(userId, pkgName);
                    final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed);
                    for (int n = 0; n < size; ++n) {
                        final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
                        note.recalculateCosts(economicPolicy, userId, pkgName);
@@ -503,7 +503,8 @@ class Agent {
                    "Tried to adjust system balance for " + appToString(userId, pkgName));
            return;
        }
        if (mIrs.isVip(userId, pkgName)) {
        final boolean isVip = mIrs.isVip(userId, pkgName);
        if (isVip) {
            // This could happen if the app was made a VIP after it started performing actions.
            // Continue recording the transaction for debugging purposes, but don't let it change
            // any numbers.
@@ -536,7 +537,6 @@ class Agent {
                    mActionAffordabilityNotes.get(userId, pkgName);
            if (actionAffordabilityNotes != null) {
                final long newBalance = ledger.getCurrentBalance();
                final boolean isVip = mIrs.isVip(userId, pkgName);
                for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
                    final boolean isAffordable = isVip
@@ -830,7 +830,6 @@ class Agent {

    @GuardedBy("mLock")
    void onUserRemovedLocked(final int userId) {
        mScribe.discardLedgersLocked(userId);
        mCurrentOngoingEvents.delete(userId);
        mBalanceThresholdAlarmQueue.removeAlarmsForUserId(userId);
    }
+15 −1
Original line number Diff line number Diff line
@@ -16,14 +16,20 @@

package com.android.server.tare;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;

import com.android.internal.util.ArrayUtils;

/** POJO to cache only the information about installed packages that TARE cares about. */
class InstalledPackageInfo {
    static final int NO_UID = -1;
@@ -31,14 +37,22 @@ class InstalledPackageInfo {
    public final int uid;
    public final String packageName;
    public final boolean hasCode;
    public final boolean isSystemInstaller;
    @Nullable
    public final String installerPackageName;

    InstalledPackageInfo(@NonNull PackageInfo packageInfo) {
    InstalledPackageInfo(@NonNull Context context, @NonNull PackageInfo packageInfo) {
        final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
        uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
        packageName = packageInfo.packageName;
        hasCode = applicationInfo != null && applicationInfo.hasCode();
        isSystemInstaller = applicationInfo != null
                && ArrayUtils.indexOf(
                packageInfo.requestedPermissions, Manifest.permission.INSTALL_PACKAGES) >= 0
                && PackageManager.PERMISSION_GRANTED
                == PermissionChecker.checkPermissionForPreflight(context,
                Manifest.permission.INSTALL_PACKAGES, PermissionChecker.PID_UNKNOWN,
                applicationInfo.uid, packageName);
        InstallSourceInfo installSourceInfo = null;
        try {
            installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName);
+160 −4
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseLongArray;
import android.util.SparseSetArray;

import com.android.internal.annotations.GuardedBy;
@@ -107,6 +108,16 @@ public class InternalResourceService extends SystemService {
    private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS;
    /** The amount of time to delay reclamation by after boot. */
    private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
    /**
     * The amount of time after TARE has first been set up that a system installer will be allowed
     * expanded credit privileges.
     */
    static final long INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS = 7 * DAY_IN_MILLIS;
    /**
     * The amount of time to wait after TARE has first been set up before considering adjusting the
     * stock/consumption limit.
     */
    private static final long STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS = 5 * DAY_IN_MILLIS;
    /**
     * The battery level above which we may consider quantitative easing (increasing the consumption
     * limit).
@@ -127,7 +138,7 @@ public class InternalResourceService extends SystemService {
    private static final long STOCK_RECALCULATION_MIN_DATA_DURATION_MS = 8 * HOUR_IN_MILLIS;
    private static final int PACKAGE_QUERY_FLAGS =
            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                    | PackageManager.MATCH_APEX;
                    | PackageManager.MATCH_APEX | PackageManager.GET_PERMISSIONS;

    /** Global lock for all resource economy state. */
    private final Object mLock = new Object();
@@ -179,6 +190,13 @@ public class InternalResourceService extends SystemService {
    @GuardedBy("mLock")
    private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>();

    /**
     * Set of temporary Very Important Packages and when their VIP status ends, in the elapsed
     * realtime ({@link android.annotation.ElapsedRealtimeLong}) timebase.
     */
    @GuardedBy("mLock")
    private final SparseArrayMap<String, Long> mTemporaryVips = new SparseArrayMap<>();

    /** Set of apps each installer is responsible for installing. */
    @GuardedBy("mLock")
    private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>();
@@ -308,6 +326,7 @@ public class InternalResourceService extends SystemService {
    private static final int MSG_PROCESS_USAGE_EVENT = 2;
    private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3;
    private static final int MSG_NOTIFY_STATE_CHANGE_LISTENER = 4;
    private static final int MSG_CLEAN_UP_TEMP_VIP_LIST = 5;
    private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";

    /**
@@ -413,6 +432,13 @@ public class InternalResourceService extends SystemService {
        return userPkgs;
    }

    @Nullable
    InstalledPackageInfo getInstalledPackageInfo(final int userId, @NonNull final String pkgName) {
        synchronized (mLock) {
            return mPkgCache.get(userId, pkgName);
        }
    }

    @GuardedBy("mLock")
    long getConsumptionLimitLocked() {
        return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100;
@@ -429,6 +455,11 @@ public class InternalResourceService extends SystemService {
        return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
    }


    long getRealtimeSinceFirstSetupMs() {
        return mScribe.getRealtimeSinceFirstSetupMs(SystemClock.elapsedRealtime());
    }

    int getUid(final int userId, @NonNull final String pkgName) {
        synchronized (mPackageToUidCache) {
            Integer uid = mPackageToUidCache.get(userId, pkgName);
@@ -470,6 +501,10 @@ public class InternalResourceService extends SystemService {
    }

    boolean isVip(final int userId, @NonNull String pkgName) {
        return isVip(userId, pkgName, SystemClock.elapsedRealtime());
    }

    boolean isVip(final int userId, @NonNull String pkgName, final long nowElapsed) {
        synchronized (mLock) {
            final Boolean override = mVipOverrides.get(userId, pkgName);
            if (override != null) {
@@ -481,6 +516,12 @@ public class InternalResourceService extends SystemService {
            // operate.
            return true;
        }
        synchronized (mLock) {
            final Long expirationTimeElapsed = mTemporaryVips.get(userId, pkgName);
            if (expirationTimeElapsed != null) {
                return nowElapsed <= expirationTimeElapsed;
            }
        }
        return false;
    }

@@ -569,7 +610,7 @@ public class InternalResourceService extends SystemService {
            mPackageToUidCache.add(userId, pkgName, uid);
        }
        synchronized (mLock) {
            final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
            final InstalledPackageInfo ipo = new InstalledPackageInfo(getContext(), packageInfo);
            final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo);
            maybeUpdateInstallerStatusLocked(oldIpo, ipo);
            mUidToPackageCache.add(uid, pkgName);
@@ -626,11 +667,16 @@ public class InternalResourceService extends SystemService {
            final List<PackageInfo> pkgs =
                    mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
            for (int i = pkgs.size() - 1; i >= 0; --i) {
                final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
                final InstalledPackageInfo ipo =
                        new InstalledPackageInfo(getContext(), pkgs.get(i));
                final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
                maybeUpdateInstallerStatusLocked(oldIpo, ipo);
            }
            mAgent.grantBirthrightsLocked(userId);
            final long nowElapsed = SystemClock.elapsedRealtime();
            mScribe.setUserAddedTimeLocked(userId, nowElapsed);
            grantInstallersTemporaryVipStatusLocked(userId,
                    nowElapsed, INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS);
        }
    }

@@ -647,6 +693,7 @@ public class InternalResourceService extends SystemService {
            mInstallers.delete(userId);
            mPkgCache.delete(userId);
            mAgent.onUserRemovedLocked(userId);
            mScribe.onUserRemovedLocked(userId);
        }
    }

@@ -659,6 +706,10 @@ public class InternalResourceService extends SystemService {
            maybeAdjustDesiredStockLevelLocked();
            return;
        }
        if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) {
            // Things can be very tumultuous soon after first setup.
            return;
        }
        // We don't need to increase the limit if the device runs out of consumable credits
        // when the battery is low.
        final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
@@ -687,6 +738,10 @@ public class InternalResourceService extends SystemService {
        if (!mConfigObserver.ENABLE_TIP3) {
            return;
        }
        if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) {
            // Things can be very tumultuous soon after first setup.
            return;
        }
        // Don't adjust the limit too often or while the battery is low.
        final long now = getCurrentTimeMillis();
        if ((now - mScribe.getLastStockRecalculationTimeLocked()) < STOCK_RECALCULATION_DELAY_MS
@@ -775,6 +830,28 @@ public class InternalResourceService extends SystemService {
        mAgent.onCreditSupplyChanged();
    }

    @GuardedBy("mLock")
    private void grantInstallersTemporaryVipStatusLocked(int userId, long nowElapsed,
            long grantDurationMs) {
        final long grantEndTimeElapsed = nowElapsed + grantDurationMs;
        final int uIdx = mPkgCache.indexOfKey(userId);
        if (uIdx < 0) {
            return;
        }
        for (int pIdx = mPkgCache.numElementsForKey(uIdx) - 1; pIdx >= 0; --pIdx) {
            final InstalledPackageInfo ipo = mPkgCache.valueAt(uIdx, pIdx);

            if (ipo.isSystemInstaller) {
                final Long currentGrantEndTimeElapsed = mTemporaryVips.get(userId, ipo.packageName);
                if (currentGrantEndTimeElapsed == null
                        || currentGrantEndTimeElapsed < grantEndTimeElapsed) {
                    mTemporaryVips.add(userId, ipo.packageName, grantEndTimeElapsed);
                }
            }
        }
        mHandler.sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST, grantDurationMs);
    }

    @GuardedBy("mLock")
    private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
        if (!mIsEnabled) {
@@ -870,7 +947,8 @@ public class InternalResourceService extends SystemService {
            final List<PackageInfo> pkgs =
                    mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
            for (int i = pkgs.size() - 1; i >= 0; --i) {
                final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
                final InstalledPackageInfo ipo =
                        new InstalledPackageInfo(getContext(), pkgs.get(i));
                final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
                maybeUpdateInstallerStatusLocked(oldIpo, ipo);
            }
@@ -953,11 +1031,17 @@ public class InternalResourceService extends SystemService {
        synchronized (mLock) {
            mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
            loadInstalledPackageListLocked();
            final SparseLongArray timeSinceUsersAdded;
            final boolean isFirstSetup = !mScribe.recordExists();
            final long nowElapsed = SystemClock.elapsedRealtime();
            if (isFirstSetup) {
                mAgent.grantBirthrightsLocked();
                mScribe.setConsumptionLimitLocked(
                        mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
                // Set the last reclamation time to now so we don't start reclaiming assets
                // too early.
                mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
                timeSinceUsersAdded = new SparseLongArray();
            } else {
                mScribe.loadFromDiskLocked();
                if (mScribe.getSatiatedConsumptionLimitLocked()
@@ -971,6 +1055,21 @@ public class InternalResourceService extends SystemService {
                    // Adjust the supply in case battery level changed while the device was off.
                    adjustCreditSupplyLocked(true);
                }
                timeSinceUsersAdded = mScribe.getRealtimeSinceUsersAddedLocked(nowElapsed);
            }

            final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds();
            for (int userId : userIds) {
                final long timeSinceUserAddedMs = timeSinceUsersAdded.get(userId, 0);
                // Temporarily mark installers as VIPs so they aren't subject to credit
                // limits and policies on first boot.
                if (timeSinceUserAddedMs < INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS) {
                    final long remainingGraceDurationMs =
                            INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS - timeSinceUserAddedMs;

                    grantInstallersTemporaryVipStatusLocked(userId, nowElapsed,
                            remainingGraceDurationMs);
                }
            }
            scheduleUnusedWealthReclamationLocked();
        }
@@ -1079,6 +1178,36 @@ public class InternalResourceService extends SystemService {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_CLEAN_UP_TEMP_VIP_LIST: {
                    removeMessages(MSG_CLEAN_UP_TEMP_VIP_LIST);

                    synchronized (mLock) {
                        final long nowElapsed = SystemClock.elapsedRealtime();

                        long earliestExpiration = Long.MAX_VALUE;
                        for (int u = 0; u < mTemporaryVips.numMaps(); ++u) {
                            final int userId = mTemporaryVips.keyAt(u);

                            for (int p = mTemporaryVips.numElementsForKeyAt(u) - 1; p >= 0; --p) {
                                final String pkgName = mTemporaryVips.keyAt(u, p);
                                final Long expiration = mTemporaryVips.valueAt(u, p);

                                if (expiration == null || expiration < nowElapsed) {
                                    mTemporaryVips.delete(userId, pkgName);
                                } else {
                                    earliestExpiration = Math.min(earliestExpiration, expiration);
                                }
                            }
                        }

                        if (earliestExpiration < Long.MAX_VALUE) {
                            sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST,
                                    earliestExpiration - nowElapsed);
                        }
                    }
                }
                break;

                case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
                    final SomeArgs args = (SomeArgs) msg.obj;
                    final int userId = args.argi1;
@@ -1558,6 +1687,7 @@ public class InternalResourceService extends SystemService {
            boolean printedVips = false;
            pw.println();
            pw.print("VIPs:");
            pw.increaseIndent();
            for (int u = 0; u < mVipOverrides.numMaps(); ++u) {
                final int userId = mVipOverrides.keyAt(u);

@@ -1576,6 +1706,32 @@ public class InternalResourceService extends SystemService {
            } else {
                pw.print(" None");
            }
            pw.decreaseIndent();
            pw.println();

            boolean printedTempVips = false;
            pw.println();
            pw.print("Temp VIPs:");
            pw.increaseIndent();
            for (int u = 0; u < mTemporaryVips.numMaps(); ++u) {
                final int userId = mTemporaryVips.keyAt(u);

                for (int p = 0; p < mTemporaryVips.numElementsForKeyAt(u); ++p) {
                    final String pkgName = mTemporaryVips.keyAt(u, p);

                    printedTempVips = true;
                    pw.println();
                    pw.print(appToString(userId, pkgName));
                    pw.print("=");
                    pw.print(mTemporaryVips.valueAt(u, p));
                }
            }
            if (printedTempVips) {
                pw.println();
            } else {
                pw.print(" None");
            }
            pw.decreaseIndent();
            pw.println();

            pw.println();
+17 −0
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
import static com.android.server.tare.TareUtils.appToString;
import static com.android.server.tare.TareUtils.cakeToString;

import android.annotation.NonNull;
@@ -210,6 +211,22 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
        if (mIrs.isPackageRestricted(userId, pkgName)) {
            return 0;
        }
        final InstalledPackageInfo ipo = mIrs.getInstalledPackageInfo(userId, pkgName);
        if (ipo == null) {
            Slog.wtfStack(TAG,
                    "Tried to get max balance of invalid app: " + appToString(userId, pkgName));
        } else {
            // A system installer's max balance is elevated for some time after first boot so
            // they can use jobs to download and install apps.
            if (ipo.isSystemInstaller) {
                final long timeSinceFirstSetupMs = mIrs.getRealtimeSinceFirstSetupMs();
                final boolean stillExempted = timeSinceFirstSetupMs
                        < InternalResourceService.INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS;
                if (stillExempted) {
                    return mMaxSatiatedConsumptionLimit;
                }
            }
        }
        return mMaxSatiatedBalance;
    }

+56 −2

File changed.

Preview size limit exceeded, changes collapsed.

Loading