Merge "Increase app updater balances."

This commit is contained in:
TreeHugger Robot
2022-09-27 23:56:14 +00:00
committed by Android (Google) Code Review
5 changed files with 154 additions and 15 deletions

View File

@@ -227,6 +227,9 @@ public class EconomyManager {
public static final String KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP =
"js_min_satiated_balance_other_app";
/** @hide */
public static final String KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER =
"js_min_satiated_balance_increment_updater";
/** @hide */
public static final String KEY_JS_MAX_SATIATED_BALANCE =
"js_max_satiated_balance";
/** @hide */
@@ -509,6 +512,15 @@ public class EconomyManager {
public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0);
/** @hide */
public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(5000);
/**
* How many credits to increase the updating app's min satiated balance by for each app that it
* is responsible for updating.
* @hide
*/
public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES =
// Research indicates that the median time between popular app updates is 13-14 days,
// so adjust by 14 to amortize over that time.
DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES / 14;
/** @hide */
public static final long DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES = arcToCake(3);
/** @hide */

View File

@@ -81,6 +81,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@@ -164,6 +165,10 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mLock")
private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>();
/** Set of apps each installer is responsible for installing. */
@GuardedBy("mLock")
private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>();
private volatile boolean mHasBattery = true;
private volatile boolean mIsEnabled;
private volatile int mBootPhase;
@@ -353,6 +358,14 @@ public class InternalResourceService extends SystemService {
return mCompleteEconomicPolicy;
}
/** Returns the number of apps that this app is expected to update at some point. */
int getAppUpdateResponsibilityCount(final int userId, @NonNull final String pkgName) {
synchronized (mLock) {
// TODO(248274798): return 0 if the app has lost the install permission
return ArrayUtils.size(mInstallers.get(userId, pkgName));
}
}
@NonNull
SparseArrayMap<String, InstalledPackageInfo> getInstalledPackages() {
synchronized (mLock) {
@@ -525,7 +538,8 @@ public class InternalResourceService extends SystemService {
}
synchronized (mLock) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
mPkgCache.add(userId, pkgName, ipo);
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
mUidToPackageCache.add(uid, pkgName);
// TODO: only do this when the user first launches the app (app leaves stopped state)
mAgent.grantBirthrightLocked(userId, pkgName);
@@ -552,7 +566,14 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
mUidToPackageCache.remove(uid, pkgName);
mVipOverrides.delete(userId, pkgName);
mPkgCache.delete(userId, pkgName);
final InstalledPackageInfo ipo = mPkgCache.delete(userId, pkgName);
mInstallers.delete(userId, pkgName);
if (ipo != null && ipo.installerPackageName != null) {
final ArraySet<String> list = mInstallers.get(userId, ipo.installerPackageName);
if (list != null) {
list.remove(pkgName);
}
}
mAgent.onPackageRemovedLocked(userId, pkgName);
}
}
@@ -574,7 +595,8 @@ public class InternalResourceService extends SystemService {
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
mPkgCache.add(userId, ipo.packageName, ipo);
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
mAgent.grantBirthrightsLocked(userId);
}
@@ -590,6 +612,7 @@ public class InternalResourceService extends SystemService {
mUidToPackageCache.remove(pkgInfo.uid);
}
}
mInstallers.delete(userId);
mPkgCache.delete(userId);
mAgent.onUserRemovedLocked(userId);
}
@@ -746,11 +769,49 @@ public class InternalResourceService extends SystemService {
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
mPkgCache.add(userId, ipo.packageName, ipo);
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
}
}
/**
* Used to update the set of installed apps for each installer. This only has an effect if the
* installer package name is different between {@code oldIpo} and {@code newIpo}.
*/
@GuardedBy("mLock")
private void maybeUpdateInstallerStatusLocked(@Nullable InstalledPackageInfo oldIpo,
@NonNull InstalledPackageInfo newIpo) {
final boolean changed;
if (oldIpo == null) {
changed = newIpo.installerPackageName != null;
} else {
changed = !Objects.equals(oldIpo.installerPackageName, newIpo.installerPackageName);
}
if (!changed) {
return;
}
// InstallSourceInfo doesn't track userId, so for now, assume the installer on the package's
// user profile did the installation.
// TODO(246640162): use the actual installer's user ID
final int userId = UserHandle.getUserId(newIpo.uid);
final String pkgName = newIpo.packageName;
if (oldIpo != null) {
final ArraySet<String> oldList = mInstallers.get(userId, oldIpo.installerPackageName);
if (oldList != null) {
oldList.remove(pkgName);
}
}
if (newIpo.installerPackageName != null) {
ArraySet<String> newList = mInstallers.get(userId, newIpo.installerPackageName);
if (newList == null) {
newList = new ArraySet<>();
mInstallers.add(userId, newIpo.installerPackageName, newList);
}
newList.add(pkgName);
}
}
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -1359,6 +1420,23 @@ public class InternalResourceService extends SystemService {
}
pw.println();
pw.println();
pw.println("Installers:");
pw.increaseIndent();
for (int u = 0; u < mInstallers.numMaps(); ++u) {
final int userId = mInstallers.keyAt(u);
for (int p = 0; p < mInstallers.numElementsForKeyAt(u); ++p) {
final String pkgName = mInstallers.keyAt(u, p);
pw.print(appToString(userId, pkgName));
pw.print(": ");
pw.print(mInstallers.valueAt(u, p).size());
pw.println(" apps");
}
}
pw.decreaseIndent();
pw.println();
mCompleteEconomicPolicy.dump(pw);

