diff options
82 files changed, 2174 insertions, 979 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java index d401373c0066..aeb6abc3ce1b 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java @@ -306,6 +306,10 @@ public abstract class EconomicPolicy { return eventId & MASK_TYPE; } + static boolean isReward(int eventId) { + return getEventType(eventId) == TYPE_REWARD; + } + @NonNull static String eventToString(int eventId) { switch (eventId & MASK_TYPE) { diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java index 2e2a9b56d3df..e91ed1287e1b 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java @@ -17,15 +17,19 @@ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.util.TimeUtils.dumpTime; import static com.android.server.tare.TareUtils.cakeToString; -import static com.android.server.tare.TareUtils.dumpTime; import static com.android.server.tare.TareUtils.getCurrentTimeMillis; +import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.IndentingPrintWriter; import android.util.SparseLongArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; @@ -34,6 +38,21 @@ import java.util.List; * Ledger to track the last recorded balance and recent activities of an app. */ class Ledger { + /** The window size within which rewards will be counted and used towards reward limiting. */ + private static final long TOTAL_REWARD_WINDOW_MS = 24 * HOUR_IN_MILLIS; + /** The number of buckets to split {@link #TOTAL_REWARD_WINDOW_MS} into. */ + @VisibleForTesting + static final int NUM_REWARD_BUCKET_WINDOWS = 4; + /** + * The duration size of each bucket resulting from splitting {@link #TOTAL_REWARD_WINDOW_MS} + * into smaller buckets. + */ + private static final long REWARD_BUCKET_WINDOW_SIZE_MS = + TOTAL_REWARD_WINDOW_MS / NUM_REWARD_BUCKET_WINDOWS; + /** The maximum number of transactions to retain in memory at any one time. */ + @VisibleForTesting + static final int MAX_TRANSACTION_COUNT = 50; + static class Transaction { public final long startTimeMs; public final long endTimeMs; @@ -54,18 +73,47 @@ class Ledger { } } + static class RewardBucket { + @CurrentTimeMillisLong + public long startTimeMs; + public final SparseLongArray cumulativeDelta = new SparseLongArray(); + + private void reset() { + startTimeMs = 0; + cumulativeDelta.clear(); + } + } + /** Last saved balance. This doesn't take currently ongoing events into account. */ private long mCurrentBalance = 0; - private final List<Transaction> mTransactions = new ArrayList<>(); - private final SparseLongArray mCumulativeDeltaPerReason = new SparseLongArray(); - private long mEarliestSumTime; + private final Transaction[] mTransactions = new Transaction[MAX_TRANSACTION_COUNT]; + /** Index within {@link #mTransactions} where the next transaction should be placed. */ + private int mTransactionIndex = 0; + private final RewardBucket[] mRewardBuckets = new RewardBucket[NUM_REWARD_BUCKET_WINDOWS]; + /** Index within {@link #mRewardBuckets} of the current active bucket. */ + private int mRewardBucketIndex = 0; Ledger() { } - Ledger(long currentBalance, @NonNull List<Transaction> transactions) { + Ledger(long currentBalance, @NonNull List<Transaction> transactions, + @NonNull List<RewardBucket> rewardBuckets) { mCurrentBalance = currentBalance; - mTransactions.addAll(transactions); + + final int numTxs = transactions.size(); + for (int i = Math.max(0, numTxs - MAX_TRANSACTION_COUNT); i < numTxs; ++i) { + mTransactions[mTransactionIndex++] = transactions.get(i); + } + mTransactionIndex %= MAX_TRANSACTION_COUNT; + + final int numBuckets = rewardBuckets.size(); + if (numBuckets > 0) { + // Set the index to -1 so that we put the first bucket in index 0. + mRewardBucketIndex = -1; + for (int i = Math.max(0, numBuckets - NUM_REWARD_BUCKET_WINDOWS); i < numBuckets; ++i) { + mRewardBuckets[++mRewardBucketIndex] = rewardBuckets.get(i); + } + } } long getCurrentBalance() { @@ -74,66 +122,142 @@ class Ledger { @Nullable Transaction getEarliestTransaction() { - if (mTransactions.size() > 0) { - return mTransactions.get(0); + for (int t = 0; t < mTransactions.length; ++t) { + final Transaction transaction = + mTransactions[(mTransactionIndex + t) % mTransactions.length]; + if (transaction != null) { + return transaction; + } } return null; } @NonNull + List<RewardBucket> getRewardBuckets() { + final long cutoffMs = getCurrentTimeMillis() - TOTAL_REWARD_WINDOW_MS; + final List<RewardBucket> list = new ArrayList<>(NUM_REWARD_BUCKET_WINDOWS); + for (int i = 1; i <= NUM_REWARD_BUCKET_WINDOWS; ++i) { + final int idx = (mRewardBucketIndex + i) % NUM_REWARD_BUCKET_WINDOWS; + final RewardBucket rewardBucket = mRewardBuckets[idx]; + if (rewardBucket != null) { + if (cutoffMs <= rewardBucket.startTimeMs) { + list.add(rewardBucket); + } else { + rewardBucket.reset(); + } + } + } + return list; + } + + @NonNull List<Transaction> getTransactions() { - return mTransactions; + final List<Transaction> list = new ArrayList<>(MAX_TRANSACTION_COUNT); + for (int i = 0; i < MAX_TRANSACTION_COUNT; ++i) { + final int idx = (mTransactionIndex + i) % MAX_TRANSACTION_COUNT; + final Transaction transaction = mTransactions[idx]; + if (transaction != null) { + list.add(transaction); + } + } + return list; } void recordTransaction(@NonNull Transaction transaction) { - mTransactions.add(transaction); + mTransactions[mTransactionIndex] = transaction; mCurrentBalance += transaction.delta; + mTransactionIndex = (mTransactionIndex + 1) % MAX_TRANSACTION_COUNT; - final long sum = mCumulativeDeltaPerReason.get(transaction.eventId); - mCumulativeDeltaPerReason.put(transaction.eventId, sum + transaction.delta); - mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs); + if (EconomicPolicy.isReward(transaction.eventId)) { + final RewardBucket bucket = getCurrentRewardBucket(); + bucket.cumulativeDelta.put(transaction.eventId, + bucket.cumulativeDelta.get(transaction.eventId, 0) + transaction.delta); + } + } + + @NonNull + private RewardBucket getCurrentRewardBucket() { + RewardBucket bucket = mRewardBuckets[mRewardBucketIndex]; + final long now = getCurrentTimeMillis(); + if (bucket == null) { + bucket = new RewardBucket(); + bucket.startTimeMs = now; + mRewardBuckets[mRewardBucketIndex] = bucket; + return bucket; + } + + if (now - bucket.startTimeMs < REWARD_BUCKET_WINDOW_SIZE_MS) { + return bucket; + } + + mRewardBucketIndex = (mRewardBucketIndex + 1) % NUM_REWARD_BUCKET_WINDOWS; + bucket = mRewardBuckets[mRewardBucketIndex]; + if (bucket == null) { + bucket = new RewardBucket(); + mRewardBuckets[mRewardBucketIndex] = bucket; + } + bucket.reset(); + // Using now as the start time means there will be some gaps between sequential buckets, + // but makes processing of large gaps between events easier. + bucket.startTimeMs = now; + return bucket; } long get24HourSum(int eventId, final long now) { final long windowStartTime = now - 24 * HOUR_IN_MILLIS; - if (mEarliestSumTime < windowStartTime) { - // Need to redo sums - mCumulativeDeltaPerReason.clear(); - for (int i = mTransactions.size() - 1; i >= 0; --i) { - final Transaction transaction = mTransactions.get(i); - if (transaction.endTimeMs <= windowStartTime) { - break; - } - long sum = mCumulativeDeltaPerReason.get(transaction.eventId); - if (transaction.startTimeMs >= windowStartTime) { - sum += transaction.delta; - } else { - // Pro-rate durationed deltas. Intentionally floor the result. - sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime) - * transaction.delta) - / (transaction.endTimeMs - transaction.startTimeMs); - } - mCumulativeDeltaPerReason.put(transaction.eventId, sum); + long sum = 0; + for (int i = 0; i < mRewardBuckets.length; ++i) { + final RewardBucket bucket = mRewardBuckets[i]; + if (bucket != null + && bucket.startTimeMs >= windowStartTime && bucket.startTimeMs < now) { + sum += bucket.cumulativeDelta.get(eventId, 0); } - mEarliestSumTime = windowStartTime; } - return mCumulativeDeltaPerReason.get(eventId); + return sum; } - /** Deletes transactions that are older than {@code minAgeMs}. */ - void removeOldTransactions(long minAgeMs) { + /** + * Deletes transactions that are older than {@code minAgeMs}. + * @return The earliest transaction in the ledger, or {@code null} if there are no more + * transactions. + */ + @Nullable + Transaction removeOldTransactions(long minAgeMs) { final long cutoff = getCurrentTimeMillis() - minAgeMs; - while (mTransactions.size() > 0 && mTransactions.get(0).endTimeMs <= cutoff) { - mTransactions.remove(0); + for (int t = 0; t < mTransactions.length; ++t) { + final int idx = (mTransactionIndex + t) % mTransactions.length; + final Transaction transaction = mTransactions[idx]; + if (transaction == null) { + continue; + } + if (transaction.endTimeMs <= cutoff) { + mTransactions[idx] = null; + } else { + // Everything we look at after this transaction will also be within the window, + // so no need to go further. + return transaction; + } } + return null; } void dump(IndentingPrintWriter pw, int numRecentTransactions) { pw.print("Current balance", cakeToString(getCurrentBalance())).println(); + pw.println(); - final int size = mTransactions.size(); - for (int i = Math.max(0, size - numRecentTransactions); i < size; ++i) { - final Transaction transaction = mTransactions.get(i); + boolean printedTransactionTitle = false; + for (int t = 0; t < Math.min(MAX_TRANSACTION_COUNT, numRecentTransactions); ++t) { + final int idx = (mTransactionIndex - t + MAX_TRANSACTION_COUNT) % MAX_TRANSACTION_COUNT; + final Transaction transaction = mTransactions[idx]; + if (transaction == null) { + continue; + } + + if (!printedTransactionTitle) { + pw.println("Transactions:"); + pw.increaseIndent(); + printedTransactionTitle = true; + } dumpTime(pw, transaction.startTimeMs); pw.print("--"); @@ -151,5 +275,42 @@ class Ledger { pw.print(cakeToString(transaction.ctp)); pw.println(")"); } + if (printedTransactionTitle) { + pw.decreaseIndent(); + pw.println(); + } + + final long now = getCurrentTimeMillis(); + boolean printedBucketTitle = false; + for (int b = 0; b < NUM_REWARD_BUCKET_WINDOWS; ++b) { + final int idx = (mRewardBucketIndex - b + NUM_REWARD_BUCKET_WINDOWS) + % NUM_REWARD_BUCKET_WINDOWS; + final RewardBucket rewardBucket = mRewardBuckets[idx]; + if (rewardBucket == null) { + continue; + } + + if (!printedBucketTitle) { + pw.println("Reward buckets:"); + pw.increaseIndent(); + printedBucketTitle = true; + } + + dumpTime(pw, rewardBucket.startTimeMs); + pw.print(" ("); + TimeUtils.formatDuration(now - rewardBucket.startTimeMs, pw); + pw.println(" ago):"); + pw.increaseIndent(); + for (int r = 0; r < rewardBucket.cumulativeDelta.size(); ++r) { + pw.print(EconomicPolicy.eventToString(rewardBucket.cumulativeDelta.keyAt(r))); + pw.print(": "); + pw.println(cakeToString(rewardBucket.cumulativeDelta.valueAt(r))); + } + pw.decreaseIndent(); + } + if (printedBucketTitle) { + pw.decreaseIndent(); + pw.println(); + } } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java index 941cc39f2d97..8f7657e6c414 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java @@ -62,15 +62,14 @@ public class Scribe { private static final int MAX_NUM_TRANSACTION_DUMP = 25; /** * The maximum amount of time we'll keep a transaction around for. - * For now, only keep transactions we actually have a use for. We can increase it if we want - * to use older transactions or provide older transactions to apps. */ - private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS; + private static final long MAX_TRANSACTION_AGE_MS = 8 * 24 * HOUR_IN_MILLIS; private static final String XML_TAG_HIGH_LEVEL_STATE = "irs-state"; private static final String XML_TAG_LEDGER = "ledger"; private static final String XML_TAG_TARE = "tare"; private static final String XML_TAG_TRANSACTION = "transaction"; + private static final String XML_TAG_REWARD_BUCKET = "rewardBucket"; private static final String XML_TAG_USER = "user"; private static final String XML_TAG_PERIOD_REPORT = "report"; @@ -346,8 +345,8 @@ public class Scribe { for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) { final String pkgName = mLedgers.keyAt(uIdx, pIdx); final Ledger ledger = mLedgers.get(userId, pkgName); - ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS); - Ledger.Transaction transaction = ledger.getEarliestTransaction(); + final Ledger.Transaction transaction = + ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS); if (transaction != null) { earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs); } @@ -370,6 +369,7 @@ public class Scribe { final String pkgName; final long curBalance; final List<Ledger.Transaction> transactions = new ArrayList<>(); + final List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>(); pkgName = parser.getAttributeValue(null, XML_ATTR_PACKAGE_NAME); curBalance = parser.getAttributeLong(null, XML_ATTR_CURRENT_BALANCE); @@ -391,8 +391,7 @@ public class Scribe { } continue; } - if (eventType != XmlPullParser.START_TAG || !XML_TAG_TRANSACTION.equals(tagName)) { - // Expecting only "transaction" tags. + if (eventType != XmlPullParser.START_TAG || tagName == null) { Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName); return null; } @@ -402,25 +401,37 @@ public class Scribe { if (DEBUG) { Slog.d(TAG, "Starting ledger tag: " + tagName); } - final String tag = parser.getAttributeValue(null, XML_ATTR_TAG); - final long startTime = parser.getAttributeLong(null, XML_ATTR_START_TIME); - final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME); - final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID); - final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA); - final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP); - if (endTime <= endTimeCutoff) { - if (DEBUG) { - Slog.d(TAG, "Skipping event because it's too old."); - } - continue; + switch (tagName) { + case XML_TAG_TRANSACTION: + final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME); + if (endTime <= endTimeCutoff) { + if (DEBUG) { + Slog.d(TAG, "Skipping event because it's too old."); + } + continue; + } + final String tag = parser.getAttributeValue(null, XML_ATTR_TAG); + final long startTime = parser.getAttributeLong(null, XML_ATTR_START_TIME); + final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID); + final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA); + final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP); + transactions.add( + new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp)); + break; + case XML_TAG_REWARD_BUCKET: + rewardBuckets.add(readRewardBucketFromXml(parser)); + break; + default: + // Expecting only "transaction" and "rewardBucket" tags. + Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName); + return null; } - transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp)); } if (!isInstalled) { return null; } - return Pair.create(pkgName, new Ledger(curBalance, transactions)); + return Pair.create(pkgName, new Ledger(curBalance, transactions, rewardBuckets)); } /** @@ -508,6 +519,44 @@ public class Scribe { return report; } + /** + * @param parser Xml parser at the beginning of a {@value #XML_TAG_REWARD_BUCKET} tag. The next + * "parser.next()" call will take the parser into the body of the tag. + * @return Newly instantiated {@link Ledger.RewardBucket} holding all the information we just + * read out of the xml tag. + */ + @Nullable + private static Ledger.RewardBucket readRewardBucketFromXml(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { + + final Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); + + rewardBucket.startTimeMs = parser.getAttributeLong(null, XML_ATTR_START_TIME); + + for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; + eventType = parser.next()) { + final String tagName = parser.getName(); + if (eventType == XmlPullParser.END_TAG) { + if (XML_TAG_REWARD_BUCKET.equals(tagName)) { + // We've reached the end of the rewardBucket tag. + break; + } + continue; + } + if (eventType != XmlPullParser.START_TAG || !XML_ATTR_DELTA.equals(tagName)) { + // Expecting only delta tags. + Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName); + return null; + } + + final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID); + final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA); + rewardBucket.cumulativeDelta.put(eventId, delta); + } + + return rewardBucket; + } + private void scheduleCleanup(long earliestEndTime) { if (earliestEndTime == Long.MAX_VALUE) { return; @@ -595,6 +644,11 @@ public class Scribe { } writeTransaction(out, transaction); } + + final List<Ledger.RewardBucket> rewardBuckets = ledger.getRewardBuckets(); + for (int r = 0; r < rewardBuckets.size(); ++r) { + writeRewardBucket(out, rewardBuckets.get(r)); + } out.endTag(null, XML_TAG_LEDGER); } out.endTag(null, XML_TAG_USER); @@ -616,6 +670,23 @@ public class Scribe { out.endTag(null, XML_TAG_TRANSACTION); } + private static void writeRewardBucket(@NonNull TypedXmlSerializer out, + @NonNull Ledger.RewardBucket rewardBucket) throws IOException { + final int numEvents = rewardBucket.cumulativeDelta.size(); + if (numEvents == 0) { + return; + } + out.startTag(null, XML_TAG_REWARD_BUCKET); + out.attributeLong(null, XML_ATTR_START_TIME, rewardBucket.startTimeMs); + for (int i = 0; i < numEvents; ++i) { + out.startTag(null, XML_ATTR_DELTA); + out.attributeInt(null, XML_ATTR_EVENT_ID, rewardBucket.cumulativeDelta.keyAt(i)); + out.attributeLong(null, XML_ATTR_DELTA, rewardBucket.cumulativeDelta.valueAt(i)); + out.endTag(null, XML_ATTR_DELTA); + } + out.endTag(null, XML_TAG_REWARD_BUCKET); + } + private static void writeReport(@NonNull TypedXmlSerializer out, @NonNull Analyst.Report report) throws IOException { out.startTag(null, XML_TAG_PERIOD_REPORT); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java index 6b6984f6ac17..aa4c75a0be80 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java @@ -19,26 +19,15 @@ package com.android.server.tare; import static android.app.tare.EconomyManager.CAKE_IN_ARC; import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; -import java.text.SimpleDateFormat; import java.time.Clock; class TareUtils { - @SuppressLint("SimpleDateFormat") - private static final SimpleDateFormat sDumpDateFormat = - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - @VisibleForTesting static Clock sSystemClock = Clock.systemUTC(); - static void dumpTime(IndentingPrintWriter pw, long time) { - pw.print(sDumpDateFormat.format(time)); - } - static long getCurrentTimeMillis() { return sSystemClock.millis(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 2a83cbddd3dc..b383d7daafd0 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -374,6 +374,7 @@ public final class ActivityThread extends ClientTransactionHandler int mCurDefaultDisplayDpi; @UnsupportedAppUsage boolean mDensityCompatMode; + private CompatibilityInfo mCompatibilityInfo; @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.") Configuration mConfiguration; @@ -612,7 +613,7 @@ public final class ActivityThread extends ClientTransactionHandler } public ActivityClientRecord(IBinder token, Intent intent, int ident, - ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo, + ActivityInfo info, Configuration overrideConfig, String referrer, IVoiceInteractor voiceInteractor, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, @@ -627,7 +628,6 @@ public final class ActivityThread extends ClientTransactionHandler this.referrer = referrer; this.voiceInteractor = voiceInteractor; this.activityInfo = info; - this.compatInfo = compatInfo; this.state = state; this.persistentState = persistentState; this.pendingResults = pendingResults; @@ -635,8 +635,7 @@ public final class ActivityThread extends ClientTransactionHandler this.isForward = isForward; this.profilerInfo = profilerInfo; this.overrideConfig = overrideConfig; - this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo, - compatInfo); + this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo); mActivityOptions = activityOptions; mLaunchedFromBubble = launchedFromBubble; mInitialTaskFragmentToken = initialTaskFragmentToken; @@ -804,7 +803,6 @@ public final class ActivityThread extends ClientTransactionHandler static final class CreateBackupAgentData { ApplicationInfo appInfo; - CompatibilityInfo compatInfo; int backupMode; int userId; int operationType; @@ -1038,15 +1036,13 @@ public final class ActivityThread extends ClientTransactionHandler ReceiverData r = new ReceiverData(intent, resultCode, data, extras, sync, false, mAppThread.asBinder(), sendingUser); r.info = info; - r.compatInfo = compatInfo; sendMessage(H.RECEIVER, r); } public final void scheduleCreateBackupAgent(ApplicationInfo app, - CompatibilityInfo compatInfo, int backupMode, int userId, int operationType) { + int backupMode, int userId, int operationType) { CreateBackupAgentData d = new CreateBackupAgentData(); d.appInfo = app; - d.compatInfo = compatInfo; d.backupMode = backupMode; d.userId = userId; d.operationType = operationType; @@ -1054,11 +1050,9 @@ public final class ActivityThread extends ClientTransactionHandler sendMessage(H.CREATE_BACKUP_AGENT, d); } - public final void scheduleDestroyBackupAgent(ApplicationInfo app, - CompatibilityInfo compatInfo, int userId) { + public final void scheduleDestroyBackupAgent(ApplicationInfo app, int userId) { CreateBackupAgentData d = new CreateBackupAgentData(); d.appInfo = app; - d.compatInfo = compatInfo; d.userId = userId; sendMessage(H.DESTROY_BACKUP_AGENT, d); @@ -1070,7 +1064,6 @@ public final class ActivityThread extends ClientTransactionHandler CreateServiceData s = new CreateServiceData(); s.token = token; s.info = info; - s.compatInfo = compatInfo; sendMessage(H.CREATE_SERVICE, s); } @@ -2577,16 +2570,16 @@ public final class ActivityThread extends ClientTransactionHandler registerPackage); } - @Override @UnsupportedAppUsage public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { return getPackageInfo(ai, compatInfo, null, false, true, false); } - private LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo, - boolean isSdkSandbox) { - return getPackageInfo(ai, compatInfo, null, false, true, false, isSdkSandbox); + @Override + public LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) { + return getPackageInfo(ai, mCompatibilityInfo, null /* baseLoader */, + false /* securityViolation */, true /* includeCode */, false /* registerPackage */); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -3538,7 +3531,7 @@ public final class ActivityThread extends ClientTransactionHandler private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { - r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, + r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo, Context.CONTEXT_INCLUDE_CODE); } @@ -4279,8 +4272,7 @@ public final class ActivityThread extends ClientTransactionHandler String component = data.intent.getComponent().getClassName(); - LoadedApk packageInfo = getPackageInfoNoCheck( - data.info.applicationInfo, data.compatInfo); + final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo); IActivityManager mgr = ActivityManager.getService(); @@ -4366,7 +4358,7 @@ public final class ActivityThread extends ClientTransactionHandler unscheduleGcIdler(); // instantiate the BackupAgent class named in the manifest - LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); + final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo); String packageName = packageInfo.mPackageName; if (packageName == null) { Slog.d(TAG, "Asked to create backup agent for nonexistent package"); @@ -4439,7 +4431,7 @@ public final class ActivityThread extends ClientTransactionHandler private void handleDestroyBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data); - LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); + final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo); String packageName = packageInfo.mPackageName; ArrayMap<String, BackupAgent> backupAgents = getBackupAgentsForUser(data.userId); BackupAgent agent = backupAgents.get(packageName); @@ -4471,8 +4463,7 @@ public final class ActivityThread extends ClientTransactionHandler // we are back active so skip it. unscheduleGcIdler(); - LoadedApk packageInfo = getPackageInfoNoCheck( - data.info.applicationInfo, data.compatInfo); + final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo); Service service = null; try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); @@ -5306,6 +5297,7 @@ public final class ActivityThread extends ClientTransactionHandler } private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) { + mCompatibilityInfo = data.info; LoadedApk apk = peekPackageInfo(data.pkg, false); if (apk != null) { apk.setCompatibilityInfo(data.info); @@ -6486,6 +6478,7 @@ public final class ActivityThread extends ClientTransactionHandler mConfigurationController.setConfiguration(data.config); mConfigurationController.setCompatConfiguration(data.config); mConfiguration = mConfigurationController.getConfiguration(); + mCompatibilityInfo = data.compatInfo; mProfiler = new Profiler(); String agent = null; @@ -6569,7 +6562,9 @@ public final class ActivityThread extends ClientTransactionHandler } final boolean isSdkSandbox = data.sdkSandboxClientAppPackage != null; - data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo, isSdkSandbox); + data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */, + false /* securityViolation */, true /* includeCode */, + false /* registerPackage */, isSdkSandbox); if (isSdkSandbox) { data.info.setSdkSandboxStorage(data.sdkSandboxClientAppVolumeUuid, data.sdkSandboxClientAppPackage); @@ -6844,7 +6839,7 @@ public final class ActivityThread extends ClientTransactionHandler private void handleInstrumentWithoutRestart(AppBindData data) { try { data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; - data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); + data.info = getPackageInfoNoCheck(data.appInfo); mInstrumentingWithoutRestart = true; final InstrumentationInfo ii = prepareInstrumentation(data); final ContextImpl appContext = diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 65e6ab7a2137..389da2d094d5 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -23,7 +23,6 @@ import android.app.servertransaction.PendingTransactionActions; import android.app.servertransaction.TransactionExecutor; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.IBinder; import android.util.MergedConfiguration; @@ -180,8 +179,7 @@ public abstract class ClientTransactionHandler { PendingTransactionActions pendingActions, ActivityOptions activityOptions); /** Get package info. */ - public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, - CompatibilityInfo compatInfo); + public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai); /** Deliver app configuration change notification. */ public abstract void handleConfigurationChanged(Configuration config); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 75ad363a2668..843b6846d053 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -102,10 +102,9 @@ oneway interface IApplicationThread { void scheduleLowMemory(); void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType); void setSchedulingGroup(int group); - void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo, + void scheduleCreateBackupAgent(in ApplicationInfo app, int backupMode, int userId, int operationType); - void scheduleDestroyBackupAgent(in ApplicationInfo app, - in CompatibilityInfo compatInfo, int userId); + void scheduleDestroyBackupAgent(in ApplicationInfo app, int userId); void scheduleOnNewActivityOptions(IBinder token, in Bundle options); void scheduleSuicide(); void dispatchPackageBroadcast(int cmd, in String[] packages); diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 076dbef9ebc4..03d6e27d6a8d 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -30,7 +30,6 @@ import android.app.ResultInfo; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.BaseBundle; import android.os.Bundle; @@ -58,7 +57,6 @@ public class LaunchActivityItem extends ClientTransactionItem { private ActivityInfo mInfo; private Configuration mCurConfig; private Configuration mOverrideConfig; - private CompatibilityInfo mCompatInfo; private String mReferrer; private IVoiceInteractor mVoiceInteractor; private int mProcState; @@ -94,7 +92,7 @@ public class LaunchActivityItem extends ClientTransactionItem { PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, - mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, + mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken); @@ -115,7 +113,7 @@ public class LaunchActivityItem extends ClientTransactionItem { /** Obtain an instance initialized with provided params. */ public static LaunchActivityItem obtain(Intent intent, int ident, ActivityInfo info, - Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, + Configuration curConfig, Configuration overrideConfig, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, @@ -126,7 +124,7 @@ public class LaunchActivityItem extends ClientTransactionItem { if (instance == null) { instance = new LaunchActivityItem(); } - setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer, + setValues(instance, intent, ident, info, curConfig, overrideConfig, referrer, voiceInteractor, procState, state, persistentState, pendingResults, pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken, activityClientController, shareableActivityToken, @@ -137,7 +135,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void recycle() { - setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, + setValues(this, null, 0, null, null, null, null, null, 0, null, null, null, null, null, false, null, null, null, null, false, null); ObjectPool.recycle(this); } @@ -153,7 +151,6 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeTypedObject(mInfo, flags); dest.writeTypedObject(mCurConfig, flags); dest.writeTypedObject(mOverrideConfig, flags); - dest.writeTypedObject(mCompatInfo, flags); dest.writeString(mReferrer); dest.writeStrongInterface(mVoiceInteractor); dest.writeInt(mProcState); @@ -175,8 +172,7 @@ public class LaunchActivityItem extends ClientTransactionItem { private LaunchActivityItem(Parcel in) { setValues(this, in.readTypedObject(Intent.CREATOR), in.readInt(), in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR), - in.readTypedObject(Configuration.CREATOR), - in.readTypedObject(CompatibilityInfo.CREATOR), in.readString(), + in.readTypedObject(Configuration.CREATOR), in.readString(), IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(), in.readBundle(getClass().getClassLoader()), in.readPersistableBundle(getClass().getClassLoader()), @@ -216,7 +212,6 @@ public class LaunchActivityItem extends ClientTransactionItem { return intentsEqual && mIdent == other.mIdent && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig) && Objects.equals(mOverrideConfig, other.mOverrideConfig) - && Objects.equals(mCompatInfo, other.mCompatInfo) && Objects.equals(mReferrer, other.mReferrer) && mProcState == other.mProcState && areBundlesEqualRoughly(mState, other.mState) && areBundlesEqualRoughly(mPersistentState, other.mPersistentState) @@ -237,7 +232,6 @@ public class LaunchActivityItem extends ClientTransactionItem { result = 31 * result + mIdent; result = 31 * result + Objects.hashCode(mCurConfig); result = 31 * result + Objects.hashCode(mOverrideConfig); - result = 31 * result + Objects.hashCode(mCompatInfo); result = 31 * result + Objects.hashCode(mReferrer); result = 31 * result + Objects.hashCode(mProcState); result = 31 * result + getRoughBundleHashCode(mState); @@ -292,7 +286,7 @@ public class LaunchActivityItem extends ClientTransactionItem { // Using the same method to set and clear values to make sure we don't forget anything private static void setValues(LaunchActivityItem instance, Intent intent, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, - CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, @@ -303,7 +297,6 @@ public class LaunchActivityItem extends ClientTransactionItem { instance.mInfo = info; instance.mCurConfig = curConfig; instance.mOverrideConfig = overrideConfig; - instance.mCompatInfo = compatInfo; instance.mReferrer = referrer; instance.mVoiceInteractor = voiceInteractor; instance.mProcState = procState; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 8db298ff8a85..861a8502c44d 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1325,8 +1325,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * flashlight brightness level via * {@link android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel }. * If this value is equal to 1, flashlight brightness control is not supported. - * The value for this key will be null for devices with no flash unit. - * This level must be set to a safe value to prevent any burn out issues.</p> + * The value for this key will be null for devices with no flash unit.</p> + * <p>The maximum value is guaranteed to be safe to use for an indefinite duration in + * terms of device flashlight lifespan, but may be too bright for comfort for many + * use cases. Use the default torch brightness value to avoid problems with an + * over-bright flashlight.</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> */ @PublicKey diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 91d6a9bf69cb..40f7533a2800 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -117,6 +117,7 @@ interface INetworkManagementService /** * Shuts down the service */ + @EnforcePermission("SHUTDOWN") void shutdown(); /** @@ -277,6 +278,7 @@ interface INetworkManagementService */ void setUidOnMeteredNetworkDenylist(int uid, boolean enable); void setUidOnMeteredNetworkAllowlist(int uid, boolean enable); + @EnforcePermission("NETWORK_SETTINGS") boolean setDataSaverModeEnabled(boolean enable); void setUidCleartextNetworkPolicy(int uid, int policy); @@ -308,5 +310,6 @@ interface INetworkManagementService void removeInterfaceFromLocalNetwork(String iface); int removeRoutesFromLocalNetwork(in List<RouteInfo> routes); + @EnforcePermission("OBSERVE_NETWORK_POLICY") boolean isNetworkRestricted(int uid); } diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index 5dae31373e82..c69298192109 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -145,5 +145,8 @@ public interface AttachedSurfaceControl { * * @hide */ - SurfaceSyncGroup.SyncTarget getSyncTarget(); + @Nullable + default SurfaceSyncGroup.SyncTarget getSyncTarget() { + return null; + } } diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 9c63d56d74da..121839bc9ea6 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -666,14 +666,12 @@ public final class InputMethodSubtype implements Parcelable { /** * Sort the list of InputMethodSubtype - * @param context Context will be used for getting localized strings from IME - * @param flags Flags for the sort order * @param imi InputMethodInfo of which subtypes are subject to be sorted * @param subtypeList List of InputMethodSubtype which will be sorted * @return Sorted list of subtypes * @hide */ - public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi, + public static List<InputMethodSubtype> sort(InputMethodInfo imi, List<InputMethodSubtype> subtypeList) { if (imi == null) return subtypeList; final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>( diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java index 5672697f665e..4248096f307d 100644 --- a/core/java/android/window/SurfaceSyncGroup.java +++ b/core/java/android/window/SurfaceSyncGroup.java @@ -186,7 +186,11 @@ public final class SurfaceSyncGroup { if (viewRoot == null) { return false; } - return addToSync(viewRoot.getSyncTarget()); + SyncTarget syncTarget = viewRoot.getSyncTarget(); + if (syncTarget == null) { + return false; + } + return addToSync(syncTarget); } /** diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl index a2eca3aee13d..84b2a354020f 100644 --- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl +++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl @@ -21,7 +21,9 @@ import android.os.ParcelFileDescriptor; import com.android.internal.app.procstats.ProcessStats; interface IProcessStats { + @EnforcePermission("PACKAGE_USAGE_STATS") byte[] getCurrentStats(out List<ParcelFileDescriptor> historic); + @EnforcePermission("PACKAGE_USAGE_STATS") ParcelFileDescriptor getStatsOverTime(long minTime); int getCurrentMemoryState(); @@ -43,6 +45,7 @@ interface IProcessStats { * @param List of Files of individual commits in protobuf binary or one that is merged from them. * @param ProcessStats object that will be used to return the full set of merged stats. */ + @EnforcePermission("PACKAGE_USAGE_STATS") long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate, out List<ParcelFileDescriptor> committedStats, out ProcessStats mergedStats); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 50639be57f22..0a5a4d5a9adb 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -32,7 +32,6 @@ import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Bundle; @@ -140,7 +139,6 @@ public class ObjectPoolTests { activityInfo.name = "name"; Configuration overrideConfig = new Configuration(); overrideConfig.assetsSeq = 5; - CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; String referrer = "referrer"; int procState = 4; Bundle bundle = new Bundle(); @@ -152,7 +150,7 @@ public class ObjectPoolTests { Supplier<LaunchActivityItem> itemSupplier = () -> new LaunchActivityItemBuilder() .setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config()) - .setOverrideConfig(overrideConfig).setCompatInfo(compat).setReferrer(referrer) + .setOverrideConfig(overrideConfig).setReferrer(referrer) .setProcState(procState).setState(bundle).setPersistentState(persistableBundle) .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList()) .setIsForward(true).setAssistToken(assistToken) diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java index 26d9628ba55b..2cd890cd9d18 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -23,7 +23,6 @@ import android.app.ProfilerInfo; import android.app.ResultInfo; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; @@ -96,7 +95,6 @@ class TestUtils { private ActivityInfo mInfo; private Configuration mCurConfig; private Configuration mOverrideConfig; - private CompatibilityInfo mCompatInfo; private String mReferrer; private IVoiceInteractor mVoiceInteractor; private int mProcState; @@ -137,11 +135,6 @@ class TestUtils { return this; } - LaunchActivityItemBuilder setCompatInfo(CompatibilityInfo compatInfo) { - mCompatInfo = compatInfo; - return this; - } - LaunchActivityItemBuilder setReferrer(String referrer) { mReferrer = referrer; return this; @@ -214,7 +207,7 @@ class TestUtils { LaunchActivityItem build() { return LaunchActivityItem.obtain(mIntent, mIdent, mInfo, - mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, + mCurConfig, mOverrideConfig, mReferrer, mVoiceInteractor, mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */, mShareableActivityToken, diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 0eca0a8cb1a7..e9bbdbee576c 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -187,7 +187,6 @@ public class TransactionParcelTests { activityInfo.name = "name"; Configuration overrideConfig = new Configuration(); overrideConfig.assetsSeq = 5; - CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; String referrer = "referrer"; int procState = 4; Bundle bundle = new Bundle(); @@ -198,7 +197,7 @@ public class TransactionParcelTests { LaunchActivityItem item = new LaunchActivityItemBuilder() .setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config()) - .setOverrideConfig(overrideConfig).setCompatInfo(compat).setReferrer(referrer) + .setOverrideConfig(overrideConfig).setReferrer(referrer) .setProcState(procState).setState(bundle).setPersistentState(persistableBundle) .setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic()) .setPendingNewIntents(referrerIntentList()).setIsForward(true) @@ -489,13 +488,13 @@ public class TransactionParcelTests { @Override public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo, - CompatibilityInfo compatibilityInfo, int i, int userId, int operatioType) + int i, int userId, int operatioType) throws RemoteException { } @Override public void scheduleDestroyBackupAgent(ApplicationInfo applicationInfo, - CompatibilityInfo compatibilityInfo, int userId) throws RemoteException { + int userId) throws RemoteException { } @Override diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 47f70ddf2d42..f4a6f025074e 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -54,7 +54,6 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.IBinder; import android.os.UserHandle; @@ -339,8 +338,7 @@ public class ActivityThreadClientTest { doNothing().when(packageInfo).updateApplicationInfo(any(), any()); return new ActivityClientRecord(mock(IBinder.class), Intent.makeMainActivity(component), - 0 /* ident */, info, new Configuration(), - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */, + 0 /* ident */, info, new Configuration(), null /* referrer */, null /* voiceInteractor */, null /* state */, null /* persistentState */, null /* pendingResults */, null /* pendingNewIntents */, null /* activityOptions */, true /* isForward */, null /* profilerInfo */, diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index c14c3c534cf4..5ab21bc5f489 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -237,6 +237,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final boolean mUnlockedDeviceRequired; private final boolean mIsStrongBoxBacked; private final int mMaxUsageCount; + private final boolean mRollbackResistant; private KeyProtection( Date keyValidityStart, @@ -259,7 +260,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { boolean userConfirmationRequired, boolean unlockedDeviceRequired, boolean isStrongBoxBacked, - int maxUsageCount) { + int maxUsageCount, + boolean rollbackResistant) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -283,6 +285,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mUnlockedDeviceRequired = unlockedDeviceRequired; mIsStrongBoxBacked = isStrongBoxBacked; mMaxUsageCount = maxUsageCount; + mRollbackResistant = rollbackResistant; } /** @@ -563,6 +566,17 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Returns {@code true} if the key is rollback-resistant, meaning that when deleted it is + * guaranteed to be permanently deleted and unusable. + * + * @see Builder#setRollbackResistant(boolean) + * @hide + */ + public boolean isRollbackResistant() { + return mRollbackResistant; + } + + /** * Builder of {@link KeyProtection} instances. */ public final static class Builder { @@ -591,6 +605,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private boolean mIsStrongBoxBacked = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; private String mAttestKeyAlias = null; + private boolean mRollbackResistant = false; /** * Creates a new instance of the {@code Builder}. @@ -1097,6 +1112,21 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Sets whether the key should be rollback-resistant, meaning that when deleted it is + * guaranteed to be permanently deleted and unusable. Not all implementations support + * rollback-resistant keys. This method is hidden because implementations only support a + * limited number of rollback-resistant keys; currently the available space is reserved for + * critical system keys. + * + * @hide + */ + @NonNull + public Builder setRollbackResistant(boolean rollbackResistant) { + mRollbackResistant = rollbackResistant; + return this; + } + + /** * Builds an instance of {@link KeyProtection}. * * @throws IllegalArgumentException if a required field is missing @@ -1124,7 +1154,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mUserConfirmationRequired, mUnlockedDeviceRequired, mIsStrongBoxBacked, - mMaxUsageCount); + mMaxUsageCount, + mRollbackResistant); } } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 33411e1ec5b9..dfda356ec35b 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -783,6 +783,12 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { params.getMaxUsageCount() )); } + + if (params.isRollbackResistant()) { + importArgs.add(KeyStore2ParameterUtils.makeBool( + KeymasterDefs.KM_TAG_ROLLBACK_RESISTANT + )); + } } catch (IllegalArgumentException | IllegalStateException e) { throw new KeyStoreException(e); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt index 0972cf2c032f..1636c5f73133 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt @@ -25,6 +25,7 @@ import com.android.wm.shell.bubbles.storage.BubbleXmlHelperTest.Companion.sparse import junit.framework.Assert.assertEquals import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertTrue +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -61,6 +62,12 @@ class BubblePersistentRepositoryTest : ShellTestCase() { bubbles.put(1, user1Bubbles) } + @After + fun teardown() { + // Clean up the any persisted bubbles for the next run + repository.persistsToDisk(SparseArray()) + } + @Test fun testReadWriteOperation() { // Verify read before write doesn't cause FileNotFoundException @@ -71,4 +78,4 @@ class BubblePersistentRepositoryTest : ShellTestCase() { repository.persistsToDisk(bubbles) assertTrue(sparseArraysEqual(bubbles, repository.readFromDisk())) } -}
\ No newline at end of file +} diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 24c117911f7c..44c0b54546be 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -166,9 +166,8 @@ public final class MediaRouter2Manager { */ public void registerScanRequest() { if (mScanRequestCount.getAndIncrement() == 0) { - Client client = getOrCreateClient(); try { - mMediaRouterService.startScan(client); + mMediaRouterService.startScan(getOrCreateClient()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -194,9 +193,8 @@ public final class MediaRouter2Manager { } }) == 0) { - Client client = getOrCreateClient(); try { - mMediaRouterService.stopScan(client); + mMediaRouterService.stopScan(getOrCreateClient()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -425,15 +423,11 @@ public final class MediaRouter2Manager { */ @NonNull public List<RoutingSessionInfo> getRemoteSessions() { - Client client = getOrCreateClient(); - if (client != null) { - try { - return mMediaRouterService.getRemoteSessions(client); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + return mMediaRouterService.getRemoteSessions(getOrCreateClient()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return Collections.emptyList(); } /** @@ -516,14 +510,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setRouteVolumeWithManager( + getOrCreateClient(), requestId, route, volume); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -545,15 +537,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.setSessionVolumeWithManager( - client, requestId, sessionInfo.getId(), volume); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setSessionVolumeWithManager( + getOrCreateClient(), requestId, sessionInfo.getId(), volume); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -810,15 +799,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.selectRouteWithManager( - client, requestId, sessionInfo.getId(), route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.selectRouteWithManager( + getOrCreateClient(), requestId, sessionInfo.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -852,15 +838,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.deselectRouteWithManager( - client, requestId, sessionInfo.getId(), route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.deselectRouteWithManager( + getOrCreateClient(), requestId, sessionInfo.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -877,15 +860,12 @@ public final class MediaRouter2Manager { public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.releaseSessionWithManager( - client, requestId, sessionInfo.getId()); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.releaseSessionWithManager( + getOrCreateClient(), requestId, sessionInfo.getId()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -898,14 +878,11 @@ public final class MediaRouter2Manager { @NonNull MediaRoute2Info route) { int requestId = createTransferRequest(session, route); - Client client = getOrCreateClient(); - if (client != null) { - try { - mMediaRouterService.transferToRouteWithManager( - client, requestId, session.getId(), route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + mMediaRouterService.transferToRouteWithManager( + getOrCreateClient(), requestId, session.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -918,14 +895,11 @@ public final class MediaRouter2Manager { int requestId = createTransferRequest(oldSession, route); - Client client = getOrCreateClient(); - if (client != null) { - try { - mMediaRouterService.requestCreateSessionWithManager( - client, requestId, oldSession, route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + mMediaRouterService.requestCreateSessionWithManager( + getOrCreateClient(), requestId, oldSession, route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 281501e6043c..f4355c39819f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -84,7 +84,8 @@ public class LocalMediaManager implements BluetoothCallback { private InfoMediaManager mInfoMediaManager; private String mPackageName; private MediaDevice mOnTransferBluetoothDevice; - private AudioManager mAudioManager; + @VisibleForTesting + AudioManager mAudioManager; @VisibleForTesting List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java index b416738ade4a..1a08366734bc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java @@ -44,8 +44,6 @@ public class MobileStatusTracker { private final Handler mReceiverHandler; private final MobileTelephonyCallback mTelephonyCallback; - private boolean mListening = false; - /** * MobileStatusTracker constructors * @@ -78,7 +76,6 @@ public class MobileStatusTracker { * Config the MobileStatusTracker to start or stop monitoring platform signals. */ public void setListening(boolean listening) { - mListening = listening; if (listening) { mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback); } else { @@ -86,10 +83,6 @@ public class MobileStatusTracker { } } - public boolean isListening() { - return mListening; - } - private void updateDataSim() { int activeDataSubId = mDefaults.getActiveDataSubId(); if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 4332bd24e1d1..45c0d7823e99 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -57,6 +57,7 @@ public class WifiStatusTracker { private final Handler mHandler; private final Handler mMainThreadHandler; private final Set<Integer> mNetworks = new HashSet<>(); + private int mPrimaryNetworkId; // Save the previous HISTORY_SIZE states for logging. private final String[] mHistory = new String[HISTORY_SIZE]; // Where to copy the next state into. @@ -106,6 +107,7 @@ public class WifiStatusTracker { if (!mNetworks.contains(network.getNetId())) { mNetworks.add(network.getNetId()); } + mPrimaryNetworkId = network.getNetId(); updateWifiInfo(wifiInfo); updateStatusLabel(); mMainThreadHandler.post(() -> postResults()); @@ -121,10 +123,13 @@ public class WifiStatusTracker { recordLastWifiNetwork(log); if (mNetworks.contains(network.getNetId())) { mNetworks.remove(network.getNetId()); - updateWifiInfo(null); - updateStatusLabel(); - mMainThreadHandler.post(() -> postResults()); } + if (network.getNetId() != mPrimaryNetworkId) { + return; + } + updateWifiInfo(null); + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); } }; private final NetworkCallback mDefaultNetworkCallback = diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 8e850b25159c..24bb1bc9ac67 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -16,7 +16,6 @@ package com.android.settingslib.media; -import static android.bluetooth.BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES; import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; import static com.google.common.truth.Truth.assertThat; @@ -29,10 +28,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.media.AudioDeviceAttributes; +import android.media.AudioManager; +import android.media.AudioSystem; import android.media.MediaRoute2Info; import android.media.MediaRouter2Manager; import android.media.RoutingSessionInfo; @@ -71,6 +72,7 @@ public class LocalMediaManagerTest { private static final String TEST_CURRENT_DEVICE_ID = "currentDevice_id"; private static final String TEST_PACKAGE_NAME = "com.test.playmusic"; private static final String TEST_SESSION_ID = "session_id"; + private static final String TEST_ADDRESS = "00:01:02:03:04:05"; @Mock private InfoMediaManager mInfoMediaManager; @@ -90,6 +92,8 @@ public class LocalMediaManagerTest { private MediaRoute2Info mRouteInfo1; @Mock private MediaRoute2Info mRouteInfo2; + @Mock + private AudioManager mAudioManager; private Context mContext; private LocalMediaManager mLocalMediaManager; @@ -118,6 +122,7 @@ public class LocalMediaManagerTest { TEST_PACKAGE_NAME); mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, mInfoMediaManager, "com.test.packagename"); + mLocalMediaManager.mAudioManager = mAudioManager; } @Test @@ -551,16 +556,12 @@ public class LocalMediaManagerTest { } @Test - public void onDeviceListAdded_haveDisconnectedDevice_addDisconnectedDevice() { + public void onDeviceListAdded_haveMutingExpectedDevice_addMutingExpectedDevice() { final List<MediaDevice> devices = new ArrayList<>(); final MediaDevice device1 = mock(MediaDevice.class); - final MediaDevice device2 = mock(MediaDevice.class); - final MediaDevice device3 = mock(MediaDevice.class); mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class); devices.add(device1); - devices.add(device2); - mLocalMediaManager.mMediaDevices.add(device3); - mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice); + devices.add(mLocalMediaManager.mPhoneDevice); final List<LocalBluetoothProfile> profiles = new ArrayList<>(); final A2dpProfile a2dpProfile = mock(A2dpProfile.class); @@ -573,22 +574,25 @@ public class LocalMediaManagerTest { bluetoothDevices.add(bluetoothDevice); mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices); + AudioDeviceAttributes audioDeviceAttributes = new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, TEST_ADDRESS); + + when(mAudioManager.getMutingExpectedDevice()).thenReturn(audioDeviceAttributes); when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager); when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); when(cachedDevice.isConnected()).thenReturn(false); when(cachedDevice.getConnectableProfiles()).thenReturn(profiles); when(cachedDevice.getDevice()).thenReturn(bluetoothDevice); + when(cachedDevice.getAddress()).thenReturn(TEST_ADDRESS); when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice); when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>()); when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); - when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); - when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); - assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); + assertThat(mLocalMediaManager.mMediaDevices).hasSize(0); mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); @@ -634,68 +638,6 @@ public class LocalMediaManagerTest { } @Test - public void onDeviceListAdded_haveDisconnectedDevice_list5DisconnectedDevice() { - final List<MediaDevice> devices = new ArrayList<>(); - final MediaDevice device1 = mock(MediaDevice.class); - final MediaDevice device2 = mock(MediaDevice.class); - final MediaDevice device3 = mock(MediaDevice.class); - mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class); - devices.add(device1); - devices.add(device2); - mLocalMediaManager.mMediaDevices.add(device3); - mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice); - - final List<LocalBluetoothProfile> profiles = new ArrayList<>(); - final A2dpProfile a2dpProfile = mock(A2dpProfile.class); - profiles.add(a2dpProfile); - - final List<BluetoothDevice> bluetoothDevices = new ArrayList<>(); - final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class); - final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class); - final BluetoothDevice bluetoothDevice3 = mock(BluetoothDevice.class); - final BluetoothDevice bluetoothDevice4 = mock(BluetoothDevice.class); - final BluetoothDevice bluetoothDevice5 = mock(BluetoothDevice.class); - final BluetoothDevice bluetoothDevice6 = mock(BluetoothDevice.class); - final BluetoothClass bluetoothClass = mock(BluetoothClass.class); - final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); - final CachedBluetoothDeviceManager cachedManager = mock(CachedBluetoothDeviceManager.class); - bluetoothDevices.add(bluetoothDevice); - bluetoothDevices.add(bluetoothDevice2); - bluetoothDevices.add(bluetoothDevice3); - bluetoothDevices.add(bluetoothDevice4); - bluetoothDevices.add(bluetoothDevice5); - bluetoothDevices.add(bluetoothDevice6); - mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices); - - when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager); - when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); - when(cachedManager.findDevice(bluetoothDevice2)).thenReturn(cachedDevice); - when(cachedManager.findDevice(bluetoothDevice3)).thenReturn(cachedDevice); - when(cachedManager.findDevice(bluetoothDevice4)).thenReturn(cachedDevice); - when(cachedManager.findDevice(bluetoothDevice5)).thenReturn(cachedDevice); - when(cachedManager.findDevice(bluetoothDevice6)).thenReturn(cachedDevice); - when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - when(cachedDevice.isConnected()).thenReturn(false); - when(cachedDevice.getDevice()).thenReturn(bluetoothDevice); - when(cachedDevice.getConnectableProfiles()).thenReturn(profiles); - when(bluetoothDevice.getBluetoothClass()).thenReturn(bluetoothClass); - when(bluetoothClass.getDeviceClass()).thenReturn(AUDIO_VIDEO_HEADPHONES); - - when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); - when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); - when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); - when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); - when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); - - assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); - mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); - - assertThat(mLocalMediaManager.mMediaDevices).hasSize(7); - verify(mCallback).onDeviceListUpdate(any()); - } - - @Test public void onDeviceListAdded_bluetoothAdapterIsNull_noDisconnectedDeviceAdded() { final List<MediaDevice> devices = new ArrayList<>(); final MediaDevice device1 = mock(MediaDevice.class); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java new file mode 100644 index 000000000000..dc7e313dfcfa --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.wifi; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class WifiStatusTrackerTest { + @Mock Context mContext; + @Mock WifiManager mWifiManager; + @Mock NetworkScoreManager mNetworkScoreManager; + @Mock ConnectivityManager mConnectivityManager; + @Mock Runnable mCallback; + + private final ArgumentCaptor<ConnectivityManager.NetworkCallback> + mNetworkCallbackCaptor = + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + /** + * Verify that we only clear the WifiInfo if the primary network was lost. + */ + @Test + public void testWifiInfoClearedOnPrimaryNetworkLost() { + WifiStatusTracker wifiStatusTracker = new WifiStatusTracker(mContext, mWifiManager, + mNetworkScoreManager, mConnectivityManager, mCallback); + wifiStatusTracker.setListening(true); + + verify(mConnectivityManager) + .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture(), any()); + + // Trigger a validation callback for the primary Wifi network. + WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class); + when(primaryWifiInfo.makeCopy(anyLong())).thenReturn(primaryWifiInfo); + when(primaryWifiInfo.isPrimary()).thenReturn(true); + int primaryRssi = -55; + when(primaryWifiInfo.getRssi()).thenReturn(primaryRssi); + NetworkCapabilities primaryCap = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setTransportInfo(primaryWifiInfo) + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .build(); + Network primaryNetwork = Mockito.mock(Network.class); + int primaryNetworkId = 1; + when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap); + + // Verify primary wifi info is the one being used. + assertThat(wifiStatusTracker.connected).isTrue(); + assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi); + + // Trigger a validation callback for the non-primary Wifi network. + WifiInfo nonPrimaryWifiInfo = Mockito.mock(WifiInfo.class); + when(nonPrimaryWifiInfo.makeCopy(anyLong())).thenReturn(nonPrimaryWifiInfo); + when(nonPrimaryWifiInfo.isPrimary()).thenReturn(false); + int nonPrimaryRssi = -75; + when(primaryWifiInfo.getRssi()).thenReturn(nonPrimaryRssi); + NetworkCapabilities nonPrimaryCap = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setTransportInfo(nonPrimaryWifiInfo) + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .build(); + Network nonPrimaryNetwork = Mockito.mock(Network.class); + int nonPrimaryNetworkId = 2; + when(nonPrimaryNetwork.getNetId()).thenReturn(nonPrimaryNetworkId); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(nonPrimaryNetwork, nonPrimaryCap); + + // Verify primary wifi info is still the one being used. + assertThat(wifiStatusTracker.connected).isTrue(); + assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi); + + // Lose the non-primary network. + mNetworkCallbackCaptor.getValue().onLost(nonPrimaryNetwork); + + // Verify primary wifi info is still the one being used. + assertThat(wifiStatusTracker.connected).isTrue(); + assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi); + + // Lose the primary network. + mNetworkCallbackCaptor.getValue().onLost(primaryNetwork); + + // Verify we aren't connected anymore. + assertThat(wifiStatusTracker.connected).isFalse(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 73bc92d270b7..b0f025d4a195 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -61,7 +61,7 @@ public class Flags { new ResourceBooleanFlag(108, R.bool.config_notificationToContents); public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS = - new BooleanFlag(109, false); + new BooleanFlag(109, false, true); public static final BooleanFlag FSI_REQUIRES_KEYGUARD = new BooleanFlag(110, false, true); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index eeb1010693fc..ec0d0811ee76 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -134,9 +134,18 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mQSCarrierGroupController .setOnSingleCarrierChangedListener(mView::setIsSingleCarrier); - List<String> rssiIgnoredSlots = List.of( - getResources().getString(com.android.internal.R.string.status_bar_mobile) - ); + List<String> rssiIgnoredSlots; + + if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) { + rssiIgnoredSlots = List.of( + getResources().getString(com.android.internal.R.string.status_bar_no_calling), + getResources().getString(com.android.internal.R.string.status_bar_call_strength) + ); + } else { + rssiIgnoredSlots = List.of( + getResources().getString(com.android.internal.R.string.status_bar_mobile) + ); + } mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots, mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)); diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt index e925b5472c27..2dac63905524 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt @@ -27,6 +27,7 @@ data class CellSignalState( @JvmField val contentDescription: String? = null, @JvmField val typeContentDescription: String? = null, @JvmField val roaming: Boolean = false, + @JvmField val providerModelBehavior: Boolean = false ) { /** * Changes the visibility of this state by returning a copy with the visibility changed. @@ -40,4 +41,4 @@ data class CellSignalState( if (this.visible == visible) return this else return copy(visible = visible) } -} +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java index 703b95a082dc..592da6554b90 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java @@ -45,7 +45,7 @@ public class QSCarrier extends LinearLayout { private View mSpacer; @Nullable private CellSignalState mLastSignalState; - private boolean mMobileSignalInitialized = false; + private boolean mProviderModelInitialized = false; private boolean mIsSingleCarrier; public QSCarrier(Context context) { @@ -96,25 +96,35 @@ public class QSCarrier extends LinearLayout { mMobileRoaming.setImageTintList(colorStateList); mMobileSignal.setImageTintList(colorStateList); - if (!mMobileSignalInitialized) { - mMobileSignalInitialized = true; - mMobileSignal.setImageDrawable(new SignalDrawable(mContext)); + if (state.providerModelBehavior) { + if (!mProviderModelInitialized) { + mProviderModelInitialized = true; + mMobileSignal.setImageDrawable( + mContext.getDrawable(R.drawable.ic_qs_no_calling_sms)); + } + mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId)); + mMobileSignal.setContentDescription(state.contentDescription); + } else { + if (!mProviderModelInitialized) { + mProviderModelInitialized = true; + mMobileSignal.setImageDrawable(new SignalDrawable(mContext)); + } + mMobileSignal.setImageLevel(state.mobileSignalIconId); + StringBuilder contentDescription = new StringBuilder(); + if (state.contentDescription != null) { + contentDescription.append(state.contentDescription).append(", "); + } + if (state.roaming) { + contentDescription + .append(mContext.getString(R.string.data_connection_roaming)) + .append(", "); + } + // TODO: show mobile data off/no internet text for 5 seconds before carrier text + if (hasValidTypeContentDescription(state.typeContentDescription)) { + contentDescription.append(state.typeContentDescription); + } + mMobileSignal.setContentDescription(contentDescription); } - mMobileSignal.setImageLevel(state.mobileSignalIconId); - StringBuilder contentDescription = new StringBuilder(); - if (state.contentDescription != null) { - contentDescription.append(state.contentDescription).append(", "); - } - if (state.roaming) { - contentDescription - .append(mContext.getString(R.string.data_connection_roaming)) - .append(", "); - } - // TODO: show mobile data off/no internet text for 5 seconds before carrier text - if (hasValidTypeContentDescription(state.typeContentDescription)) { - contentDescription.append(state.typeContentDescription); - } - mMobileSignal.setContentDescription(contentDescription); } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java index cb76ee2bb6a1..6908e5ab49e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java @@ -42,7 +42,10 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; @@ -75,6 +78,7 @@ public class QSCarrierGroupController { private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS]; private int[] mLastSignalLevel = new int[SIM_SLOTS]; private String[] mLastSignalLevelDescription = new String[SIM_SLOTS]; + private final boolean mProviderModel; private final CarrierConfigTracker mCarrierConfigTracker; private boolean mIsSingleCarrier; @@ -86,6 +90,9 @@ public class QSCarrierGroupController { private final SignalCallback mSignalCallback = new SignalCallback() { @Override public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) { + if (mProviderModel) { + return; + } int slotIndex = getSlotIndex(indicators.subId); if (slotIndex >= SIM_SLOTS) { Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); @@ -100,12 +107,91 @@ public class QSCarrierGroupController { indicators.statusIcon.icon, indicators.statusIcon.contentDescription, indicators.typeContentDescription.toString(), - indicators.roaming + indicators.roaming, + mProviderModel ); mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); } @Override + public void setCallIndicator(@NonNull IconState statusIcon, int subId) { + if (!mProviderModel) { + return; + } + int slotIndex = getSlotIndex(subId); + if (slotIndex >= SIM_SLOTS) { + Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); + return; + } + if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, "Invalid SIM slot index for subscription: " + subId); + return; + } + + boolean displayCallStrengthIcon = + mCarrierConfigTracker.getCallStrengthConfig(subId); + + if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) { + if (statusIcon.visible) { + mInfos[slotIndex] = new CellSignalState( + true, + statusIcon.icon, + statusIcon.contentDescription, + "", + false, + mProviderModel); + } else { + // Whenever the no Calling & SMS state is cleared, switched to the last + // known call strength icon. + if (displayCallStrengthIcon) { + mInfos[slotIndex] = new CellSignalState( + true, + mLastSignalLevel[slotIndex], + mLastSignalLevelDescription[slotIndex], + "", + false, + mProviderModel); + } else { + mInfos[slotIndex] = new CellSignalState( + true, + R.drawable.ic_qs_sim_card, + "", + "", + false, + mProviderModel); + } + } + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); + } else { + mLastSignalLevel[slotIndex] = statusIcon.icon; + mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription; + // Only Shows the call strength icon when the no Calling & SMS icon is not + // shown. + if (mInfos[slotIndex].mobileSignalIconId + != R.drawable.ic_qs_no_calling_sms) { + if (displayCallStrengthIcon) { + mInfos[slotIndex] = new CellSignalState( + true, + statusIcon.icon, + statusIcon.contentDescription, + "", + false, + mProviderModel); + } else { + mInfos[slotIndex] = new CellSignalState( + true, + R.drawable.ic_qs_sim_card, + "", + "", + false, + mProviderModel); + } + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); + } + } + } + + @Override public void setNoSims(boolean hasNoSims, boolean simDetected) { if (hasNoSims) { for (int i = 0; i < SIM_SLOTS; i++) { @@ -133,8 +219,14 @@ public class QSCarrierGroupController { @Background Handler bgHandler, @Main Looper mainLooper, NetworkController networkController, CarrierTextManager.Builder carrierTextManagerBuilder, Context context, - CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) { + CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags, + SlotIndexResolver slotIndexResolver) { + if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) { + mProviderModel = true; + } else { + mProviderModel = false; + } mActivityStarter = activityStarter; mBgHandler = bgHandler; mNetworkController = networkController; @@ -170,7 +262,8 @@ public class QSCarrierGroupController { R.drawable.ic_qs_no_calling_sms, context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(), "", - false); + false, + mProviderModel); mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0]; mLastSignalLevelDescription[i] = context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]) @@ -258,7 +351,8 @@ public class QSCarrierGroupController { for (int i = 0; i < SIM_SLOTS; i++) { if (mInfos[i].visible && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) { - mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false); + mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false, + mProviderModel); } } } @@ -376,13 +470,15 @@ public class QSCarrierGroupController { private final CarrierTextManager.Builder mCarrierTextControllerBuilder; private final Context mContext; private final CarrierConfigTracker mCarrierConfigTracker; + private final FeatureFlags mFeatureFlags; private final SlotIndexResolver mSlotIndexResolver; @Inject public Builder(ActivityStarter activityStarter, @Background Handler handler, @Main Looper looper, NetworkController networkController, CarrierTextManager.Builder carrierTextControllerBuilder, Context context, - CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) { + CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags, + SlotIndexResolver slotIndexResolver) { mActivityStarter = activityStarter; mHandler = handler; mLooper = looper; @@ -390,6 +486,7 @@ public class QSCarrierGroupController { mCarrierTextControllerBuilder = carrierTextControllerBuilder; mContext = context; mCarrierConfigTracker = carrierConfigTracker; + mFeatureFlags = featureFlags; mSlotIndexResolver = slotIndexResolver; } @@ -401,7 +498,7 @@ public class QSCarrierGroupController { public QSCarrierGroupController build() { return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper, mNetworkController, mCarrierTextControllerBuilder, mContext, - mCarrierConfigTracker, mSlotIndexResolver); + mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index d5efe367ae2b..f629a0361ddb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -806,8 +806,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi return; } - mTelephonyManager.setDataEnabledForReason( - TelephonyManager.DATA_ENABLED_REASON_USER, enabled); + mTelephonyManager.setDataEnabled(enabled); if (disableOtherSubscriptions) { final List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt index 8c8f54fe9c3d..ad073c073ed8 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt @@ -49,8 +49,8 @@ class UserFileManagerImpl @Inject constructor( ) : UserFileManager, CoreStartable(context) { companion object { private const val FILES = "files" - private const val SHARED_PREFS = "shared_prefs" - internal const val ID = "UserFileManager" + @VisibleForTesting internal const val SHARED_PREFS = "shared_prefs" + @VisibleForTesting internal const val ID = "UserFileManager" } private val broadcastReceiver = object : BroadcastReceiver() { @@ -85,13 +85,15 @@ class UserFileManagerImpl @Inject constructor( fileName ) } else { - Environment.buildPath( + val secondaryFile = Environment.buildPath( context.filesDir, ID, userId.toString(), FILES, fileName ) + ensureParentDirExists(secondaryFile) + secondaryFile } } @@ -114,6 +116,7 @@ class UserFileManagerImpl @Inject constructor( fileName ) + ensureParentDirExists(secondaryUserDir) return context.getSharedPreferences(secondaryUserDir, mode) } @@ -141,4 +144,18 @@ class UserFileManagerImpl @Inject constructor( } } } + + /** + * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs + * recursively. + */ + @VisibleForTesting + internal fun ensureParentDirExists(file: File) { + val parent = file.parentFile + if (!parent.exists()) { + if (!parent.mkdirs()) { + Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}") + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java index ec221b7eccc0..f2014e9deb72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java @@ -26,17 +26,25 @@ import android.net.NetworkCapabilities; import android.os.Handler; import android.os.Looper; import android.provider.Settings.Global; +import android.telephony.AccessNetworkConstants; import android.telephony.CellSignalStrength; import android.telephony.CellSignalStrengthCdma; +import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.ims.ImsException; +import android.telephony.ims.ImsMmTelManager; +import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; +import android.telephony.ims.RegistrationManager.RegistrationCallback; import android.text.Html; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.AccessibilityContentDescriptions; import com.android.settingslib.SignalIcon.MobileIconGroup; import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.mobile.MobileMappings.Config; @@ -46,6 +54,8 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.util.CarrierConfigTracker; import java.io.PrintWriter; @@ -60,22 +70,33 @@ import java.util.Map; public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> { private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); private static final int STATUS_HISTORY_SIZE = 64; + private static final int IMS_TYPE_WWAN = 1; + private static final int IMS_TYPE_WLAN = 2; + private static final int IMS_TYPE_WLAN_CROSS_SIM = 3; private final TelephonyManager mPhone; private final CarrierConfigTracker mCarrierConfigTracker; + private final ImsMmTelManager mImsMmTelManager; private final SubscriptionDefaults mDefaults; private final String mNetworkNameDefault; private final String mNetworkNameSeparator; private final ContentObserver mObserver; + private final boolean mProviderModelBehavior; + private final Handler mReceiverHandler; + private int mImsType = IMS_TYPE_WWAN; // Save entire info for logging, we only use the id. final SubscriptionInfo mSubscriptionInfo; private Map<String, MobileIconGroup> mNetworkToIconLookup; + private int mLastLevel; private MobileIconGroup mDefaultIcons; private Config mConfig; @VisibleForTesting boolean mInflateSignalStrengths = false; + private int mLastWwanLevel; + private int mLastWlanLevel; + private int mLastWlanCrossSimLevel; @VisibleForTesting - final MobileStatusTracker mMobileStatusTracker; + MobileStatusTracker mMobileStatusTracker; // Save the previous STATUS_HISTORY_SIZE states for logging. private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE]; @@ -112,6 +133,52 @@ public class MobileSignalController extends SignalController<MobileState, Mobile } }; + private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() { + @Override + public void onRegistered(ImsRegistrationAttributes attributes) { + Log.d(mTag, "onRegistered: " + "attributes=" + attributes); + int imsTransportType = attributes.getTransportType(); + int registrationAttributes = attributes.getAttributeFlags(); + if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { + mImsType = IMS_TYPE_WWAN; + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false), + getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { + if (registrationAttributes == 0) { + mImsType = IMS_TYPE_WLAN; + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true), + getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } else if (registrationAttributes + == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) { + mImsType = IMS_TYPE_WLAN_CROSS_SIM; + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false), + getCallStrengthDescription( + mLastWlanCrossSimLevel, /* isWifi= */false)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + } + } + + @Override + public void onUnregistered(ImsReasonInfo info) { + Log.d(mTag, "onDeregistered: " + "info=" + info); + mImsType = IMS_TYPE_WWAN; + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false), + getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + }; + // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't // need listener lists anymore. public MobileSignalController( @@ -125,7 +192,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile SubscriptionDefaults defaults, Looper receiverLooper, CarrierConfigTracker carrierConfigTracker, - MobileStatusTrackerFactory mobileStatusTrackerFactory + FeatureFlags featureFlags ) { super("MobileSignalController(" + info.getSubscriptionId() + ")", context, NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler, @@ -139,6 +206,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile R.string.status_bar_network_name_separator).toString(); mNetworkNameDefault = getTextIfExists( com.android.internal.R.string.lockscreen_carrier_default).toString(); + mReceiverHandler = new Handler(receiverLooper); mNetworkToIconLookup = mapIconSets(mConfig); mDefaultIcons = getDefaultIcons(mConfig); @@ -155,7 +223,10 @@ public class MobileSignalController extends SignalController<MobileState, Mobile updateTelephony(); } }; - mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback); + mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId()); + mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper, + info, mDefaults, mMobileCallback); + mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS); } void setConfiguration(Config config) { @@ -200,14 +271,41 @@ public class MobileSignalController extends SignalController<MobileState, Mobile mContext.getContentResolver().registerContentObserver(Global.getUriFor( Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()), true, mObserver); + if (mProviderModelBehavior) { + mReceiverHandler.post(mTryRegisterIms); + } } + // There is no listener to monitor whether the IMS service is ready, so we have to retry the + // IMS registration. + private final Runnable mTryRegisterIms = new Runnable() { + private static final int MAX_RETRY = 12; + private int mRetryCount; + + @Override + public void run() { + try { + mRetryCount++; + mImsMmTelManager.registerImsRegistrationCallback( + mReceiverHandler::post, mRegistrationCallback); + Log.d(mTag, "registerImsRegistrationCallback succeeded"); + } catch (RuntimeException | ImsException e) { + if (mRetryCount < MAX_RETRY) { + Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e); + // Wait for 5 seconds to retry + mReceiverHandler.postDelayed(mTryRegisterIms, 5000); + } + } + } + }; + /** * Stop listening for phone state changes. */ public void unregisterListener() { mMobileStatusTracker.setListening(false); mContext.getContentResolver().unregisterContentObserver(mObserver); + mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback); } private void updateInflateSignalStrength() { @@ -296,7 +394,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile CharSequence qsDescription = null; if (mCurrentState.dataSim) { - // only show QS icons if the state is also default + // If using provider model behavior, only show QS icons if the state is also default if (!mCurrentState.isDefault) { return new QsInfo(qsTypeIcon, qsIcon, qsDescription); } @@ -318,15 +416,32 @@ public class MobileSignalController extends SignalController<MobileState, Mobile private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) { final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault(); - IconState statusIcon = new IconState( - mCurrentState.enabled && !mCurrentState.airplaneMode, - getCurrentIconId(), contentDescription); + boolean showTriangle = false; + int typeIcon = 0; + IconState statusIcon = null; + + if (mProviderModelBehavior) { + boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled) + && (mCurrentState.dataSim && mCurrentState.isDefault); + typeIcon = + (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0; + showDataIconStatusBar |= mCurrentState.roaming; + statusIcon = new IconState( + showDataIconStatusBar && !mCurrentState.airplaneMode, + getCurrentIconId(), contentDescription); + + showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode; + } else { + statusIcon = new IconState( + mCurrentState.enabled && !mCurrentState.airplaneMode, + getCurrentIconId(), contentDescription); - boolean showDataIconInStatusBar = - (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled; - int typeIcon = - (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0; - boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; + boolean showDataIconInStatusBar = + (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled; + typeIcon = + (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0; + showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; + } return new SbInfo(showTriangle, typeIcon, statusIcon); } @@ -445,7 +560,144 @@ public class MobileSignalController extends SignalController<MobileState, Mobile } private void updateMobileStatus(MobileStatus mobileStatus) { + int lastVoiceState = mCurrentState.getVoiceServiceState(); mCurrentState.setFromMobileStatus(mobileStatus); + + notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength); + if (mProviderModelBehavior) { + maybeNotifyCallStateChanged(lastVoiceState); + } + } + + /** Call state changed is only applicable when provider model behavior is true */ + private void maybeNotifyCallStateChanged(int lastVoiceState) { + int currentVoiceState = mCurrentState.getVoiceServiceState(); + if (lastVoiceState == currentVoiceState) { + return; + } + // Only update the no calling Status in the below scenarios + // 1. The first valid voice state has been received + // 2. The voice state has been changed and either the last or current state is + // ServiceState.STATE_IN_SERVICE + if (lastVoiceState == -1 + || (lastVoiceState == ServiceState.STATE_IN_SERVICE + || currentVoiceState == ServiceState.STATE_IN_SERVICE)) { + boolean isNoCalling = mCurrentState.isNoCalling(); + isNoCalling &= !hideNoCalling(); + IconState statusIcon = new IconState(isNoCalling, + R.drawable.ic_qs_no_calling_sms, + getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString()); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + } + + void updateNoCallingState() { + int currentVoiceState = mCurrentState.getVoiceServiceState(); + boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE; + isNoCalling &= !hideNoCalling(); + IconState statusIcon = new IconState(isNoCalling, + R.drawable.ic_qs_no_calling_sms, + getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString()); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + + private boolean hideNoCalling() { + return mNetworkController.hasDefaultNetwork() + && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId()); + } + + private int getCallStrengthIcon(int level, boolean isWifi) { + return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level] + : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level]; + } + + private String getCallStrengthDescription(int level, boolean isWifi) { + return isWifi + ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level]) + .toString() + : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level]) + .toString(); + } + + void refreshCallIndicator(SignalCallback callback) { + boolean isNoCalling = mCurrentState.isNoCalling(); + isNoCalling &= !hideNoCalling(); + IconState statusIcon = new IconState(isNoCalling, + R.drawable.ic_qs_no_calling_sms, + getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString()); + callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId()); + + switch (mImsType) { + case IMS_TYPE_WWAN: + statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false), + getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false)); + break; + case IMS_TYPE_WLAN: + statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true), + getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true)); + break; + case IMS_TYPE_WLAN_CROSS_SIM: + statusIcon = new IconState( + true, + getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false), + getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false)); + } + callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + + void notifyWifiLevelChange(int level) { + if (!mProviderModelBehavior) { + return; + } + mLastWlanLevel = level; + if (mImsType != IMS_TYPE_WLAN) { + return; + } + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(level, /* isWifi= */true), + getCallStrengthDescription(level, /* isWifi= */true)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + + void notifyDefaultMobileLevelChange(int level) { + if (!mProviderModelBehavior) { + return; + } + mLastWlanCrossSimLevel = level; + if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) { + return; + } + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(level, /* isWifi= */false), + getCallStrengthDescription(level, /* isWifi= */false)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + + void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) { + if (!mProviderModelBehavior) { + return; + } + int newLevel = getSignalLevel(signalStrength); + if (newLevel != mLastLevel) { + mLastLevel = newLevel; + mLastWwanLevel = newLevel; + if (mImsType == IMS_TYPE_WWAN) { + IconState statusIcon = new IconState( + true, + getCallStrengthIcon(newLevel, /* isWifi= */false), + getCallStrengthDescription(newLevel, /* isWifi= */false)); + notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId()); + } + if (mCurrentState.dataSim) { + mNetworkController.notifyDefaultMobileLevelChange(newLevel); + } + } } int getSignalLevel(SignalStrength signalStrength) { @@ -549,14 +801,19 @@ public class MobileSignalController extends SignalController<MobileState, Mobile mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE; } + @VisibleForTesting + void setImsType(int imsType) { + mImsType = imsType; + } + @Override public void dump(PrintWriter pw) { super.dump(pw); pw.println(" mSubscription=" + mSubscriptionInfo + ","); + pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ","); pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ","); pw.println(" isDataDisabled=" + isDataDisabled() + ","); pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ","); - pw.println(" mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening()); pw.println(" MobileStatusHistory"); int size = 0; for (int i = 0; i < STATUS_HISTORY_SIZE; i++) { @@ -586,11 +843,6 @@ public class MobileSignalController extends SignalController<MobileState, Mobile icon = iconState; description = desc; } - - @Override - public String toString() { - return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon; - } } /** Box for status bar icon info */ @@ -604,11 +856,5 @@ public class MobileSignalController extends SignalController<MobileState, Mobile ratTypeIcon = typeIcon; icon = iconState; } - - @Override - public String toString() { - return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon - + " icon=" + icon; - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt deleted file mode 100644 index f0e52f190165..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.connectivity - -import android.content.Context -import android.os.Looper -import android.telephony.SubscriptionInfo -import android.telephony.TelephonyManager -import com.android.settingslib.mobile.MobileMappings -import com.android.settingslib.mobile.MobileStatusTracker -import com.android.systemui.util.CarrierConfigTracker -import javax.inject.Inject - -/** - * Factory to make MobileSignalController injectable - */ -internal class MobileSignalControllerFactory @Inject constructor( - val context: Context, - val callbackHandler: CallbackHandler, - val carrierConfigTracker: CarrierConfigTracker, -) { - fun createMobileSignalController( - config: MobileMappings.Config, - hasMobileData: Boolean, - phone: TelephonyManager, - networkController: NetworkControllerImpl, - subscriptionInfo: SubscriptionInfo, - subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults, - receiverLooper: Looper, - ): MobileSignalController { - val mobileTrackerFactory = MobileStatusTrackerFactory( - phone, - receiverLooper, - subscriptionInfo, - subscriptionDefaults) - - return MobileSignalController( - context, - config, - hasMobileData, - phone, - callbackHandler, - networkController, - subscriptionInfo, - subscriptionDefaults, - receiverLooper, - carrierConfigTracker, - mobileTrackerFactory, - ) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt deleted file mode 100644 index a4c1a1989933..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.connectivity - -import android.os.Looper -import android.telephony.SubscriptionInfo -import android.telephony.TelephonyManager -import com.android.settingslib.mobile.MobileStatusTracker - -/** - * Factory for [MobileStatusTracker], which lives in SettingsLib - */ -class MobileStatusTrackerFactory ( - val phone: TelephonyManager, - val receiverLooper: Looper, - val info: SubscriptionInfo, - val defaults: MobileStatusTracker.SubscriptionDefaults, -) { - fun createTracker( - callback: MobileStatusTracker.Callback - ): MobileStatusTracker { - return MobileStatusTracker( - phone, - receiverLooper, - info, - defaults, - callback) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index ea7ec4f7fc39..268531d48f9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -71,6 +71,8 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogLevel; import com.android.systemui.log.dagger.StatusBarNetworkControllerLog; @@ -131,11 +133,12 @@ public class NetworkControllerImpl extends BroadcastReceiver private final BroadcastDispatcher mBroadcastDispatcher; private final DemoModeController mDemoModeController; private final Object mLock = new Object(); + private final boolean mProviderModelBehavior; private Config mConfig; private final CarrierConfigTracker mCarrierConfigTracker; + private final FeatureFlags mFeatureFlags; private final DumpManager mDumpManager; private final LogBuffer mLogBuffer; - private final MobileSignalControllerFactory mMobileFactory; private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -231,9 +234,9 @@ public class NetworkControllerImpl extends BroadcastReceiver DemoModeController demoModeController, CarrierConfigTracker carrierConfigTracker, WifiStatusTrackerFactory trackerFactory, - MobileSignalControllerFactory mobileFactory, @Main Handler handler, InternetDialogFactory internetDialogFactory, + FeatureFlags featureFlags, DumpManager dumpManager, @StatusBarNetworkControllerLog LogBuffer logBuffer) { this(context, connectivityManager, @@ -253,8 +256,8 @@ public class NetworkControllerImpl extends BroadcastReceiver demoModeController, carrierConfigTracker, trackerFactory, - mobileFactory, handler, + featureFlags, dumpManager, logBuffer); mReceiverHandler.post(mRegisterListeners); @@ -279,8 +282,8 @@ public class NetworkControllerImpl extends BroadcastReceiver DemoModeController demoModeController, CarrierConfigTracker carrierConfigTracker, WifiStatusTrackerFactory trackerFactory, - MobileSignalControllerFactory mobileFactory, @Main Handler handler, + FeatureFlags featureFlags, DumpManager dumpManager, LogBuffer logBuffer ) { @@ -294,7 +297,6 @@ public class NetworkControllerImpl extends BroadcastReceiver mCallbackHandler = callbackHandler; mDataSaverController = new DataSaverControllerImpl(context); mBroadcastDispatcher = broadcastDispatcher; - mMobileFactory = mobileFactory; mSubscriptionManager = subManager; mSubDefaults = defaultsHandler; @@ -302,6 +304,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mHasMobileDataFeature = telephonyManager.isDataCapable(); mDemoModeController = demoModeController; mCarrierConfigTracker = carrierConfigTracker; + mFeatureFlags = featureFlags; mDumpManager = dumpManager; mLogBuffer = logBuffer; @@ -453,6 +456,7 @@ public class NetworkControllerImpl extends BroadcastReceiver }; mDemoModeController.addCallback(this); + mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS); mDumpManager.registerDumpable(TAG, this); } @@ -493,16 +497,16 @@ public class NetworkControllerImpl extends BroadcastReceiver // broadcasts IntentFilter filter = new IntentFilter(); - filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - filter.addAction(Intent.ACTION_SERVICE_STATE); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); - filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY); filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); + filter.addAction(Intent.ACTION_SERVICE_STATE); filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); - filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY); mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler); mListening = true; @@ -655,6 +659,20 @@ public class NetworkControllerImpl extends BroadcastReceiver return controller != null ? controller.getNetworkNameForCarrierWiFi() : ""; } + void notifyWifiLevelChange(int level) { + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.notifyWifiLevelChange(level); + } + } + + void notifyDefaultMobileLevelChange(int level) { + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.notifyDefaultMobileLevelChange(level); + } + } + private void notifyControllersMobileDataChanged() { for (int i = 0; i < mMobileSignalControllers.size(); i++) { MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); @@ -727,6 +745,9 @@ public class NetworkControllerImpl extends BroadcastReceiver for (int i = 0; i < mMobileSignalControllers.size(); i++) { MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); mobileSignalController.notifyListeners(cb); + if (mProviderModelBehavior) { + mobileSignalController.refreshCallIndicator(cb); + } } mCallbackHandler.setListening(cb, true); } @@ -841,6 +862,9 @@ public class NetworkControllerImpl extends BroadcastReceiver for (int i = 0; i < mMobileSignalControllers.size(); i++) { MobileSignalController controller = mMobileSignalControllers.valueAt(i); controller.setConfiguration(mConfig); + if (mProviderModelBehavior) { + controller.refreshCallIndicator(mCallbackHandler); + } } refreshLocale(); } @@ -957,15 +981,11 @@ public class NetworkControllerImpl extends BroadcastReceiver mMobileSignalControllers.put(subId, cachedControllers.get(subId)); cachedControllers.remove(subId); } else { - MobileSignalController controller = mMobileFactory.createMobileSignalController( - mConfig, - mHasMobileDataFeature, - mPhone.createForSubscriptionId(subId), - this, - subscriptions.get(i), - mSubDefaults, - mReceiverHandler.getLooper() - ); + MobileSignalController controller = new MobileSignalController(mContext, mConfig, + mHasMobileDataFeature, mPhone.createForSubscriptionId(subId), + mCallbackHandler, this, subscriptions.get(i), + mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker, + mFeatureFlags); controller.setUserSetupComplete(mUserSetup); mMobileSignalControllers.put(subId, controller); if (subscriptions.get(i).getSimSlotIndex() == 0) { @@ -1119,11 +1139,24 @@ public class NetworkControllerImpl extends BroadcastReceiver || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); pushConnectivityToSignals(); - mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) - && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) - && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); - mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, - mNoNetworksAvailable); + if (mProviderModelBehavior) { + mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); + mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, + mNoNetworksAvailable); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.updateNoCallingState(); + } + notifyAllListeners(); + } else { + mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); + mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, + mNoNetworksAvailable); + } } /** @@ -1313,7 +1346,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mMobileSignalControllers.clear(); int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); for (int i = start /* get out of normal index range */; i < start + num; i++) { - subs.add(addDemoModeSignalController(i, i)); + subs.add(addSignalController(i, i)); } mCallbackHandler.setSubs(subs); for (int i = 0; i < mMobileSignalControllers.size(); i++) { @@ -1339,7 +1372,7 @@ public class NetworkControllerImpl extends BroadcastReceiver List<SubscriptionInfo> subs = new ArrayList<>(); while (mMobileSignalControllers.size() <= slot) { int nextSlot = mMobileSignalControllers.size(); - subs.add(addDemoModeSignalController(nextSlot, nextSlot)); + subs.add(addSignalController(nextSlot, nextSlot)); } if (!subs.isEmpty()) { mCallbackHandler.setSubs(subs); @@ -1429,20 +1462,14 @@ public class NetworkControllerImpl extends BroadcastReceiver mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; } - private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) { + private SubscriptionInfo addSignalController(int id, int simSlotIndex) { SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, null, null, null, "", false, null, null); - - MobileSignalController controller = mMobileFactory.createMobileSignalController( - mConfig, - mHasMobileDataFeature, - mPhone.createForSubscriptionId(info.getSubscriptionId()), - this, - info, - mSubDefaults, - mReceiverHandler.getLooper() - ); - + MobileSignalController controller = new MobileSignalController(mContext, + mConfig, mHasMobileDataFeature, + mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this, + info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker, + mFeatureFlags); mMobileSignalControllers.put(id, controller); controller.getState().userSetup = true; return info; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java index 12f2c22ab86a..87cdb17245f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java @@ -222,6 +222,7 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup> mCurrentState.connected = mWifiTracker.connected; mCurrentState.ssid = mWifiTracker.ssid; mCurrentState.rssi = mWifiTracker.rssi; + boolean levelChanged = mCurrentState.level != mWifiTracker.level; mCurrentState.level = mWifiTracker.level; mCurrentState.statusLabel = mWifiTracker.statusLabel; mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged; @@ -229,6 +230,10 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup> mCurrentState.iconGroup = mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup : mUnmergedWifiIconGroup; + + if (levelChanged) { + mNetworkController.notifyWifiLevelChange(mCurrentState.level); + } } boolean isCarrierMergedWifi(int subId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 492734e93dca..ee242a4b1b75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -26,6 +26,8 @@ import android.util.Log; import com.android.settingslib.mobile.TelephonyIcons; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; @@ -64,6 +66,7 @@ public class StatusBarSignalPolicy implements SignalCallback, private final Handler mHandler = Handler.getMain(); private final CarrierConfigTracker mCarrierConfigTracker; private final TunerService mTunerService; + private final FeatureFlags mFeatureFlags; private boolean mHideAirplane; private boolean mHideMobile; @@ -87,7 +90,8 @@ public class StatusBarSignalPolicy implements SignalCallback, CarrierConfigTracker carrierConfigTracker, NetworkController networkController, SecurityController securityController, - TunerService tunerService + TunerService tunerService, + FeatureFlags featureFlags ) { mContext = context; @@ -96,6 +100,7 @@ public class StatusBarSignalPolicy implements SignalCallback, mNetworkController = networkController; mSecurityController = securityController; mTunerService = tunerService; + mFeatureFlags = featureFlags; mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); @@ -373,6 +378,40 @@ public class StatusBarSignalPolicy implements SignalCallback, } @Override + public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, + boolean noNetworksAvailable) { + if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) { + return; + } + if (DEBUG) { + Log.d(TAG, "setConnectivityStatus: " + + "noDefaultNetwork = " + noDefaultNetwork + "," + + "noValidatedNetwork = " + noValidatedNetwork + "," + + "noNetworksAvailable = " + noNetworksAvailable); + } + WifiIconState newState = mWifiIconState.copy(); + newState.noDefaultNetwork = noDefaultNetwork; + newState.noValidatedNetwork = noValidatedNetwork; + newState.noNetworksAvailable = noNetworksAvailable; + newState.slot = mSlotWifi; + newState.airplaneSpacerVisible = mIsAirplaneMode; + if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) { + newState.visible = true; + newState.resId = R.drawable.ic_qs_no_internet_unavailable; + } else if (noDefaultNetwork && !noNetworksAvailable + && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { + newState.visible = true; + newState.resId = R.drawable.ic_qs_no_internet_available; + } else { + newState.visible = false; + newState.resId = 0; + } + updateWifiIconWithState(newState); + mWifiIconState = newState; + } + + + @Override public void setEthernetIndicators(IconState state) { boolean visible = state.visible && !mHideEthernet; int resId = state.icon; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 2e6664820fa7..dfcdaefd8e03 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -62,7 +62,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.LatencyTracker; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.users.UserCreatingDialog; -import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; @@ -167,6 +166,7 @@ public class UserSwitcherController implements Dumpable { private final AtomicBoolean mGuestIsResetting; private final AtomicBoolean mGuestCreationScheduled; private FalsingManager mFalsingManager; + @Nullable private View mView; private String mCreateSupervisedUserPackage; private GlobalSettings mGlobalSettings; @@ -572,9 +572,11 @@ public class UserSwitcherController implements Dumpable { protected void switchToUserId(int id) { try { - mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder - .withView(InteractionJankMonitor.CUJ_USER_SWITCH, mView) - .setTimeout(MULTI_USER_JOURNEY_TIMEOUT)); + if (mView != null) { + mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder + .withView(InteractionJankMonitor.CUJ_USER_SWITCH, mView) + .setTimeout(MULTI_USER_JOURNEY_TIMEOUT)); + } mLatencyTracker.onActionStart(LatencyTracker.ACTION_USER_SWITCH); pauseRefreshUsers(); mActivityManager.switchUser(id); @@ -936,15 +938,17 @@ public class UserSwitcherController implements Dumpable { guestCreationProgressDialog.show(); // userManager.createGuest will block the thread so post is needed for the dialog to show - ThreadUtils.postOnMainThread(() -> { + mBgExecutor.execute(() -> { final int guestId = createGuest(); - guestCreationProgressDialog.dismiss(); - if (guestId == UserHandle.USER_NULL) { - Toast.makeText(mContext, - com.android.settingslib.R.string.add_guest_failed, - Toast.LENGTH_SHORT).show(); - } - callback.accept(guestId); + mUiExecutor.execute(() -> { + guestCreationProgressDialog.dismiss(); + if (guestId == UserHandle.USER_NULL) { + Toast.makeText(mContext, + com.android.settingslib.R.string.add_guest_failed, + Toast.LENGTH_SHORT).show(); + } + callback.accept(guestId); + }); }); } diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java deleted file mode 100644 index 0686071c1718..000000000000 --- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.user; - -import android.app.Dialog; -import android.content.Context; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.UserManager; - -import com.android.internal.util.UserIcons; -import com.android.settingslib.users.UserCreatingDialog; -import com.android.settingslib.utils.ThreadUtils; - -import java.util.function.Consumer; - -import javax.inject.Inject; - -/** - * A class to do the user creation process. It shows a progress dialog, and manages the user - * creation - */ -public class UserCreator { - - private final Context mContext; - private final UserManager mUserManager; - - @Inject - public UserCreator(Context context, UserManager userManager) { - mContext = context; - mUserManager = userManager; - } - - /** - * Shows a progress dialog then starts the user creation process on the main thread. - * - * @param successCallback is called when the user creation is successful. - * @param errorCallback is called when userManager.createUser returns null. - * (Exceptions are not handled by this class) - */ - public void createUser(String userName, Drawable userIcon, Consumer<UserInfo> successCallback, - Runnable errorCallback) { - - Dialog userCreationProgressDialog = new UserCreatingDialog(mContext); - userCreationProgressDialog.show(); - - // userManager.createUser will block the thread so post is needed for the dialog to show - ThreadUtils.postOnMainThread(() -> { - UserInfo user = - mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0); - if (user == null) { - // Couldn't create user for some reason - userCreationProgressDialog.dismiss(); - errorCallback.run(); - return; - } - - Drawable newUserIcon = userIcon; - Resources res = mContext.getResources(); - if (newUserIcon == null) { - newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false); - } - mUserManager.setUserIcon( - user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon)); - - userCreationProgressDialog.dismiss(); - successCallback.accept(user); - }); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt new file mode 100644 index 000000000000..dcbbe7441922 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.user + +import android.app.Dialog +import android.content.Context +import android.content.pm.UserInfo +import android.graphics.drawable.Drawable +import android.os.UserManager +import com.android.internal.util.UserIcons +import com.android.settingslib.users.UserCreatingDialog +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import java.util.concurrent.Executor +import java.util.function.Consumer +import javax.inject.Inject + +/** + * A class to do the user creation process. It shows a progress dialog, and manages the user + * creation + */ +class UserCreator @Inject constructor( + private val context: Context, + private val userManager: UserManager, + @Main private val mainExecutor: Executor, + @Background private val bgExecutor: Executor +) { + /** + * Shows a progress dialog then starts the user creation process on the main thread. + * + * @param successCallback is called when the user creation is successful. + * @param errorCallback is called when userManager.createUser returns null. + * (Exceptions are not handled by this class) + */ + fun createUser( + userName: String?, + userIcon: Drawable?, + successCallback: Consumer<UserInfo?>, + errorCallback: Runnable + ) { + val userCreationProgressDialog: Dialog = UserCreatingDialog(context) + userCreationProgressDialog.show() + + // userManager.createUser will block the thread so post is needed for the dialog to show + bgExecutor.execute { + val user = userManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0) + mainExecutor.execute main@{ + if (user == null) { + // Couldn't create user for some reason + userCreationProgressDialog.dismiss() + errorCallback.run() + return@main + } + bgExecutor.execute { + var newUserIcon = userIcon + val res = context.resources + if (newUserIcon == null) { + newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false) + } + userManager.setUserIcon( + user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon)) + } + userCreationProgressDialog.dismiss() + successCallback.accept(user) + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index be14cc51ef96..07c8af953d1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.colorextraction.SysuiColorExtractor import com.android.systemui.demomode.DemoModeController import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.qs.carrier.QSCarrierGroup import com.android.systemui.qs.carrier.QSCarrierGroupController import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider @@ -45,10 +46,10 @@ import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock +import org.mockito.Mockito.`when` import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -161,6 +162,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Test fun testRSSISlot_notCombined() { + `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false) controller.init() val captor = argumentCaptor<List<String>>() @@ -172,6 +174,20 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { } @Test + fun testRSSISlot_combined() { + `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true) + controller.init() + + val captor = argumentCaptor<List<String>>() + verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean()) + + assertThat(captor.value).containsExactly( + mContext.getString(com.android.internal.R.string.status_bar_no_calling), + mContext.getString(com.android.internal.R.string.status_bar_call_strength) + ) + } + + @Test fun testSingleCarrierCallback() { controller.init() reset(view) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java index 1963e30e741e..bd794d6813ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java @@ -37,6 +37,7 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; import com.android.keyguard.CarrierTextManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; @@ -79,6 +80,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { @Mock private QSCarrier mQSCarrier3; private TestableLooper mTestableLooper; + @Mock private FeatureFlags mFeatureFlags; @Mock private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener; @@ -118,7 +120,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mQSCarrierGroupController = new QSCarrierGroupController.Builder( mActivityStarter, handler, TestableLooper.get(this).getLooper(), mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker, - mSlotIndexResolver) + mFeatureFlags, mSlotIndexResolver) .setQSCarrierGroup(mQSCarrierGroup) .build(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java index 99a17a613041..5212255078fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java @@ -22,11 +22,13 @@ import static org.junit.Assert.assertTrue; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.FeatureFlagUtils; import android.view.LayoutInflater; import android.view.View; import androidx.test.filters.SmallTest; +import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.mobile.TelephonyIcons; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -57,14 +59,14 @@ public class QSCarrierTest extends SysuiTestCase { @Test public void testUpdateState_first() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); assertTrue(mQSCarrier.updateState(c, false)); } @Test public void testUpdateState_same() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); assertTrue(mQSCarrier.updateState(c, false)); assertFalse(mQSCarrier.updateState(c, false)); @@ -72,7 +74,7 @@ public class QSCarrierTest extends SysuiTestCase { @Test public void testUpdateState_changed() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); assertTrue(mQSCarrier.updateState(c, false)); @@ -83,14 +85,14 @@ public class QSCarrierTest extends SysuiTestCase { @Test public void testUpdateState_singleCarrier_first() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); assertTrue(mQSCarrier.updateState(c, true)); } @Test public void testUpdateState_singleCarrier_noShowIcon() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); mQSCarrier.updateState(c, true); @@ -99,7 +101,7 @@ public class QSCarrierTest extends SysuiTestCase { @Test public void testUpdateState_multiCarrier_showIcon() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); mQSCarrier.updateState(c, false); @@ -108,7 +110,7 @@ public class QSCarrierTest extends SysuiTestCase { @Test public void testUpdateState_changeSingleMultiSingle() { - CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false); + CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false); mQSCarrier.updateState(c, true); assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt index 73226fa0b12c..6d9b01e28aa4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -62,6 +63,14 @@ class UserFileManagerImplTest : SysuiTestCase() { broadcastDispatcher, backgroundExecutor) } + @After + fun end() { + val dir = Environment.buildPath( + context.filesDir, + UserFileManagerImpl.ID) + dir.deleteRecursively() + } + @Test fun testGetFile() { assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path) @@ -72,8 +81,19 @@ class UserFileManagerImplTest : SysuiTestCase() { @Test fun testGetSharedPreferences() { + val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11) + val secondaryUserDir = Environment.buildPath( + context.filesDir, + UserFileManagerImpl.ID, + "11", + UserFileManagerImpl.SHARED_PREFS, + TEST_FILE_NAME + ) + + assertThat(secondarySharedPref).isNotNull() + assertThat(secondaryUserDir.exists()) assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0)) - .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)) + .isNotEqualTo(secondarySharedPref) } @Test @@ -115,6 +135,19 @@ class UserFileManagerImplTest : SysuiTestCase() { verify(userManager).aliveUsers assertThat(secondaryUserDir.exists()).isFalse() assertThat(file.exists()).isFalse() - dir.deleteRecursively() + } + + @Test + fun testEnsureParentDirExists() { + val file = Environment.buildPath( + context.filesDir, + UserFileManagerImpl.ID, + "11", + "files", + TEST_FILE_NAME + ) + assertThat(file.parentFile.exists()).isFalse() + userFileManager.ensureParentDirExists(file) + assertThat(file.parentFile.exists()).isTrue() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java index f8a0d2fc415c..0d1879cb2593 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java @@ -70,6 +70,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.log.LogBuffer; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; @@ -125,8 +127,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected CarrierConfigTracker mCarrierConfigTracker; protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); protected Handler mMainHandler; + protected FeatureFlags mFeatureFlags; protected WifiStatusTrackerFactory mWifiStatusTrackerFactory; - protected MobileSignalControllerFactory mMobileFactory; protected int mSubId; @@ -156,6 +158,9 @@ public class NetworkControllerBaseTest extends SysuiTestCase { @Before public void setUp() throws Exception { + mFeatureFlags = mock(FeatureFlags.class); + when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0); TestableResources res = mContext.getOrCreateTestableResources(); @@ -219,11 +224,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mWifiStatusTrackerFactory = new WifiStatusTrackerFactory( mContext, mMockWm, mMockNsm, mMockCm, mMainHandler); - mMobileFactory = new MobileSignalControllerFactory( - mContext, - mCallbackHandler, - mCarrierConfigTracker - ); mNetworkController = new NetworkControllerImpl(mContext, mMockCm, @@ -243,8 +243,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mDemoModeController, mCarrierConfigTracker, mWifiStatusTrackerFactory, - mMobileFactory, mMainHandler, + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class) ); @@ -438,6 +438,10 @@ public class NetworkControllerBaseTest extends SysuiTestCase { updateSignalStrength(); } + public void setImsType(int imsType) { + mMobileSignalController.setImsType(imsType); + } + public void setIsGsm(boolean gsm) { when(mSignalStrength.isGsm()).thenReturn(gsm); updateSignalStrength(); @@ -633,4 +637,5 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void assertDataNetworkNameEquals(String expected) { assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName()); } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java index ed8a3e16cdd1..e3dd6f4e6e40 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java @@ -145,8 +145,8 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { mDemoModeController, mock(CarrierConfigTracker.class), mWifiStatusTrackerFactory, - mMobileFactory, new Handler(TestableLooper.get(this).getLooper()), + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class)); setupNetworkController(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java index a76676e01c15..698899a8fc36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java @@ -85,8 +85,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mDemoModeController, mCarrierConfigTracker, mWifiStatusTrackerFactory, - mMobileFactory, mMainHandler, + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class) ); @@ -121,8 +121,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mDemoModeController, mCarrierConfigTracker, mWifiStatusTrackerFactory, - mMobileFactory, mMainHandler, + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class)); TestableLooper.get(this).processAllMessages(); @@ -155,8 +155,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mDemoModeController, mock(CarrierConfigTracker.class), mWifiStatusTrackerFactory, - mMobileFactory, mMainHandler, + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class)); setupNetworkController(); @@ -192,8 +192,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mDemoModeController, mock(CarrierConfigTracker.class), mWifiStatusTrackerFactory, - mMobileFactory, mMainHandler, + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class)); mNetworkController.registerListeners(); @@ -277,8 +277,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { mDemoModeController, mock(CarrierConfigTracker.class), mWifiStatusTrackerFactory, - mMobileFactory, mMainHandler, + mFeatureFlags, mock(DumpManager.class), mock(LogBuffer.class)); setupNetworkController(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java index 68170ea4b518..3f7149159247 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java @@ -30,6 +30,7 @@ import android.net.NetworkInfo; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.telephony.CellSignalStrength; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -284,6 +285,44 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false); } + @Test + public void testCallStrengh() { + if (true) return; + String testSsid = "Test SSID"; + setWifiEnabled(true); + setWifiState(true, testSsid); + // Set the ImsType to be IMS_TYPE_WLAN + setImsType(2); + setWifiLevel(1); + for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) { + setWifiLevel(testLevel); + verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]); + } + // Set the ImsType to be IMS_TYPE_WWAN + setImsType(1); + setupDefaultSignal(); + for (int testStrength = 0; + testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) { + setLevel(testStrength); + verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]); + } + } + + @Test + public void testNonPrimaryWiFi() { + if (true) return; + String testSsid = "Test SSID"; + setWifiEnabled(true); + setWifiState(true, testSsid); + // Set the ImsType to be IMS_TYPE_WLAN + setImsType(2); + setWifiLevel(1); + verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]); + when(mWifiInfo.isPrimary()).thenReturn(false); + setWifiLevel(3); + verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]); + } + protected void setWifiActivity(int activity) { // TODO: Not this, because this variable probably isn't sticking around. mNetworkController.mWifiSignalController.setActivity(activity); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index 09d7c03c2091..359a780d2a94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -77,6 +77,7 @@ import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -269,6 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userManager.createGuest(any())).thenReturn(guestInfo) userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null) + bgExecutor.runAllReady() + uiExecutor.runAllReady() testableLooper.processAllMessages() verify(interactionJankMonitor).begin(any()) verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH) @@ -294,6 +297,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userManager.createGuest(any())).thenReturn(guestInfo) userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower) + bgExecutor.runAllReady() + uiExecutor.runAllReady() testableLooper.processAllMessages() verify(dialogShower).dismiss() } @@ -584,4 +589,24 @@ class UserSwitcherControllerTest : SysuiTestCase() { broadcastReceiverCaptor.value.onReceive(context, intent) verify(cb).onUserSwitched() } + + @Test + fun onUserItemClicked_guest_runsOnBgThread() { + val dialogShower = mock(UserSwitchDialogController.DialogShower::class.java) + val guestUserRecord = UserSwitcherController.UserRecord( + null, + picture, + true /* guest */, + false /* current */, + false /* isAddUser */, + false /* isRestricted */, + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) + + userSwitcherController.onUserListItemClicked(guestUserRecord, dialogShower) + assertTrue(bgExecutor.numPending() > 0) + verify(userManager, never()).createGuest(context) + bgExecutor.runAllReady() + verify(userManager).createGuest(context) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt new file mode 100644 index 000000000000..a85ae7df546b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt @@ -0,0 +1,73 @@ +package com.android.systemui.user + +import android.content.pm.UserInfo +import android.graphics.Bitmap +import android.os.UserManager +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock +import java.util.function.Consumer +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class UserCreatorTest : SysuiTestCase() { + companion object { + const val USER_NAME = "abc" + } + + @Mock + private lateinit var userCreator: UserCreator + @Mock + private lateinit var userManager: UserManager + private lateinit var mainExecutor: FakeExecutor + private lateinit var bgExecutor: FakeExecutor + private lateinit var user: UserInfo + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + mainExecutor = FakeExecutor(FakeSystemClock()) + bgExecutor = FakeExecutor(FakeSystemClock()) + userCreator = UserCreator(context, userManager, mainExecutor, bgExecutor) + user = Mockito.mock(UserInfo::class.java) + Mockito.`when`(userManager.createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)) + .thenReturn(user) + } + + @Test + fun testCreateUser_threadingOrder() { + val successCallback = Mockito.mock(Consumer::class.java) + val errorCallback = Mockito.mock(Runnable::class.java) + + userCreator.createUser( + USER_NAME, + null, + successCallback as Consumer<UserInfo?>, + errorCallback) + + verify(userManager, never()).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0) + bgExecutor.runAllReady() + verify(successCallback, never()).accept(user) + mainExecutor.runAllReady() + verify(userManager, never()).setUserIcon(anyInt(), any(Bitmap::class.java)) + bgExecutor.runAllReady() + + verify(userManager).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0) + verify(userManager).setUserIcon(anyInt(), any(Bitmap::class.java)) + verify(successCallback).accept(user) + } +} diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 73d9cc759b10..d29e25c3faff 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -862,10 +862,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return list; } + @android.annotation.EnforcePermission(android.Manifest.permission.SHUTDOWN) @Override public void shutdown() { // TODO: remove from aidl if nobody calls externally - mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG); Slog.i(TAG, "Shutting down"); } @@ -1203,9 +1203,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { setUidOnMeteredNetworkList(uid, true, enable); } + @android.annotation.EnforcePermission(android.Manifest.permission.NETWORK_SETTINGS) @Override public boolean setDataSaverModeEnabled(boolean enable) { - mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG); if (DBG) Log.d(TAG, "setDataSaverMode: " + enable); synchronized (mQuotaLock) { @@ -1741,9 +1741,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return NetdUtils.removeRoutesFromLocalNetwork(mNetdService, routes); } + @android.annotation.EnforcePermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) @Override public boolean isNetworkRestricted(int uid) { - mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); return isNetworkRestrictedInternal(uid); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c5b5fb941e05..002aa773d85a 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -4413,7 +4413,7 @@ public final class ActiveServices { mAm.notifyPackageUse(r.serviceInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_SERVICE); thread.scheduleCreateService(r, r.serviceInfo, - mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo), + null /* compatInfo (unused but need to keep method signature) */, app.mState.getReportedProcState()); r.postNotification(); created = true; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9f0aeec40bd7..5729a06830cf 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4980,7 +4980,6 @@ public class ActivityManagerService extends IActivityManager.Stub PackageManager.NOTIFY_PACKAGE_USE_BACKUP); try { thread.scheduleCreateBackupAgent(backupTarget.appInfo, - compatibilityInfoForPackage(backupTarget.appInfo), backupTarget.backupMode, backupTarget.userId, backupTarget.operationType); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e); @@ -12900,8 +12899,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (thread != null) { if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc); try { - thread.scheduleCreateBackupAgent(app, - compatibilityInfoForPackage(app), backupMode, targetUserId, + thread.scheduleCreateBackupAgent(app, backupMode, targetUserId, operationType); } catch (RemoteException e) { // Will time out on the backup manager side @@ -13027,8 +13025,7 @@ public class ActivityManagerService extends IActivityManager.Stub final IApplicationThread thread = proc.getThread(); if (thread != null) { try { - thread.scheduleDestroyBackupAgent(appInfo, - compatibilityInfoForPackage(appInfo), userId); + thread.scheduleDestroyBackupAgent(appInfo, userId); } catch (Exception e) { Slog.e(TAG, "Exception when unbinding backup agent:"); e.printStackTrace(); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 0cd1bfa3740a..6d520c3c6be8 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -361,7 +361,7 @@ public final class BroadcastQueue { mService.notifyPackageUse(r.intent.getComponent().getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER); thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, - mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo), + null /* compatInfo (unused but need to keep method signature) */, r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.mState.getReportedProcState()); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index 7371d07183a9..33e407025ae6 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -564,10 +564,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { return res; } + @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS) @Override public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) { - mAm.mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); synchronized (mLock) { long now = SystemClock.uptimeMillis(); @@ -619,11 +618,10 @@ public final class ProcessStatsService extends IProcessStats.Stub { * @return List of proto binary of individual commit files or one that is merged from them; * the merged, final ProcessStats object. */ + @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS) @Override public long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate, List<ParcelFileDescriptor> committedStats, ProcessStats mergedStats) { - mAm.mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); long newHighWaterMark = highWaterMarkMs; mFileLock.lock(); @@ -708,10 +706,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { return fds[0]; } + @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS) @Override public ParcelFileDescriptor getStatsOverTime(long minTime) { - mAm.mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); long curTime; synchronized (mLock) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 18d5194e8bc0..e40925605899 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3904,6 +3904,36 @@ public class AudioService extends IAudioService.Stub } } + private void setLeAudioVolumeOnModeUpdate(int mode) { + switch (mode) { + case AudioSystem.MODE_IN_COMMUNICATION: + case AudioSystem.MODE_IN_CALL: + case AudioSystem.MODE_NORMAL: + break; + case AudioSystem.MODE_RINGTONE: + // not changing anything for ringtone + return; + case AudioSystem.MODE_CURRENT: + case AudioSystem.MODE_INVALID: + default: + // don't know what to do in this case, better bail + return; + } + + int streamType = getBluetoothContextualVolumeStream(mode); + + // Currently, DEVICE_OUT_BLE_HEADSET is the only output type for LE_AUDIO profile. + // (See AudioDeviceBroker#createBtDeviceInfo()) + int index = mStreamStates[streamType].getIndex(AudioSystem.DEVICE_OUT_BLE_HEADSET); + int maxIndex = mStreamStates[streamType].getMaxIndex(); + + if (DEBUG_VOL) { + Log.d(TAG, "setLeAudioVolumeOnModeUpdate postSetLeAudioVolumeIndex index=" + + index + " maxIndex=" + maxIndex + " streamType=" + streamType); + } + mDeviceBroker.postSetLeAudioVolumeIndex(index, maxIndex, streamType); + } + private void setStreamVolume(int streamType, int index, int flags, String callingPackage, String caller, String attributionTag, int uid, boolean hasModifyAudioSettings) { @@ -5276,6 +5306,10 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(previousMode, mode); + // Forcefully set LE audio volume as a workaround, since the value of 'device' + // is not DEVICE_OUT_BLE_* even when BLE is connected. + setLeAudioVolumeOnModeUpdate(mode); + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO // connections not started by the application changing the mode when pid changes mDeviceBroker.postSetModeOwnerPid(pid, mode); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index e37a8e031c57..5e20dfbc7144 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -412,7 +412,7 @@ final class InputMethodUtils { enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked( context.getResources(), imi); } - return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes); + return InputMethodSubtype.sort(imi, enabledSubtypes); } List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi) { diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java index 371ef76b1ba6..b06af8e5a385 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java @@ -17,6 +17,7 @@ package com.android.server.locksettings; import android.security.AndroidKeyStoreMaintenance; +import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; @@ -210,10 +211,26 @@ public class SyntheticPasswordCrypto { .setBoundToSpecificSecureUserId(sid) .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_VALIDITY); } + final KeyProtection protNonRollbackResistant = builder.build(); + builder.setRollbackResistant(true); + final KeyProtection protRollbackResistant = builder.build(); + final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(keyStoreKey); + try { + keyStore.setEntry(keyAlias, entry, protRollbackResistant); + Slog.i(TAG, "Using rollback-resistant key"); + } catch (KeyStoreException e) { + if (!(e.getCause() instanceof android.security.KeyStoreException)) { + throw e; + } + int errorCode = ((android.security.KeyStoreException) e.getCause()).getErrorCode(); + if (errorCode != KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE) { + throw e; + } + Slog.w(TAG, "Rollback-resistant keys unavailable. Falling back to " + + "non-rollback-resistant key"); + keyStore.setEntry(keyAlias, entry, protNonRollbackResistant); + } - keyStore.setEntry(keyAlias, - new KeyStore.SecretKeyEntry(keyStoreKey), - builder.build()); byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data); return encrypt(keyStoreKey, intermediate); } catch (CertificateException | IOException | BadPaddingException diff --git a/services/core/java/com/android/server/pm/ApexPackageInfo.java b/services/core/java/com/android/server/pm/ApexPackageInfo.java index f959a52f0374..73cb0ada6b87 100644 --- a/services/core/java/com/android/server/pm/ApexPackageInfo.java +++ b/services/core/java/com/android/server/pm/ApexPackageInfo.java @@ -22,10 +22,9 @@ import static com.android.server.pm.ApexManager.MATCH_FACTORY_PACKAGE; import android.annotation.NonNull; import android.annotation.Nullable; import android.apex.ApexInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.ArrayMap; +import android.util.Pair; import android.util.PrintWriterPrinter; import com.android.internal.annotations.GuardedBy; @@ -33,8 +32,9 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; +import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; @@ -59,16 +59,17 @@ class ApexPackageInfo { private final Object mLock = new Object(); @GuardedBy("mLock") - private List<PackageInfo> mAllPackagesCache; + private List<Pair<ApexInfo, AndroidPackage>> mAllPackagesCache; - /** - * Whether an APEX package is active or not. - * - * @param packageInfo the package to check - * @return {@code true} if this package is active, {@code false} otherwise. - */ - private static boolean isActive(PackageInfo packageInfo) { - return packageInfo.isActiveApex; + @Nullable + private final PackageManagerService mPackageManager; + + ApexPackageInfo() { + mPackageManager = null; + } + + ApexPackageInfo(@NonNull PackageManagerService pms) { + mPackageManager = pms; } /** @@ -105,20 +106,23 @@ class ApexPackageInfo { * is not found. */ @Nullable - PackageInfo getPackageInfo(String packageName, @ApexManager.PackageInfoFlags int flags) { + Pair<ApexInfo, AndroidPackage> getPackageInfo(String packageName, + @ApexManager.PackageInfoFlags int flags) { synchronized (mLock) { Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { - final PackageInfo packageInfo = mAllPackagesCache.get(i); - if (!packageInfo.packageName.equals(packageName)) { + final Pair<ApexInfo, AndroidPackage> pair = mAllPackagesCache.get(i); + var apexInfo = pair.first; + var pkg = pair.second; + if (!pkg.getPackageName().equals(packageName)) { continue; } - if ((matchActive && isActive(packageInfo)) - || (matchFactory && isFactory(packageInfo))) { - return packageInfo; + if ((matchActive && apexInfo.isActive) + || (matchFactory && apexInfo.isFactory)) { + return pair; } } return null; @@ -128,18 +132,18 @@ class ApexPackageInfo { /** * Retrieves information about all active APEX packages. * - * @return a List of PackageInfo object, each one containing information about a different - * active package. + * @return list containing information about different active packages. */ - List<PackageInfo> getActivePackages() { + @NonNull + List<Pair<ApexInfo, AndroidPackage>> getActivePackages() { synchronized (mLock) { Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); - final List<PackageInfo> activePackages = new ArrayList<>(); + final List<Pair<ApexInfo, AndroidPackage>> activePackages = new ArrayList<>(); for (int i = 0; i < mAllPackagesCache.size(); i++) { - final PackageInfo packageInfo = mAllPackagesCache.get(i); - if (isActive(packageInfo)) { - activePackages.add(packageInfo); + final var pair = mAllPackagesCache.get(i); + if (pair.first.isActive) { + activePackages.add(pair); } } return activePackages; @@ -147,20 +151,20 @@ class ApexPackageInfo { } /** - * Retrieves information about all active pre-installed APEX packages. + * Retrieves information about all pre-installed APEX packages. * - * @return a List of PackageInfo object, each one containing information about a different - * active pre-installed package. + * @return list containing information about different pre-installed packages. */ - List<PackageInfo> getFactoryPackages() { + @NonNull + List<Pair<ApexInfo, AndroidPackage>> getFactoryPackages() { synchronized (mLock) { Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); - final List<PackageInfo> factoryPackages = new ArrayList<>(); + final List<Pair<ApexInfo, AndroidPackage>> factoryPackages = new ArrayList<>(); for (int i = 0; i < mAllPackagesCache.size(); i++) { - final PackageInfo packageInfo = mAllPackagesCache.get(i); - if (isFactory(packageInfo)) { - factoryPackages.add(packageInfo); + final var pair = mAllPackagesCache.get(i); + if (pair.first.isFactory) { + factoryPackages.add(pair); } } return factoryPackages; @@ -170,18 +174,18 @@ class ApexPackageInfo { /** * Retrieves information about all inactive APEX packages. * - * @return a List of PackageInfo object, each one containing information about a different - * inactive package. + * @return list containing information about different inactive packages. */ - List<PackageInfo> getInactivePackages() { + @NonNull + List<Pair<ApexInfo, AndroidPackage>> getInactivePackages() { synchronized (mLock) { Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); - final List<PackageInfo> inactivePackages = new ArrayList<>(); + final List<Pair<ApexInfo, AndroidPackage>> inactivePackages = new ArrayList<>(); for (int i = 0; i < mAllPackagesCache.size(); i++) { - final PackageInfo packageInfo = mAllPackagesCache.get(i); - if (!isActive(packageInfo)) { - inactivePackages.add(packageInfo); + final var pair = mAllPackagesCache.get(i); + if (!pair.first.isActive) { + inactivePackages.add(pair); } } return inactivePackages; @@ -199,8 +203,8 @@ class ApexPackageInfo { Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { - final PackageInfo packageInfo = mAllPackagesCache.get(i); - if (packageInfo.packageName.equals(packageName)) { + final var pair = mAllPackagesCache.get(i); + if (pair.second.getPackageName().equals(packageName)) { return true; } } @@ -222,21 +226,18 @@ class ApexPackageInfo { } void notifyPackageInstalled(ApexInfo apexInfo, AndroidPackage pkg) { - final int flags = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_SIGNATURES; - final PackageInfo newApexPkg = PackageInfoWithoutStateUtils.generate( - pkg, apexInfo, flags); - final String packageName = newApexPkg.packageName; + final String packageName = pkg.getPackageName(); synchronized (mLock) { for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { - PackageInfo oldApexPkg = mAllPackagesCache.get(i); - if (oldApexPkg.isActiveApex && oldApexPkg.packageName.equals(packageName)) { - if (isFactory(oldApexPkg)) { - oldApexPkg.isActiveApex = false; - mAllPackagesCache.add(newApexPkg); + var pair = mAllPackagesCache.get(i); + var oldApexInfo = pair.first; + var oldApexPkg = pair.second; + if (oldApexInfo.isActive && oldApexPkg.getPackageName().equals(packageName)) { + if (oldApexInfo.isFactory) { + oldApexInfo.isActive = false; + mAllPackagesCache.add(Pair.create(apexInfo, pkg)); } else { - mAllPackagesCache.set(i, newApexPkg); + mAllPackagesCache.set(i, Pair.create(apexInfo, pkg)); } break; } @@ -245,16 +246,6 @@ class ApexPackageInfo { } /** - * Whether the APEX package is pre-installed or not. - * - * @param packageInfo the package to check - * @return {@code true} if this package is pre-installed, {@code false} otherwise. - */ - private static boolean isFactory(@NonNull PackageInfo packageInfo) { - return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0; - } - - /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -288,33 +279,25 @@ class ApexPackageInfo { HashSet<String> factoryPackagesSet = new HashSet<>(); for (ApexManager.ScanResult result : scanResults) { ApexInfo ai = result.apexInfo; - - final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate( - result.pkg, ai, flags); - if (packageInfo == null) { - throw new IllegalStateException("Unable to generate package info: " - + ai.modulePath); - } - if (!packageInfo.packageName.equals(result.packageName)) { + String packageName = result.pkg.getPackageName(); + if (!packageName.equals(result.packageName)) { throw new IllegalStateException("Unmatched package name: " - + result.packageName + " != " + packageInfo.packageName + + result.packageName + " != " + packageName + ", path=" + ai.modulePath); } - mAllPackagesCache.add(packageInfo); + mAllPackagesCache.add(Pair.create(ai, result.pkg)); if (ai.isActive) { - if (!activePackagesSet.add(packageInfo.packageName)) { + if (!activePackagesSet.add(packageName)) { throw new IllegalStateException( - "Two active packages have the same name: " - + packageInfo.packageName); + "Two active packages have the same name: " + packageName); } } if (ai.isFactory) { // Don't throw when the duplicating APEX is VNDK APEX - if (!factoryPackagesSet.add(packageInfo.packageName) + if (!factoryPackagesSet.add(packageName) && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) { throw new IllegalStateException( - "Two factory packages have the same name: " - + packageInfo.packageName); + "Two factory packages have the same name: " + packageName); } } } @@ -348,6 +331,13 @@ class ApexPackageInfo { if (throwable == null) { // Calling hideAsFinal to assign derived fields for the app info flags. parseResult.parsedPackage.hideAsFinal(); + + // TODO: When ENABLE_FEATURE_SCAN_APEX is finalized, remove this and the entire + // calling path code + ScanPackageUtils.applyPolicy(parseResult.parsedPackage, + PackageManagerService.SCAN_AS_SYSTEM, + mPackageManager == null ? null : mPackageManager.getPlatformPackage(), + false); results.add(new ApexManager.ScanResult( ai, parseResult.parsedPackage, parseResult.parsedPackage.getPackageName())); } else if (throwable instanceof PackageManagerException) { @@ -363,34 +353,69 @@ class ApexPackageInfo { } /** + * @see #dumpPackages(List, String, IndentingPrintWriter) + */ + static void dumpPackageStates(List<PackageStateInternal> packageStates, boolean isActive, + @Nullable String packageName, IndentingPrintWriter ipw) { + ipw.println(); + ipw.increaseIndent(); + for (int i = 0, size = packageStates.size(); i < size; i++) { + final var packageState = packageStates.get(i); + var pkg = packageState.getPkg(); + if (packageName != null && !packageName.equals(pkg.getPackageName())) { + continue; + } + ipw.println(pkg.getPackageName()); + ipw.increaseIndent(); + ipw.println("Version: " + pkg.getLongVersionCode()); + ipw.println("Path: " + pkg.getBaseApkPath()); + ipw.println("IsActive: " + isActive); + ipw.println("IsFactory: " + !packageState.isUpdatedSystemApp()); + ipw.println("ApplicationInfo: "); + ipw.increaseIndent(); + // TODO: Dump the package manually + AndroidPackageUtils.generateAppInfoWithoutState(pkg) + .dump(new PrintWriterPrinter(ipw), ""); + ipw.decreaseIndent(); + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + ipw.println(); + } + + /** * Dump information about the packages contained in a particular cache * @param packagesCache the cache to print information about. * @param packageName a {@link String} containing a package name, or {@code null}. If set, * only information about that specific package will be dumped. * @param ipw the {@link IndentingPrintWriter} object to send information to. */ - static void dumpPackages(List<PackageInfo> packagesCache, + static void dumpPackages(List<Pair<ApexInfo, AndroidPackage>> packagesCache, @Nullable String packageName, IndentingPrintWriter ipw) { ipw.println(); ipw.increaseIndent(); for (int i = 0, size = packagesCache.size(); i < size; i++) { - final PackageInfo pi = packagesCache.get(i); - if (packageName != null && !packageName.equals(pi.packageName)) { + final var pair = packagesCache.get(i); + var apexInfo = pair.first; + var pkg = pair.second; + if (packageName != null && !packageName.equals(pkg.getPackageName())) { continue; } - ipw.println(pi.packageName); + ipw.println(pkg.getPackageName()); ipw.increaseIndent(); - ipw.println("Version: " + pi.versionCode); - ipw.println("Path: " + pi.applicationInfo.sourceDir); - ipw.println("IsActive: " + isActive(pi)); - ipw.println("IsFactory: " + isFactory(pi)); + ipw.println("Version: " + pkg.getLongVersionCode()); + ipw.println("Path: " + pkg.getBaseApkPath()); + ipw.println("IsActive: " + apexInfo.isActive); + ipw.println("IsFactory: " + apexInfo.isFactory); ipw.println("ApplicationInfo: "); ipw.increaseIndent(); - pi.applicationInfo.dump(new PrintWriterPrinter(ipw), ""); + // TODO: Dump the package manually + AndroidPackageUtils.generateAppInfoWithoutState(pkg) + .dump(new PrintWriterPrinter(ipw), ""); ipw.decreaseIndent(); ipw.decreaseIndent(); } ipw.decreaseIndent(); ipw.println(); } -} +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 63c25ea520f7..8ec3d2bc74ca 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -65,6 +65,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.apex.ApexInfo; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -1018,11 +1019,12 @@ public class ComputerEngine implements Computer { if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { apexFlags = ApexManager.MATCH_FACTORY_PACKAGE; } - final PackageInfo pi = mApexPackageInfo.getPackageInfo(packageName, apexFlags); - if (pi == null) { + final var pair = mApexPackageInfo.getPackageInfo(packageName, apexFlags); + if (pair == null) { return null; } - return pi.applicationInfo; + return PackageInfoUtils.generateApplicationInfo(pair.second, flags, + PackageUserStateInternal.DEFAULT, userId, null); } } if ("android".equals(packageName) || "system".equals(packageName)) { @@ -1718,8 +1720,12 @@ public class ComputerEngine implements Computer { // Instant app filtering for APEX modules is ignored if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { if (matchApex) { - return mApexPackageInfo.getPackageInfo(packageName, + final var pair = mApexPackageInfo.getPackageInfo(packageName, ApexManager.MATCH_FACTORY_PACKAGE); + if (pair == null) { + return null; + } + return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId); } } final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName); @@ -1775,8 +1781,12 @@ public class ComputerEngine implements Computer { } if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { if (matchApex) { - return mApexPackageInfo.getPackageInfo(packageName, + final var pair = mApexPackageInfo.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE); + if (pair == null) { + return null; + } + return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId); } } return null; @@ -1883,10 +1893,17 @@ public class ComputerEngine implements Computer { } if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { if (listApex) { + List<Pair<ApexInfo, AndroidPackage>> pairs; if (listFactory) { - list.addAll(mApexPackageInfo.getFactoryPackages()); + pairs = mApexPackageInfo.getFactoryPackages(); } else { - list.addAll(mApexPackageInfo.getActivePackages()); + pairs = mApexPackageInfo.getActivePackages(); + } + + for (int index = 0; index < pairs.size(); index++) { + var pair = pairs.get(index); + list.add(PackageInfoUtils.generate(pair.second, pair.first, flags, null, + userId)); } } } @@ -3404,29 +3421,23 @@ public class ComputerEngine implements Computer { } // switch } - private void generateApexPackageInfo(List<PackageInfo> activePackages, - List<PackageInfo> inactivePackages, List<PackageInfo> factoryPackages) { + private void generateApexPackageInfo(@NonNull List<PackageStateInternal> activePackages, + @NonNull List<PackageStateInternal> inactivePackages, + @NonNull List<PackageStateInternal> factoryActivePackages, + @NonNull List<PackageStateInternal> factoryInactivePackages) { for (AndroidPackage p : mPackages.values()) { final String packageName = p.getPackageName(); PackageStateInternal ps = mSettings.getPackage(packageName); if (!p.isApex() || ps == null) { continue; } - PackageInfo pi = generatePackageInfo(ps, 0, 0); - if (pi == null) { - continue; - } - pi.isActiveApex = true; - activePackages.add(pi); + activePackages.add(ps); if (!ps.isUpdatedSystemApp()) { - factoryPackages.add(pi); + factoryActivePackages.add(ps); } else { PackageStateInternal psDisabled = mSettings.getDisabledSystemPkg(packageName); - pi = generatePackageInfo(psDisabled, 0, 0); - if (pi != null) { - factoryPackages.add(pi); - inactivePackages.add(pi); - } + factoryInactivePackages.add(psDisabled); + inactivePackages.add(psDisabled); } } } @@ -3434,16 +3445,19 @@ public class ComputerEngine implements Computer { private void dumpApex(PrintWriter pw, String packageName) { if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - List<PackageInfo> activePackages = new ArrayList<>(); - List<PackageInfo> inactivePackages = new ArrayList<>(); - List<PackageInfo> factoryPackages = new ArrayList<>(); - generateApexPackageInfo(activePackages, inactivePackages, factoryPackages); + List<PackageStateInternal> activePackages = new ArrayList<>(); + List<PackageStateInternal> inactivePackages = new ArrayList<>(); + List<PackageStateInternal> factoryActivePackages = new ArrayList<>(); + List<PackageStateInternal> factoryInactivePackages = new ArrayList<>(); + generateApexPackageInfo(activePackages, inactivePackages, factoryActivePackages, + factoryInactivePackages); ipw.println("Active APEX packages:"); - ApexPackageInfo.dumpPackages(activePackages, packageName, ipw); + ApexPackageInfo.dumpPackageStates(activePackages, true, packageName, ipw); ipw.println("Inactive APEX packages:"); - ApexPackageInfo.dumpPackages(inactivePackages, packageName, ipw); + ApexPackageInfo.dumpPackageStates(inactivePackages, false, packageName, ipw); ipw.println("Factory APEX packages:"); - ApexPackageInfo.dumpPackages(factoryPackages, packageName, ipw); + ApexPackageInfo.dumpPackageStates(factoryActivePackages, true, packageName, ipw); + ApexPackageInfo.dumpPackageStates(factoryInactivePackages, false, packageName, ipw); } else { mApexPackageInfo.dump(pw, packageName); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2bdf62bdd707..32b3e6a4ab76 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1611,7 +1611,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mSharedLibraries = injector.getSharedLibrariesImpl(); mApexManager = testParams.apexManager; - mApexPackageInfo = new ApexPackageInfo(); + mApexPackageInfo = new ApexPackageInfo(this); mArtManagerService = testParams.artManagerService; mAvailableFeatures = testParams.availableFeatures; mBackgroundDexOptService = testParams.backgroundDexOptService; @@ -1811,7 +1811,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mProtectedPackages = new ProtectedPackages(mContext); mApexManager = injector.getApexManager(); - mApexPackageInfo = new ApexPackageInfo(); + mApexPackageInfo = new ApexPackageInfo(this); mAppsFilter = mInjector.getAppsFilter(); mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager, diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 0e57c917a72b..86affdd75481 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -823,8 +823,8 @@ final class ScanPackageUtils { * ideally be static, but, it requires locks to read system state. */ public static void applyPolicy(ParsedPackage parsedPackage, - final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg, - boolean isUpdatedSystemApp) { + final @PackageManagerService.ScanFlags int scanFlags, + @Nullable AndroidPackage platformPkg, boolean isUpdatedSystemApp) { if ((scanFlags & SCAN_AS_SYSTEM) != 0) { parsedPackage.setSystem(true); // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java index 937b2cf047a4..b57d4d55b6eb 100644 --- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java +++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java @@ -219,38 +219,21 @@ class UserSystemPackageInstaller { // Install/uninstall system packages per user. for (int userId : mUm.getUserIds()) { - final Set<String> userWhitelist = getInstallablePackagesForUserId(userId); - - // If null, run for all packages - if (userWhitelist == null) { - pmInt.forEachPackageState(packageState -> { - if (packageState.getPkg() == null) { - return; - } - final boolean install = !packageState.getTransientState() - .isHiddenUntilInstalled(); - if (packageState.getUserStateOrDefault(userId).isInstalled() != install - && shouldChangeInstallationState(packageState, install, userId, - isFirstBoot, isConsideredUpgrade, preExistingPackages)) { - changesToCommit.add(userId, packageState.getPackageName(), install); - } - }); - } else { - for (String packageName : userWhitelist) { - PackageStateInternal packageState = pmInt.getPackageStateInternal(packageName); - if (packageState.getPkg() == null) { - continue; - } - - final boolean install = !packageState.getTransientState() - .isHiddenUntilInstalled(); - if (packageState.getUserStateOrDefault(userId).isInstalled() != install - && shouldChangeInstallationState(packageState, install, userId, - isFirstBoot, isConsideredUpgrade, preExistingPackages)) { - changesToCommit.add(userId, packageState.getPackageName(), install); - } + final Set<String> userAllowlist = getInstallablePackagesForUserId(userId); + + pmInt.forEachPackageState(packageState -> { + if (packageState.getPkg() == null) { + return; } - } + boolean install = (userAllowlist == null + || userAllowlist.contains(packageState.getPackageName())) + && !packageState.getTransientState().isHiddenUntilInstalled(); + if (packageState.getUserStateOrDefault(userId).isInstalled() != install + && shouldChangeInstallationState(packageState, install, userId, + isFirstBoot, isConsideredUpgrade, preExistingPackages)) { + changesToCommit.add(userId, packageState.getPackageName(), install); + } + }); } pmInt.commitPackageStateMutation(null, packageStateMutator -> { diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 9c620c4ff3ab..a44def8f772a 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -93,12 +93,14 @@ public class PackageInfoUtils { /** * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. + * @deprecated Once ENABLE_FEATURE_SCAN_APEX is removed, this should also be removed. */ + @Deprecated @Nullable - public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, int flags, - @Nullable PackageStateInternal pkgSetting) { + public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, long flags, + @Nullable PackageStateInternal pkgSetting, @UserIdInt int userId) { return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(), - PackageUserStateInternal.DEFAULT, UserHandle.getCallingUserId(), apexInfo, pkgSetting); + PackageUserStateInternal.DEFAULT, userId, apexInfo, pkgSetting); } /** diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java index b91f15a4d037..748d32894d8a 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import com.android.server.pm.pkg.SELinuxUtil; import android.content.pm.SigningDetails; import android.content.res.TypedArray; import android.os.Environment; @@ -35,6 +34,7 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; import com.android.server.pm.parsing.PackageInfoUtils; +import com.android.server.pm.pkg.SELinuxUtil; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedProvider; @@ -255,7 +255,7 @@ public class PackageImpl extends ParsingPackageImpl implements ParsedPackage, An } @Override - public PackageImpl setSigningDetails(@Nullable SigningDetails value) { + public PackageImpl setSigningDetails(@NonNull SigningDetails value) { super.setSigningDetails(value); return this; } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java index 40f859c5e82e..b7b37b2630f7 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java @@ -334,7 +334,7 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setSharedUserLabel(int sharedUserLabel); - ParsingPackage setSigningDetails(SigningDetails signingDetails); + ParsingPackage setSigningDetails(@NonNull SigningDetails signingDetails); ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName); diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java index 6a4513d45d8f..803780fe2a9b 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java @@ -317,8 +317,8 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, @Nullable @DataClass.ParcelWith(ForInternedString.class) protected String volumeUuid; - @Nullable - private SigningDetails signingDetails; + @NonNull + private SigningDetails signingDetails = SigningDetails.UNKNOWN; @NonNull @DataClass.ParcelWith(ForInternedString.class) @@ -1873,7 +1873,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, return volumeUuid; } - @Nullable + @NonNull @Override public SigningDetails getSigningDetails() { return signingDetails; @@ -2474,7 +2474,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, } @Override - public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) { + public ParsingPackageImpl setSigningDetails(@NonNull SigningDetails value) { signingDetails = value; return this; } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java index 20b1ed8d0c3e..22729993af64 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java @@ -188,6 +188,7 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt * The signature data of all APKs in this package, which must be exactly the same across the * base and splits. */ + @NonNull SigningDetails getSigningDetails(); /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 69bbff3a4db1..42e0533f178f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -272,7 +272,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConstrainDisplayApisConfig; import android.content.pm.PackageManager; -import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; @@ -478,7 +477,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private int mLastReportedDisplayId; boolean mLastReportedMultiWindowMode; boolean mLastReportedPictureInPictureMode; - CompatibilityInfo compat;// last used compatibility mode ActivityRecord resultTo; // who started this entry, so will get our reply final String resultWho; // additional identifier for use by resultTo. final int requestCode; // code given by requester (resultTo) @@ -1022,7 +1020,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (rootVoiceInteraction) { pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction); } - pw.print(prefix); pw.print("compat="); pw.print(compat); + pw.print(prefix); pw.print("compat="); + pw.print(mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo)); pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes)); pw.print(" icon=0x"); pw.print(Integer.toHexString(icon)); pw.print(" theme=0x"); pw.println(Integer.toHexString(theme)); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 84740683f6aa..345ec117b03a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -875,7 +875,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY); r.forceNewConfig = false; mService.getAppWarningsLocked().onStartActivity(r); - r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo); // Because we could be starting an Activity in the system process this may not go // across a Binder interface which would create a new Configuration. Consequently @@ -905,7 +904,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // TODO: Have this take the merged configuration instead of separate global // and override configs. mergedConfiguration.getGlobalConfiguration(), - mergedConfiguration.getOverrideConfiguration(), r.compat, + mergedConfiguration.getOverrideConfiguration(), r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor, proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, r.takeOptions(), isTransitionForward, diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3ef4aaeffd0c..366ca3b25130 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -137,7 +137,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITIO import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; -import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.WINDOW; @@ -4957,10 +4956,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP || isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION); } - boolean isExitAnimationRunningSelfOrChild() { - return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION); - } - private boolean shouldFinishAnimatingExit() { // Exit animation might be applied soon. if (inTransition()) { @@ -5916,6 +5911,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (!super.prepareSync()) { return false; } + if (mIsWallpaper) { + // TODO(b/233286785): Add sync support to wallpaper. + return false; + } // In the WindowContainer implementation we immediately mark ready // since a generic WindowContainer only needs to wait for its // children to finish and is immediately ready from its own diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java index 721777ca6b38..13510adbb960 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java @@ -30,6 +30,7 @@ import android.content.pm.PackageInfo; import android.os.UserHandle; import android.util.Log; import android.util.SparseArrayMap; +import android.util.SparseLongArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -174,10 +175,13 @@ public class ScribeTest { when(mIrs.getConsumptionLimitLocked()).thenReturn(consumptionLimit); Ledger ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000, 0)); + ledger.recordTransaction( + new Ledger.Transaction(0, 1000L, EconomicPolicy.TYPE_REWARD | 1, null, 2000, 0)); // Negative ledger balance shouldn't affect the total circulation value. ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID + 1, TEST_PACKAGE); - ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000)); + ledger.recordTransaction( + new Ledger.Transaction(0, 1000L, + EconomicPolicy.TYPE_ACTION | 1, null, -5000, 3000)); mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime); mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit); mScribeUnderTest.adjustRemainingConsumableCakesLocked( @@ -209,9 +213,13 @@ public class ScribeTest { @Test public void testWritingPopulatedLedgerToDisk() { final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0)); - ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, -1)); - ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 12)); + ogLedger.recordTransaction( + new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 0)); + ogLedger.recordTransaction( + new Ledger.Transaction(1500, 2000, + EconomicPolicy.TYPE_REWARD | 2, "green", 52, -1)); + ogLedger.recordTransaction( + new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_REWARD | 3, "blue", 3, 12)); mScribeUnderTest.writeImmediatelyForTesting(); mScribeUnderTest.loadFromDiskLocked(); @@ -230,11 +238,13 @@ public class ScribeTest { addInstalledPackage(userId, pkgName); final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName); ledger.recordTransaction(new Ledger.Transaction( - 0, 1000L * u + l, 1, null, -51L * u + l, 50)); + 0, 1000L * u + l, EconomicPolicy.TYPE_ACTION | 1, null, -51L * u + l, 50)); ledger.recordTransaction(new Ledger.Transaction( - 1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l, 0)); + 1500L * u + l, 2000L * u + l, + EconomicPolicy.TYPE_REWARD | 2 * u + l, "green" + u + l, 52L * u + l, 0)); ledger.recordTransaction(new Ledger.Transaction( - 2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l, 0)); + 2500L * u + l, 3000L * u + l, + EconomicPolicy.TYPE_REWARD | 3 * u + l, "blue" + u + l, 3L * u + l, 0)); ledgers.add(userId, pkgName, ledger); } } @@ -248,9 +258,12 @@ public class ScribeTest { @Test public void testDiscardLedgerFromDisk() { final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1)); - ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 0)); - ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 1)); + ogLedger.recordTransaction( + new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 1)); + ogLedger.recordTransaction( + new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 0)); + ogLedger.recordTransaction( + new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_REWARD | 3, "blue", 3, 1)); mScribeUnderTest.writeImmediatelyForTesting(); mScribeUnderTest.loadFromDiskLocked(); @@ -269,9 +282,12 @@ public class ScribeTest { public void testLoadingMissingPackageFromDisk() { final String pkgName = TEST_PACKAGE + ".uninstalled"; final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName); - ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1)); - ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 2)); - ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3)); + ogLedger.recordTransaction( + new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REGULATION | 1, null, 51, 1)); + ogLedger.recordTransaction( + new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 2)); + ogLedger.recordTransaction( + new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_ACTION | 3, "blue", -3, 3)); mScribeUnderTest.writeImmediatelyForTesting(); // Package isn't installed, so make sure it's not saved to memory after loading. @@ -283,9 +299,13 @@ public class ScribeTest { public void testLoadingMissingUserFromDisk() { final int userId = TEST_USER_ID + 1; final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE); - ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0)); - ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 1)); - ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3)); + ogLedger.recordTransaction( + new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 0)); + ogLedger.recordTransaction( + new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 1)); + ogLedger.recordTransaction( + new Ledger.Transaction(2500, 3000, + EconomicPolicy.TYPE_REGULATION | 3, "blue", 3, 3)); mScribeUnderTest.writeImmediatelyForTesting(); // User doesn't show up with any packages, so make sure nothing is saved after loading. @@ -331,12 +351,34 @@ public class ScribeTest { } assertNotNull(actual); assertEquals(expected.getCurrentBalance(), actual.getCurrentBalance()); + List<Ledger.Transaction> expectedTransactions = expected.getTransactions(); List<Ledger.Transaction> actualTransactions = actual.getTransactions(); assertEquals(expectedTransactions.size(), actualTransactions.size()); for (int i = 0; i < expectedTransactions.size(); ++i) { assertTransactionsEqual(expectedTransactions.get(i), actualTransactions.get(i)); } + + List<Ledger.RewardBucket> expectedRewardBuckets = expected.getRewardBuckets(); + List<Ledger.RewardBucket> actualRewardBuckets = actual.getRewardBuckets(); + assertEquals(expectedRewardBuckets.size(), actualRewardBuckets.size()); + for (int i = 0; i < expectedRewardBuckets.size(); ++i) { + assertRewardBucketsEqual(expectedRewardBuckets.get(i), actualRewardBuckets.get(i)); + } + } + + private void assertSparseLongArraysEqual(SparseLongArray expected, SparseLongArray actual) { + if (expected == null) { + assertNull(actual); + return; + } + assertNotNull(actual); + final int size = expected.size(); + assertEquals(size, actual.size()); + for (int i = 0; i < size; ++i) { + assertEquals(expected.keyAt(i), actual.keyAt(i)); + assertEquals(expected.valueAt(i), actual.valueAt(i)); + } } private void assertReportListsEqual(List<Analyst.Report> expected, @@ -382,6 +424,17 @@ public class ScribeTest { } } + private void assertRewardBucketsEqual(Ledger.RewardBucket expected, + Ledger.RewardBucket actual) { + if (expected == null) { + assertNull(actual); + return; + } + assertNotNull(actual); + assertEquals(expected.startTimeMs, actual.startTimeMs); + assertSparseLongArraysEqual(expected.cumulativeDelta, actual.cumulativeDelta); + } + private void assertTransactionsEqual(Ledger.Transaction expected, Ledger.Transaction actual) { if (expected == null) { assertNull(actual); diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 20482afd53ac..503ca69a627f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -36,8 +36,6 @@ import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; import android.apex.IApexService; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -93,16 +91,16 @@ public class ApexManagerTest { ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); apexPackageInfo.scanApexPackages( apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - final PackageInfo activePkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, + final var activePair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); - assertThat(activePkgPi).isNotNull(); - assertThat(activePkgPi.packageName).contains(TEST_APEX_PKG); + assertThat(activePair).isNotNull(); + assertThat(activePair.second.getPackageName()).contains(TEST_APEX_PKG); - final PackageInfo factoryPkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, + final var factoryPair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); - assertThat(factoryPkgPi).isNull(); + assertThat(factoryPair).isNull(); } @Test @@ -111,16 +109,16 @@ public class ApexManagerTest { ApexPackageInfo apexPackageInfo = new ApexPackageInfo(); apexPackageInfo.scanApexPackages( apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService()); - PackageInfo factoryPkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, + var factoryPair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); - assertThat(factoryPkgPi).isNotNull(); - assertThat(factoryPkgPi.packageName).contains(TEST_APEX_PKG); + assertThat(factoryPair).isNotNull(); + assertThat(factoryPair.second.getPackageName()).contains(TEST_APEX_PKG); - final PackageInfo activePkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, + final var activePair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); - assertThat(activePkgPi).isNull(); + assertThat(activePair).isNull(); } @Test @@ -388,23 +386,16 @@ public class ApexManagerTest { newApexInfo = mApexManager.installPackage(installedApex); apexPackageInfo.notifyPackageInstalled(newApexInfo, mPackageParser2); - PackageInfo newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", + var newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", ApexManager.MATCH_ACTIVE_PACKAGE); - assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath()); - assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2); - assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) - .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); - assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) - .isEqualTo(ApplicationInfo.FLAG_INSTALLED); - - PackageInfo factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", + assertThat(newInfo.second.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath()); + assertThat(newInfo.second.getLongVersionCode()).isEqualTo(2); + + var factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", ApexManager.MATCH_FACTORY_PACKAGE); - assertThat(factoryInfo.applicationInfo.sourceDir).isEqualTo(activeApexInfo.modulePath); - assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1); - assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) - .isEqualTo(ApplicationInfo.FLAG_SYSTEM); - assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) - .isEqualTo(ApplicationInfo.FLAG_INSTALLED); + assertThat(factoryInfo.second.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath); + assertThat(factoryInfo.second.getLongVersionCode()).isEqualTo(1); + assertThat(factoryInfo.second.isSystem()).isTrue(); } @Test @@ -429,23 +420,16 @@ public class ApexManagerTest { newApexInfo = mApexManager.installPackage(installedApex); apexPackageInfo.notifyPackageInstalled(newApexInfo, mPackageParser2); - PackageInfo newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", + var newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", ApexManager.MATCH_ACTIVE_PACKAGE); - assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath()); - assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2); - assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) - .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); - assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) - .isEqualTo(ApplicationInfo.FLAG_INSTALLED); - - PackageInfo factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", + assertThat(newInfo.second.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath()); + assertThat(newInfo.second.getLongVersionCode()).isEqualTo(2); + + var factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless", ApexManager.MATCH_FACTORY_PACKAGE); - assertThat(factoryInfo.applicationInfo.sourceDir).isEqualTo(factoryApexInfo.modulePath); - assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1); - assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) - .isEqualTo(ApplicationInfo.FLAG_SYSTEM); - assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) - .isEqualTo(ApplicationInfo.FLAG_INSTALLED); + assertThat(factoryInfo.second.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath); + assertThat(factoryInfo.second.getLongVersionCode()).isEqualTo(1); + assertThat(factoryInfo.second.isSystem()).isTrue(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java index 22dcf842906c..54566c39a8d2 100644 --- a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java +++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java @@ -22,7 +22,12 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.tare.TareUtils.getCurrentTimeMillis; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.util.SparseLongArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -32,7 +37,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.time.Clock; +import java.time.Duration; import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; /** Test that the ledger records transactions correctly. */ @RunWith(AndroidJUnit4.class) @@ -44,6 +52,11 @@ public class LedgerTest { TareUtils.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); } + private void shiftSystemTime(long incrementMs) { + TareUtils.sSystemClock = + Clock.offset(TareUtils.sSystemClock, Duration.ofMillis(incrementMs)); + } + @Test public void testInitialState() { final Ledger ledger = new Ledger(); @@ -52,37 +65,149 @@ public class LedgerTest { } @Test + public void testInitialization_FullLists() { + final long balance = 1234567890L; + List<Ledger.Transaction> transactions = new ArrayList<>(); + List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>(); + + final long now = getCurrentTimeMillis(); + Ledger.Transaction secondTxn = null; + Ledger.RewardBucket remainingBucket = null; + for (int i = 0; i < Ledger.MAX_TRANSACTION_COUNT; ++i) { + final long start = now - 10 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS; + Ledger.Transaction transaction = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); + if (i == 1) { + secondTxn = transaction; + } + transactions.add(transaction); + } + for (int b = 0; b < Ledger.NUM_REWARD_BUCKET_WINDOWS; ++b) { + final long start = now - (Ledger.NUM_REWARD_BUCKET_WINDOWS - b) * 24 * HOUR_IN_MILLIS; + Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); + rewardBucket.startTimeMs = start; + for (int r = 0; r < 5; ++r) { + rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | r, b * start + r); + } + if (b == Ledger.NUM_REWARD_BUCKET_WINDOWS - 1) { + remainingBucket = rewardBucket; + } + rewardBuckets.add(rewardBucket); + } + final Ledger ledger = new Ledger(balance, transactions, rewardBuckets); + assertEquals(balance, ledger.getCurrentBalance()); + assertEquals(transactions, ledger.getTransactions()); + // Everything but the last bucket is old, so the returned list should only contain that + // bucket. + rewardBuckets.clear(); + rewardBuckets.add(remainingBucket); + assertEquals(rewardBuckets, ledger.getRewardBuckets()); + + // Make sure the ledger can properly record new transactions. + final long start = now - MINUTE_IN_MILLIS; + final long delta = 400; + final Ledger.Transaction transaction = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, EconomicPolicy.TYPE_REWARD | 1, null, delta, 0); + ledger.recordTransaction(transaction); + assertEquals(balance + delta, ledger.getCurrentBalance()); + transactions = ledger.getTransactions(); + assertEquals(secondTxn, transactions.get(0)); + assertEquals(transaction, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 1)); + final Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); + rewardBucket.startTimeMs = now; + rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | 1, delta); + rewardBuckets = ledger.getRewardBuckets(); + assertRewardBucketsEqual(remainingBucket, rewardBuckets.get(0)); + assertRewardBucketsEqual(rewardBucket, rewardBuckets.get(1)); + } + + @Test + public void testInitialization_OverflowingLists() { + final long balance = 1234567890L; + final List<Ledger.Transaction> transactions = new ArrayList<>(); + final List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>(); + + final long now = getCurrentTimeMillis(); + for (int i = 0; i < 2 * Ledger.MAX_TRANSACTION_COUNT; ++i) { + final long start = now - 20 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS; + Ledger.Transaction transaction = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); + transactions.add(transaction); + } + for (int b = 0; b < 2 * Ledger.NUM_REWARD_BUCKET_WINDOWS; ++b) { + final long start = now + - (2 * Ledger.NUM_REWARD_BUCKET_WINDOWS - b) * 6 * HOUR_IN_MILLIS; + Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); + rewardBucket.startTimeMs = start; + for (int r = 0; r < 5; ++r) { + rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | r, b * start + r); + } + rewardBuckets.add(rewardBucket); + } + final Ledger ledger = new Ledger(balance, transactions, rewardBuckets); + assertEquals(balance, ledger.getCurrentBalance()); + assertEquals(transactions.subList(Ledger.MAX_TRANSACTION_COUNT, + 2 * Ledger.MAX_TRANSACTION_COUNT), + ledger.getTransactions()); + assertEquals(rewardBuckets.subList(Ledger.NUM_REWARD_BUCKET_WINDOWS, + 2 * Ledger.NUM_REWARD_BUCKET_WINDOWS), + ledger.getRewardBuckets()); + } + + @Test public void testMultipleTransactions() { final Ledger ledger = new Ledger(); ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5, 0)); assertEquals(5, ledger.getCurrentBalance()); - assertEquals(5, ledger.get24HourSum(1, 60_000)); ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25, 0)); assertEquals(30, ledger.getCurrentBalance()); - assertEquals(30, ledger.get24HourSum(1, 60_000)); ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10, 5)); assertEquals(20, ledger.getCurrentBalance()); - assertEquals(20, ledger.get24HourSum(1, 60_000)); } @Test public void test24HourSum() { + final long now = getCurrentTimeMillis(); + final long end = now + 24 * HOUR_IN_MILLIS; + final int reward1 = EconomicPolicy.TYPE_REWARD | 1; + final int reward2 = EconomicPolicy.TYPE_REWARD | 2; final Ledger ledger = new Ledger(); - ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500, 0)); - assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS)); + + // First bucket + assertEquals(0, ledger.get24HourSum(reward1, end)); + ledger.recordTransaction(new Ledger.Transaction(now, now + 1000, reward1, null, 500, 0)); + assertEquals(500, ledger.get24HourSum(reward1, end)); + assertEquals(0, ledger.get24HourSum(reward2, end)); + ledger.recordTransaction( + new Ledger.Transaction(now + 2 * HOUR_IN_MILLIS, now + 3 * HOUR_IN_MILLIS, + reward1, null, 2500, 0)); + assertEquals(3000, ledger.get24HourSum(reward1, end)); + // Second bucket + shiftSystemTime(7 * HOUR_IN_MILLIS); // now + 7 + ledger.recordTransaction( + new Ledger.Transaction(now + 7 * HOUR_IN_MILLIS, now + 7 * HOUR_IN_MILLIS, + reward1, null, 1, 0)); ledger.recordTransaction( - new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500, 0)); - assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS)); + new Ledger.Transaction(now + 7 * HOUR_IN_MILLIS, now + 7 * HOUR_IN_MILLIS, + reward2, null, 42, 0)); + assertEquals(3001, ledger.get24HourSum(reward1, end)); + assertEquals(42, ledger.get24HourSum(reward2, end)); + // Third bucket + shiftSystemTime(12 * HOUR_IN_MILLIS); // now + 19 ledger.recordTransaction( - new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1, 0)); - assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS)); - assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS)); - assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS)); - // Pro-rated as the second transaction phases out - assertEquals(1251, - ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS)); - assertEquals(1, ledger.get24HourSum(1, 27 * HOUR_IN_MILLIS)); - assertEquals(0, ledger.get24HourSum(1, 28 * HOUR_IN_MILLIS)); + new Ledger.Transaction(now + 12 * HOUR_IN_MILLIS, now + 13 * HOUR_IN_MILLIS, + reward1, null, 300, 0)); + assertEquals(3301, ledger.get24HourSum(reward1, end)); + assertRewardBucketsInOrder(ledger.getRewardBuckets()); + // Older buckets should be excluded + assertEquals(301, ledger.get24HourSum(reward1, end + HOUR_IN_MILLIS)); + assertEquals(301, ledger.get24HourSum(reward1, end + 2 * HOUR_IN_MILLIS)); + // 2nd bucket should still be included since it started at the 7 hour mark + assertEquals(301, ledger.get24HourSum(reward1, end + 6 * HOUR_IN_MILLIS)); + assertEquals(42, ledger.get24HourSum(reward2, end + 6 * HOUR_IN_MILLIS)); + assertEquals(300, ledger.get24HourSum(reward1, end + 7 * HOUR_IN_MILLIS + 1)); + assertEquals(0, ledger.get24HourSum(reward2, end + 8 * HOUR_IN_MILLIS)); + assertEquals(0, ledger.get24HourSum(reward1, end + 19 * HOUR_IN_MILLIS + 1)); } @Test @@ -125,4 +250,127 @@ public class LedgerTest { ledger.removeOldTransactions(0); assertNull(ledger.getEarliestTransaction()); } + + @Test + public void testTransactionsAlwaysInOrder() { + final Ledger ledger = new Ledger(); + List<Ledger.Transaction> transactions = ledger.getTransactions(); + assertTrue(transactions.isEmpty()); + + final long now = getCurrentTimeMillis(); + Ledger.Transaction transaction1 = new Ledger.Transaction( + now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0); + Ledger.Transaction transaction2 = new Ledger.Transaction( + now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0); + Ledger.Transaction transaction3 = new Ledger.Transaction( + now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0); + // Instant event + Ledger.Transaction transaction4 = new Ledger.Transaction( + now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0); + + Ledger.Transaction transaction5 = new Ledger.Transaction( + now - 15 * HOUR_IN_MILLIS, now - 15 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS, + 1, null, 400, 0); + ledger.recordTransaction(transaction1); + ledger.recordTransaction(transaction2); + ledger.recordTransaction(transaction3); + ledger.recordTransaction(transaction4); + ledger.recordTransaction(transaction5); + + transactions = ledger.getTransactions(); + assertEquals(5, transactions.size()); + assertTransactionsInOrder(transactions); + + for (int i = 0; i < Ledger.MAX_TRANSACTION_COUNT - 5; ++i) { + final long start = now - 10 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS; + Ledger.Transaction transaction = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); + ledger.recordTransaction(transaction); + } + transactions = ledger.getTransactions(); + assertEquals(Ledger.MAX_TRANSACTION_COUNT, transactions.size()); + assertTransactionsInOrder(transactions); + + long start = now - 5 * HOUR_IN_MILLIS; + Ledger.Transaction transactionLast5 = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 4800, 0); + start = now - 4 * HOUR_IN_MILLIS; + Ledger.Transaction transactionLast4 = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 600, 0); + start = now - 3 * HOUR_IN_MILLIS; + Ledger.Transaction transactionLast3 = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 600, 0); + // Instant event + start = now - 2 * HOUR_IN_MILLIS; + Ledger.Transaction transactionLast2 = new Ledger.Transaction( + start, start, 1, null, 500, 0); + Ledger.Transaction transactionLast1 = new Ledger.Transaction( + start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); + ledger.recordTransaction(transactionLast5); + ledger.recordTransaction(transactionLast4); + ledger.recordTransaction(transactionLast3); + ledger.recordTransaction(transactionLast2); + ledger.recordTransaction(transactionLast1); + + transactions = ledger.getTransactions(); + assertEquals(Ledger.MAX_TRANSACTION_COUNT, transactions.size()); + assertTransactionsInOrder(transactions); + assertEquals(transactionLast1, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 1)); + assertEquals(transactionLast2, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 2)); + assertEquals(transactionLast3, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 3)); + assertEquals(transactionLast4, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 4)); + assertEquals(transactionLast5, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 5)); + assertFalse(transactions.contains(transaction1)); + assertFalse(transactions.contains(transaction2)); + assertFalse(transactions.contains(transaction3)); + assertFalse(transactions.contains(transaction4)); + assertFalse(transactions.contains(transaction5)); + } + + private void assertSparseLongArraysEqual(SparseLongArray expected, SparseLongArray actual) { + if (expected == null) { + assertNull(actual); + return; + } + assertNotNull(actual); + final int size = expected.size(); + assertEquals(size, actual.size()); + for (int i = 0; i < size; ++i) { + assertEquals(expected.keyAt(i), actual.keyAt(i)); + assertEquals(expected.valueAt(i), actual.valueAt(i)); + } + } + + private void assertRewardBucketsEqual(Ledger.RewardBucket expected, + Ledger.RewardBucket actual) { + if (expected == null) { + assertNull(actual); + return; + } + assertNotNull(actual); + assertEquals(expected.startTimeMs, actual.startTimeMs); + assertSparseLongArraysEqual(expected.cumulativeDelta, actual.cumulativeDelta); + } + + private void assertRewardBucketsInOrder(List<Ledger.RewardBucket> rewardBuckets) { + assertNotNull(rewardBuckets); + for (int i = 1; i < rewardBuckets.size(); ++i) { + final Ledger.RewardBucket prev = rewardBuckets.get(i - 1); + final Ledger.RewardBucket cur = rewardBuckets.get(i); + assertTrue("Newer bucket stored before older bucket @ index " + i + + ": " + prev.startTimeMs + " vs " + cur.startTimeMs, + prev.startTimeMs <= cur.startTimeMs); + } + } + + private void assertTransactionsInOrder(List<Ledger.Transaction> transactions) { + assertNotNull(transactions); + for (int i = 1; i < transactions.size(); ++i) { + final Ledger.Transaction prev = transactions.get(i - 1); + final Ledger.Transaction cur = transactions.get(i); + assertTrue("Newer transaction stored before older transaction @ index " + i + + ": " + prev.endTimeMs + " vs " + cur.endTimeMs, + prev.endTimeMs <= cur.endTimeMs); + } + } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a0b01a7fc27d..959429e255dd 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -639,6 +639,11 @@ public class UsageStatsService extends SystemService implements mAppStandby.initializeDefaultsForSystemApps(userId); } + private boolean sameApp(int callingUid, @UserIdInt int userId, String packageName) { + return mPackageManagerInternal.getPackageUid(packageName, 0 /* flags */, userId) + == callingUid; + } + private boolean isInstantApp(String packageName, int userId) { return mPackageManagerInternal.isPackageEphemeral(userId, packageName); } @@ -2354,17 +2359,15 @@ public class UsageStatsService extends SystemService implements } final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); // If the calling app is asking about itself, continue, else check for permission. - if (packageUid != callingUid) { - if (!hasPermission(callingPackage)) { - throw new SecurityException( - "Don't have permission to query app standby bucket"); - } + final boolean sameApp = packageUid == callingUid; + if (!sameApp && !hasPermission(callingPackage)) { + throw new SecurityException("Don't have permission to query app standby bucket"); } final boolean isInstantApp = isInstantApp(packageName, userId); final boolean cannotAccessInstantApps = shouldObfuscateInstantAppsForCaller(callingUid, userId); - if (packageUid < 0 || (isInstantApp && cannotAccessInstantApps)) { + if (packageUid < 0 || (!sameApp && isInstantApp && cannotAccessInstantApps)) { throw new IllegalArgumentException( "Cannot get standby bucket for non existent package (" + packageName + ")"); } @@ -2418,7 +2421,9 @@ public class UsageStatsService extends SystemService implements } final int targetUserId = userId; standbyBucketList.removeIf( - i -> cannotAccessInstantApps && isInstantApp(i.mPackageName, targetUserId)); + i -> !sameApp(callingUid, targetUserId, i.mPackageName) + && isInstantApp(i.mPackageName, targetUserId) + && cannotAccessInstantApps); return new ParceledListSlice<>(standbyBucketList); } finally { Binder.restoreCallingIdentity(token); diff --git a/tests/Camera2Tests/CameraToo/Android.bp b/tests/Camera2Tests/CameraToo/Android.bp new file mode 100644 index 000000000000..ebc6fed9fefd --- /dev/null +++ b/tests/Camera2Tests/CameraToo/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2014 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + default_applicable_licenses: [ + "frameworks_base_license", + ], +} + +android_test { + name: "CameraToo", + + sdk_version: "current", + srcs: ["src/**/*.java"], + +} diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk deleted file mode 100644 index 33473143c8cb..000000000000 --- a/tests/Camera2Tests/CameraToo/Android.mk +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_PACKAGE_NAME := CameraToo -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := $(call all-java-files-under,src) - -include $(BUILD_PACKAGE) diff --git a/tests/backup/Android.bp b/tests/backup/Android.bp new file mode 100644 index 000000000000..3890a1f58078 --- /dev/null +++ b/tests/backup/Android.bp @@ -0,0 +1,54 @@ +// Copyright (C) 2008 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// native test +// ======================================== +package { + // See: http://go/android-license-faq + default_applicable_licenses: [ + "frameworks_base_license", + ], +} + +cc_binary { + name: "backup_helper_test", + + srcs: ["backup_helper_test.cpp"], + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libandroidfw", + "libutils", + ], + +} + +// java test +// ======================================== +android_app { + name: "BackupTest", + + srcs: ["**/*.java"], + + platform_apis: true, + + optimize: { + enabled: false, + }, + +} diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk deleted file mode 100644 index b6f34717658c..000000000000 --- a/tests/backup/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) 2008 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -# native test -# ======================================== -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - backup_helper_test.cpp - -LOCAL_CFLAGS := -Wall -Werror -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := backup_helper_test -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE -LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) -LOCAL_SHARED_LIBRARIES := libandroidfw libutils - -include $(BUILD_EXECUTABLE) - -# java test -# ======================================== -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := BackupTest -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_PROGUARD_ENABLED := disabled - -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE -include $(BUILD_PACKAGE) |