View File

@@ -42,6 +42,7 @@ import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_
import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES;
@@ -87,6 +88,7 @@ import static android.app.tare.EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX;
@@ -154,6 +156,7 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
private long mMinSatiatedBalanceIncrementalAppUpdater;
private long mMaxSatiatedBalance;
private long mInitialSatiatedConsumptionLimit;
private long mHardSatiatedConsumptionLimit;
@@ -183,11 +186,20 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
if (mIrs.isPackageRestricted(userId, pkgName)) {
return 0;
}
final long baseBalance;
if (mIrs.isPackageExempted(userId, pkgName)) {
return mMinSatiatedBalanceExempted;
baseBalance = mMinSatiatedBalanceExempted;
} else {
baseBalance = mMinSatiatedBalanceOther;
}
// TODO: take other exemptions into account
return mMinSatiatedBalanceOther;
long minBalance = baseBalance;
final int updateResponsibilityCount = mIrs.getAppUpdateResponsibilityCount(userId, pkgName);
minBalance += updateResponsibilityCount * mMinSatiatedBalanceIncrementalAppUpdater;
return Math.min(minBalance, mMaxSatiatedBalance);
}
@Override
@@ -242,6 +254,9 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceOther);
mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
@@ -397,10 +412,11 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
@Override
void dump(IndentingPrintWriter pw) {
pw.println("Min satiated balances:");
pw.println("Min satiated balance:");
pw.increaseIndent();
pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
pw.print("+App Updater", cakeToString(mMinSatiatedBalanceIncrementalAppUpdater)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
pw.print("Consumption limits: [");

View File

@@ -286,7 +286,7 @@ class Ledger {
final int idx = (mRewardBucketIndex - b + NUM_REWARD_BUCKET_WINDOWS)
% NUM_REWARD_BUCKET_WINDOWS;
final RewardBucket rewardBucket = mRewardBuckets[idx];
if (rewardBucket == null) {
if (rewardBucket == null || rewardBucket.startTimeMs == 0) {
continue;
}

View File

@@ -134,15 +134,36 @@ public class JobSchedulerEconomicPolicyTest {
mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
mEconomicPolicy.getHardSatiatedConsumptionLimit());
final String pkgRestricted = "com.pkg.restricted";
when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
assertEquals(0, mEconomicPolicy.getMinSatiatedBalance(0, pkgRestricted));
assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
final String pkgExempted = "com.pkg.exempted";
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted));
final String pkgUpdater = "com.pkg.updater";
when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5);
assertEquals(5 * EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES
+ EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
mEconomicPolicy.getMaxSatiatedBalance(0, pkgUpdater));
// Make sure it doesn't suggest a min balance greater than max.
final int updateCount = (int) (EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES
/ EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater)))
.thenReturn(updateCount);
assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
}
@@ -152,8 +173,10 @@ public class JobSchedulerEconomicPolicyTest {
setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(25));
setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(6));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(4));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
arcToCake(1));
assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
@@ -163,8 +186,12 @@ public class JobSchedulerEconomicPolicyTest {
assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
final String pkgExempted = "com.pkg.exempted";
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
assertEquals(arcToCake(6), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
assertEquals(arcToCake(4), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
final String pkgUpdater = "com.pkg.updater";
when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(3);
assertEquals(arcToCake(4) + 3 * arcToCake(1),
mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
}
@Test
@@ -175,6 +202,8 @@ public class JobSchedulerEconomicPolicyTest {
setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
arcToCake(-4));
assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
@@ -186,6 +215,10 @@ public class JobSchedulerEconomicPolicyTest {
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
final String pkgUpdater = "com.pkg.updater";
when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5);
assertEquals(arcToCake(0) + 5 * arcToCake(0),
mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
// Test min+max reversed.
setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));