diff options
250 files changed, 5262 insertions, 1888 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java index abc196f36d7c..b84c8a41af1c 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java @@ -286,7 +286,7 @@ class Agent { for (int i = 0; i < pkgNames.size(); ++i) { final String pkgName = pkgNames.valueAt(i); - final boolean isVip = mIrs.isVip(userId, pkgName); + final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed); SparseArrayMap<String, OngoingEvent> ongoingEvents = mCurrentOngoingEvents.get(userId, pkgName); if (ongoingEvents != null) { @@ -321,7 +321,7 @@ class Agent { final long nowElapsed = SystemClock.elapsedRealtime(); final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - final boolean isVip = mIrs.isVip(userId, pkgName); + final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed); SparseArrayMap<String, OngoingEvent> ongoingEvents = mCurrentOngoingEvents.get(userId, pkgName); if (ongoingEvents != null) { @@ -397,7 +397,7 @@ class Agent { if (actionAffordabilityNotes != null) { final int size = actionAffordabilityNotes.size(); final long newBalance = getBalanceLocked(userId, pkgName); - final boolean isVip = mIrs.isVip(userId, pkgName); + final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed); for (int n = 0; n < size; ++n) { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); note.recalculateCosts(economicPolicy, userId, pkgName); @@ -503,7 +503,8 @@ class Agent { "Tried to adjust system balance for " + appToString(userId, pkgName)); return; } - if (mIrs.isVip(userId, pkgName)) { + final boolean isVip = mIrs.isVip(userId, pkgName); + if (isVip) { // This could happen if the app was made a VIP after it started performing actions. // Continue recording the transaction for debugging purposes, but don't let it change // any numbers. @@ -536,7 +537,6 @@ class Agent { mActionAffordabilityNotes.get(userId, pkgName); if (actionAffordabilityNotes != null) { final long newBalance = ledger.getCurrentBalance(); - final boolean isVip = mIrs.isVip(userId, pkgName); for (int i = 0; i < actionAffordabilityNotes.size(); ++i) { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); final boolean isAffordable = isVip @@ -830,7 +830,6 @@ class Agent { @GuardedBy("mLock") void onUserRemovedLocked(final int userId) { - mScribe.discardLedgersLocked(userId); mCurrentOngoingEvents.delete(userId); mBalanceThresholdAlarmQueue.removeAlarmsForUserId(userId); } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java index fcb3e6759412..1ff389deedd2 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java @@ -16,14 +16,20 @@ package com.android.server.tare; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; +import android.content.Context; +import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.InstallSourceInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.RemoteException; +import com.android.internal.util.ArrayUtils; + /** POJO to cache only the information about installed packages that TARE cares about. */ class InstalledPackageInfo { static final int NO_UID = -1; @@ -31,14 +37,22 @@ class InstalledPackageInfo { public final int uid; public final String packageName; public final boolean hasCode; + public final boolean isSystemInstaller; @Nullable public final String installerPackageName; - InstalledPackageInfo(@NonNull PackageInfo packageInfo) { + InstalledPackageInfo(@NonNull Context context, @NonNull PackageInfo packageInfo) { final ApplicationInfo applicationInfo = packageInfo.applicationInfo; uid = applicationInfo == null ? NO_UID : applicationInfo.uid; packageName = packageInfo.packageName; hasCode = applicationInfo != null && applicationInfo.hasCode(); + isSystemInstaller = applicationInfo != null + && ArrayUtils.indexOf( + packageInfo.requestedPermissions, Manifest.permission.INSTALL_PACKAGES) >= 0 + && PackageManager.PERMISSION_GRANTED + == PermissionChecker.checkPermissionForPreflight(context, + Manifest.permission.INSTALL_PACKAGES, PermissionChecker.PID_UNKNOWN, + applicationInfo.uid, packageName); InstallSourceInfo installSourceInfo = null; try { installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java index 17b87469f7be..4001d9b06ee0 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -64,6 +64,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArrayMap; +import android.util.SparseLongArray; import android.util.SparseSetArray; import com.android.internal.annotations.GuardedBy; @@ -108,6 +109,16 @@ public class InternalResourceService extends SystemService { /** The amount of time to delay reclamation by after boot. */ private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L; /** + * The amount of time after TARE has first been set up that a system installer will be allowed + * expanded credit privileges. + */ + static final long INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS = 7 * DAY_IN_MILLIS; + /** + * The amount of time to wait after TARE has first been set up before considering adjusting the + * stock/consumption limit. + */ + private static final long STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS = 5 * DAY_IN_MILLIS; + /** * The battery level above which we may consider quantitative easing (increasing the consumption * limit). */ @@ -127,7 +138,7 @@ public class InternalResourceService extends SystemService { private static final long STOCK_RECALCULATION_MIN_DATA_DURATION_MS = 8 * HOUR_IN_MILLIS; private static final int PACKAGE_QUERY_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_APEX; + | PackageManager.MATCH_APEX | PackageManager.GET_PERMISSIONS; /** Global lock for all resource economy state. */ private final Object mLock = new Object(); @@ -179,6 +190,13 @@ public class InternalResourceService extends SystemService { @GuardedBy("mLock") private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>(); + /** + * Set of temporary Very Important Packages and when their VIP status ends, in the elapsed + * realtime ({@link android.annotation.ElapsedRealtimeLong}) timebase. + */ + @GuardedBy("mLock") + private final SparseArrayMap<String, Long> mTemporaryVips = new SparseArrayMap<>(); + /** Set of apps each installer is responsible for installing. */ @GuardedBy("mLock") private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>(); @@ -308,6 +326,7 @@ public class InternalResourceService extends SystemService { private static final int MSG_PROCESS_USAGE_EVENT = 2; private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3; private static final int MSG_NOTIFY_STATE_CHANGE_LISTENER = 4; + private static final int MSG_CLEAN_UP_TEMP_VIP_LIST = 5; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; /** @@ -413,6 +432,13 @@ public class InternalResourceService extends SystemService { return userPkgs; } + @Nullable + InstalledPackageInfo getInstalledPackageInfo(final int userId, @NonNull final String pkgName) { + synchronized (mLock) { + return mPkgCache.get(userId, pkgName); + } + } + @GuardedBy("mLock") long getConsumptionLimitLocked() { return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100; @@ -429,6 +455,11 @@ public class InternalResourceService extends SystemService { return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit(); } + + long getRealtimeSinceFirstSetupMs() { + return mScribe.getRealtimeSinceFirstSetupMs(SystemClock.elapsedRealtime()); + } + int getUid(final int userId, @NonNull final String pkgName) { synchronized (mPackageToUidCache) { Integer uid = mPackageToUidCache.get(userId, pkgName); @@ -470,6 +501,10 @@ public class InternalResourceService extends SystemService { } boolean isVip(final int userId, @NonNull String pkgName) { + return isVip(userId, pkgName, SystemClock.elapsedRealtime()); + } + + boolean isVip(final int userId, @NonNull String pkgName, final long nowElapsed) { synchronized (mLock) { final Boolean override = mVipOverrides.get(userId, pkgName); if (override != null) { @@ -481,6 +516,12 @@ public class InternalResourceService extends SystemService { // operate. return true; } + synchronized (mLock) { + final Long expirationTimeElapsed = mTemporaryVips.get(userId, pkgName); + if (expirationTimeElapsed != null) { + return nowElapsed <= expirationTimeElapsed; + } + } return false; } @@ -569,7 +610,7 @@ public class InternalResourceService extends SystemService { mPackageToUidCache.add(userId, pkgName, uid); } synchronized (mLock) { - final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo); + final InstalledPackageInfo ipo = new InstalledPackageInfo(getContext(), packageInfo); final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo); maybeUpdateInstallerStatusLocked(oldIpo, ipo); mUidToPackageCache.add(uid, pkgName); @@ -626,11 +667,16 @@ public class InternalResourceService extends SystemService { final List<PackageInfo> pkgs = mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId); for (int i = pkgs.size() - 1; i >= 0; --i) { - final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i)); + final InstalledPackageInfo ipo = + new InstalledPackageInfo(getContext(), pkgs.get(i)); final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo); maybeUpdateInstallerStatusLocked(oldIpo, ipo); } mAgent.grantBirthrightsLocked(userId); + final long nowElapsed = SystemClock.elapsedRealtime(); + mScribe.setUserAddedTimeLocked(userId, nowElapsed); + grantInstallersTemporaryVipStatusLocked(userId, + nowElapsed, INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS); } } @@ -647,6 +693,7 @@ public class InternalResourceService extends SystemService { mInstallers.delete(userId); mPkgCache.delete(userId); mAgent.onUserRemovedLocked(userId); + mScribe.onUserRemovedLocked(userId); } } @@ -659,6 +706,10 @@ public class InternalResourceService extends SystemService { maybeAdjustDesiredStockLevelLocked(); return; } + if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) { + // Things can be very tumultuous soon after first setup. + return; + } // We don't need to increase the limit if the device runs out of consumable credits // when the battery is low. final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked(); @@ -687,6 +738,10 @@ public class InternalResourceService extends SystemService { if (!mConfigObserver.ENABLE_TIP3) { return; } + if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) { + // Things can be very tumultuous soon after first setup. + return; + } // Don't adjust the limit too often or while the battery is low. final long now = getCurrentTimeMillis(); if ((now - mScribe.getLastStockRecalculationTimeLocked()) < STOCK_RECALCULATION_DELAY_MS @@ -776,6 +831,28 @@ public class InternalResourceService extends SystemService { } @GuardedBy("mLock") + private void grantInstallersTemporaryVipStatusLocked(int userId, long nowElapsed, + long grantDurationMs) { + final long grantEndTimeElapsed = nowElapsed + grantDurationMs; + final int uIdx = mPkgCache.indexOfKey(userId); + if (uIdx < 0) { + return; + } + for (int pIdx = mPkgCache.numElementsForKey(uIdx) - 1; pIdx >= 0; --pIdx) { + final InstalledPackageInfo ipo = mPkgCache.valueAt(uIdx, pIdx); + + if (ipo.isSystemInstaller) { + final Long currentGrantEndTimeElapsed = mTemporaryVips.get(userId, ipo.packageName); + if (currentGrantEndTimeElapsed == null + || currentGrantEndTimeElapsed < grantEndTimeElapsed) { + mTemporaryVips.add(userId, ipo.packageName, grantEndTimeElapsed); + } + } + } + mHandler.sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST, grantDurationMs); + } + + @GuardedBy("mLock") private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) { if (!mIsEnabled) { return; @@ -870,7 +947,8 @@ public class InternalResourceService extends SystemService { final List<PackageInfo> pkgs = mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId); for (int i = pkgs.size() - 1; i >= 0; --i) { - final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i)); + final InstalledPackageInfo ipo = + new InstalledPackageInfo(getContext(), pkgs.get(i)); final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo); maybeUpdateInstallerStatusLocked(oldIpo, ipo); } @@ -953,11 +1031,17 @@ public class InternalResourceService extends SystemService { synchronized (mLock) { mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties()); loadInstalledPackageListLocked(); + final SparseLongArray timeSinceUsersAdded; final boolean isFirstSetup = !mScribe.recordExists(); + final long nowElapsed = SystemClock.elapsedRealtime(); if (isFirstSetup) { mAgent.grantBirthrightsLocked(); mScribe.setConsumptionLimitLocked( mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()); + // Set the last reclamation time to now so we don't start reclaiming assets + // too early. + mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); + timeSinceUsersAdded = new SparseLongArray(); } else { mScribe.loadFromDiskLocked(); if (mScribe.getSatiatedConsumptionLimitLocked() @@ -971,6 +1055,21 @@ public class InternalResourceService extends SystemService { // Adjust the supply in case battery level changed while the device was off. adjustCreditSupplyLocked(true); } + timeSinceUsersAdded = mScribe.getRealtimeSinceUsersAddedLocked(nowElapsed); + } + + final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds(); + for (int userId : userIds) { + final long timeSinceUserAddedMs = timeSinceUsersAdded.get(userId, 0); + // Temporarily mark installers as VIPs so they aren't subject to credit + // limits and policies on first boot. + if (timeSinceUserAddedMs < INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS) { + final long remainingGraceDurationMs = + INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS - timeSinceUserAddedMs; + + grantInstallersTemporaryVipStatusLocked(userId, nowElapsed, + remainingGraceDurationMs); + } } scheduleUnusedWealthReclamationLocked(); } @@ -1079,6 +1178,36 @@ public class InternalResourceService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { + case MSG_CLEAN_UP_TEMP_VIP_LIST: { + removeMessages(MSG_CLEAN_UP_TEMP_VIP_LIST); + + synchronized (mLock) { + final long nowElapsed = SystemClock.elapsedRealtime(); + + long earliestExpiration = Long.MAX_VALUE; + for (int u = 0; u < mTemporaryVips.numMaps(); ++u) { + final int userId = mTemporaryVips.keyAt(u); + + for (int p = mTemporaryVips.numElementsForKeyAt(u) - 1; p >= 0; --p) { + final String pkgName = mTemporaryVips.keyAt(u, p); + final Long expiration = mTemporaryVips.valueAt(u, p); + + if (expiration == null || expiration < nowElapsed) { + mTemporaryVips.delete(userId, pkgName); + } else { + earliestExpiration = Math.min(earliestExpiration, expiration); + } + } + } + + if (earliestExpiration < Long.MAX_VALUE) { + sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST, + earliestExpiration - nowElapsed); + } + } + } + break; + case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: { final SomeArgs args = (SomeArgs) msg.obj; final int userId = args.argi1; @@ -1558,6 +1687,7 @@ public class InternalResourceService extends SystemService { boolean printedVips = false; pw.println(); pw.print("VIPs:"); + pw.increaseIndent(); for (int u = 0; u < mVipOverrides.numMaps(); ++u) { final int userId = mVipOverrides.keyAt(u); @@ -1576,6 +1706,32 @@ public class InternalResourceService extends SystemService { } else { pw.print(" None"); } + pw.decreaseIndent(); + pw.println(); + + boolean printedTempVips = false; + pw.println(); + pw.print("Temp VIPs:"); + pw.increaseIndent(); + for (int u = 0; u < mTemporaryVips.numMaps(); ++u) { + final int userId = mTemporaryVips.keyAt(u); + + for (int p = 0; p < mTemporaryVips.numElementsForKeyAt(u); ++p) { + final String pkgName = mTemporaryVips.keyAt(u, p); + + printedTempVips = true; + pw.println(); + pw.print(appToString(userId, pkgName)); + pw.print("="); + pw.print(mTemporaryVips.valueAt(u, p)); + } + } + if (printedTempVips) { + pw.println(); + } else { + pw.print(" None"); + } + pw.decreaseIndent(); pw.println(); pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java index 7cf459c2e494..c2a6e43ba930 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java @@ -117,6 +117,7 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING; import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE; import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE; import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE; +import static com.android.server.tare.TareUtils.appToString; import static com.android.server.tare.TareUtils.cakeToString; import android.annotation.NonNull; @@ -210,6 +211,22 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { if (mIrs.isPackageRestricted(userId, pkgName)) { return 0; } + final InstalledPackageInfo ipo = mIrs.getInstalledPackageInfo(userId, pkgName); + if (ipo == null) { + Slog.wtfStack(TAG, + "Tried to get max balance of invalid app: " + appToString(userId, pkgName)); + } else { + // A system installer's max balance is elevated for some time after first boot so + // they can use jobs to download and install apps. + if (ipo.isSystemInstaller) { + final long timeSinceFirstSetupMs = mIrs.getRealtimeSinceFirstSetupMs(); + final boolean stillExempted = timeSinceFirstSetupMs + < InternalResourceService.INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS; + if (stillExempted) { + return mMaxSatiatedConsumptionLimit; + } + } + } return mMaxSatiatedBalance; } 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 ee448b5a6add..b41c0d1371c0 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java @@ -24,6 +24,7 @@ import static com.android.server.tare.TareUtils.cakeToString; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Environment; +import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.AtomicFile; @@ -33,6 +34,7 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseArrayMap; +import android.util.SparseLongArray; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -88,6 +90,7 @@ public class Scribe { "lastStockRecalculationTime"; private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes"; private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit"; + private static final String XML_ATTR_TIME_SINCE_FIRST_SETUP_MS = "timeSinceFirstSetup"; private static final String XML_ATTR_PR_DISCHARGE = "discharge"; private static final String XML_ATTR_PR_BATTERY_LEVEL = "batteryLevel"; private static final String XML_ATTR_PR_PROFIT = "profit"; @@ -112,6 +115,11 @@ public class Scribe { private final InternalResourceService mIrs; private final Analyst mAnalyst; + /** + * The value of elapsed realtime since TARE was first setup that was read from disk. + * This will only be changed when the persisted file is read. + */ + private long mLoadedTimeSinceFirstSetup; @GuardedBy("mIrs.getLock()") private long mLastReclamationTime; @GuardedBy("mIrs.getLock()") @@ -122,6 +130,9 @@ public class Scribe { private long mRemainingConsumableCakes; @GuardedBy("mIrs.getLock()") private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>(); + /** Offsets used to calculate the total realtime since each user was added. */ + @GuardedBy("mIrs.getLock()") + private final SparseLongArray mRealtimeSinceUsersAddedOffsets = new SparseLongArray(); private final Runnable mCleanRunnable = this::cleanupLedgers; private final Runnable mWriteRunnable = this::writeState; @@ -163,8 +174,9 @@ public class Scribe { } @GuardedBy("mIrs.getLock()") - void discardLedgersLocked(final int userId) { + void onUserRemovedLocked(final int userId) { mLedgers.delete(userId); + mRealtimeSinceUsersAddedOffsets.delete(userId); postWrite(); } @@ -215,6 +227,11 @@ public class Scribe { return sum; } + /** Returns the cumulative elapsed realtime since TARE was first setup. */ + long getRealtimeSinceFirstSetupMs(long nowElapsed) { + return mLoadedTimeSinceFirstSetup + nowElapsed; + } + /** Returns the total amount of cakes that remain to be consumed. */ @GuardedBy("mIrs.getLock()") long getRemainingConsumableCakesLocked() { @@ -222,6 +239,16 @@ public class Scribe { } @GuardedBy("mIrs.getLock()") + SparseLongArray getRealtimeSinceUsersAddedLocked(long nowElapsed) { + final SparseLongArray realtimes = new SparseLongArray(); + for (int i = mRealtimeSinceUsersAddedOffsets.size() - 1; i >= 0; --i) { + realtimes.put(mRealtimeSinceUsersAddedOffsets.keyAt(i), + mRealtimeSinceUsersAddedOffsets.valueAt(i) + nowElapsed); + } + return realtimes; + } + + @GuardedBy("mIrs.getLock()") void loadFromDiskLocked() { mLedgers.clear(); if (!recordExists()) { @@ -276,7 +303,8 @@ public class Scribe { } } - final long endTimeCutoff = System.currentTimeMillis() - MAX_TRANSACTION_AGE_MS; + final long now = System.currentTimeMillis(); + final long endTimeCutoff = now - MAX_TRANSACTION_AGE_MS; long earliestEndTime = Long.MAX_VALUE; for (eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; eventType = parser.next()) { @@ -294,6 +322,12 @@ public class Scribe { parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME); mLastStockRecalculationTime = parser.getAttributeLong(null, XML_ATTR_LAST_STOCK_RECALCULATION_TIME, 0); + mLoadedTimeSinceFirstSetup = + parser.getAttributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, + // If there's no recorded time since first setup, then + // offset the current elapsed time so it doesn't shift the + // timing too much. + -SystemClock.elapsedRealtime()); mSatiatedConsumptionLimit = parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mIrs.getInitialSatiatedConsumptionLimitLocked()); @@ -356,6 +390,13 @@ public class Scribe { } @GuardedBy("mIrs.getLock()") + void setUserAddedTimeLocked(int userId, long timeElapsed) { + // Use the current time as an offset so that when we persist the time, it correctly persists + // as "time since now". + mRealtimeSinceUsersAddedOffsets.put(userId, -timeElapsed); + } + + @GuardedBy("mIrs.getLock()") void tearDownLocked() { TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable); TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable); @@ -486,6 +527,14 @@ public class Scribe { // Don't return early since we need to go through all the ledger tags and get to the end // of the user tag. } + if (curUser != UserHandle.USER_NULL) { + mRealtimeSinceUsersAddedOffsets.put(curUser, + parser.getAttributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, + // If there's no recorded time since first setup, then + // offset the current elapsed time so it doesn't shift the + // timing too much. + -SystemClock.elapsedRealtime())); + } long earliestEndTime = Long.MAX_VALUE; for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; @@ -630,6 +679,8 @@ public class Scribe { out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime); out.attributeLong(null, XML_ATTR_LAST_STOCK_RECALCULATION_TIME, mLastStockRecalculationTime); + out.attributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, + mLoadedTimeSinceFirstSetup + SystemClock.elapsedRealtime()); out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit); out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES, mRemainingConsumableCakes); @@ -665,6 +716,9 @@ public class Scribe { out.startTag(null, XML_TAG_USER); out.attributeInt(null, XML_ATTR_USER_ID, userId); + out.attributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, + mRealtimeSinceUsersAddedOffsets.get(userId, + mLoadedTimeSinceFirstSetup + SystemClock.elapsedRealtime())); for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) { final String pkgName = mLedgers.keyAt(uIdx, pIdx); final Ledger ledger = mLedgers.get(userId, pkgName); diff --git a/cmds/idmap2/self_targeting/SelfTargeting.cpp b/cmds/idmap2/self_targeting/SelfTargeting.cpp index 20aa7d32a3c2..a8aa03309b16 100644 --- a/cmds/idmap2/self_targeting/SelfTargeting.cpp +++ b/cmds/idmap2/self_targeting/SelfTargeting.cpp @@ -38,9 +38,10 @@ namespace android::self_targeting { constexpr const mode_t kIdmapFilePermission = S_IRUSR | S_IWUSR; // u=rw-, g=---, o=--- extern "C" bool -CreateFrroFile(std::string& out_err_result, std::string& packageName, std::string& overlayName, - std::string& targetPackageName, std::optional<std::string>& targetOverlayable, - std::vector<FabricatedOverlayEntryParameters>& entries_params, +CreateFrroFile(std::string& out_err_result, const std::string& packageName, + const std::string& overlayName, const std::string& targetPackageName, + const std::optional<std::string>& targetOverlayable, + const std::vector<FabricatedOverlayEntryParameters>& entries_params, const std::string& frro_file_path) { android::idmap2::FabricatedOverlay::Builder builder(packageName, overlayName, targetPackageName); @@ -90,9 +91,46 @@ CreateFrroFile(std::string& out_err_result, std::string& packageName, std::strin return true; } +static PolicyBitmask GetFulfilledPolicy(const bool isSystem, const bool isVendor, + const bool isProduct, const bool isTargetSignature, + const bool isOdm, const bool isOem) { + auto fulfilled_policy = static_cast<PolicyBitmask>(PolicyFlags::PUBLIC); + + if (isSystem) { + fulfilled_policy |= PolicyFlags::SYSTEM_PARTITION; + } + if (isVendor) { + fulfilled_policy |= PolicyFlags::VENDOR_PARTITION; + } + if (isProduct) { + fulfilled_policy |= PolicyFlags::PRODUCT_PARTITION; + } + if (isOdm) { + fulfilled_policy |= PolicyFlags::ODM_PARTITION; + } + if (isOem) { + fulfilled_policy |= PolicyFlags::OEM_PARTITION; + } + if (isTargetSignature) { + fulfilled_policy |= PolicyFlags::SIGNATURE; + } + + // Not support actor_signature and config_overlay_signature + fulfilled_policy &= + ~(PolicyFlags::ACTOR_SIGNATURE | PolicyFlags::CONFIG_SIGNATURE); + + ALOGV( + "fulfilled_policy = 0x%08x, isSystem = %d, isVendor = %d, isProduct = %d," + " isTargetSignature = %d, isOdm = %d, isOem = %d,", + fulfilled_policy, isSystem, isVendor, isProduct, isTargetSignature, isOdm, isOem); + return fulfilled_policy; +} + extern "C" bool CreateIdmapFile(std::string& out_err, const std::string& targetPath, const std::string& overlayPath, - const std::string& idmapPath, const std::string& overlayName) { + const std::string& idmapPath, const std::string& overlayName, + const bool isSystem, const bool isVendor, const bool isProduct, + const bool isTargetSignature, const bool isOdm, const bool isOem) { // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap // guarantees that existing memory maps will continue to be valid and unaffected. The file must // be deleted before attempting to create the idmap, so that if idmap creation fails, the @@ -114,14 +152,11 @@ CreateIdmapFile(std::string& out_err, const std::string& targetPath, const std:: } // Overlay self target process. Only allow self-targeting types. - const auto fulfilled_policies = static_cast<PolicyBitmask>( - PolicyFlags::PUBLIC | PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION | - PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE | PolicyFlags::ODM_PARTITION | - PolicyFlags::OEM_PARTITION | PolicyFlags::ACTOR_SIGNATURE | - PolicyFlags::CONFIG_SIGNATURE); + const auto fulfilled_policies = GetFulfilledPolicy(isSystem, isVendor, isProduct, + isTargetSignature, isOdm, isOem); const auto idmap = Idmap::FromContainers(**target, **overlay, overlayName, - fulfilled_policies, false /* enforce_overlayable */); + fulfilled_policies, true /* enforce_overlayable */); if (!idmap) { out_err = base::StringPrintf("Failed to create idmap because of %s", idmap.GetErrorMessage().c_str()); diff --git a/core/api/current.txt b/core/api/current.txt index fc319ce6891e..7cfb7f46b57f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -11735,6 +11735,7 @@ package android.content.pm { method @NonNull public int[] getChildSessionIds(); method @NonNull public String[] getNames() throws java.io.IOException; method public int getParentSessionId(); + method public boolean isKeepApplicationEnabledSetting(); method public boolean isMultiPackage(); method public boolean isStaged(); method @NonNull public java.io.InputStream openRead(@NonNull String) throws java.io.IOException; @@ -11786,6 +11787,7 @@ package android.content.pm { method public boolean hasParentSessionId(); method public boolean isActive(); method public boolean isCommitted(); + method public boolean isKeepApplicationEnabledSetting(); method public boolean isMultiPackage(); method public boolean isSealed(); method public boolean isStaged(); @@ -11818,6 +11820,7 @@ package android.content.pm { method public void setInstallLocation(int); method public void setInstallReason(int); method public void setInstallScenario(int); + method public void setKeepApplicationEnabledSetting(); method public void setMultiPackage(); method public void setOriginatingUid(int); method public void setOriginatingUri(@Nullable android.net.Uri); @@ -12982,6 +12985,14 @@ package android.content.res.loader { package android.credentials { + public final class ClearCredentialStateRequest implements android.os.Parcelable { + ctor public ClearCredentialStateRequest(@NonNull android.os.Bundle); + method public int describeContents(); + method @NonNull public android.os.Bundle getData(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ClearCredentialStateRequest> CREATOR; + } + public final class CreateCredentialRequest implements android.os.Parcelable { ctor public CreateCredentialRequest(@NonNull String, @NonNull android.os.Bundle); method public int describeContents(); @@ -13009,8 +13020,9 @@ package android.credentials { } public final class CredentialManager { - method public void executeCreateCredential(@NonNull android.credentials.CreateCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CredentialManagerException>); - method public void executeGetCredential(@NonNull android.credentials.GetCredentialRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.CredentialManagerException>); + method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.CredentialManagerException>); + method public void executeCreateCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CredentialManagerException>); + method public void executeGetCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.CredentialManagerException>); } public class CredentialManagerException extends java.lang.Exception { @@ -35868,6 +35880,7 @@ package android.provider { method public static boolean canDrawOverlays(android.content.Context); field public static final String ACTION_ACCESSIBILITY_SETTINGS = "android.settings.ACCESSIBILITY_SETTINGS"; field public static final String ACTION_ADD_ACCOUNT = "android.settings.ADD_ACCOUNT_SETTINGS"; + field public static final String ACTION_ADVANCED_MEMORY_PROTECTION_SETTINGS = "android.settings.ADVANCED_MEMORY_PROTECTION_SETTINGS"; field public static final String ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS"; field public static final String ACTION_ALL_APPS_NOTIFICATION_SETTINGS = "android.settings.ALL_APPS_NOTIFICATION_SETTINGS"; field public static final String ACTION_APN_SETTINGS = "android.settings.APN_SETTINGS"; @@ -35916,7 +35929,6 @@ package android.provider { field public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES"; field public static final String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS"; field public static final String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; - field public static final String ACTION_MEMTAG_SETTINGS = "android.settings.MEMTAG_SETTINGS"; field public static final String ACTION_NETWORK_OPERATOR_SETTINGS = "android.settings.NETWORK_OPERATOR_SETTINGS"; field public static final String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS"; field public static final String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS"; @@ -39319,6 +39331,149 @@ package android.service.controls.templates { } +package android.service.credentials { + + public final class Action implements android.os.Parcelable { + ctor public Action(@NonNull android.app.slice.Slice, @NonNull android.app.PendingIntent); + method public int describeContents(); + method @NonNull public android.app.PendingIntent getPendingIntent(); + method @NonNull public android.app.slice.Slice getSlice(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.Action> CREATOR; + } + + public final class BeginCreateCredentialRequest implements android.os.Parcelable { + ctor public BeginCreateCredentialRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle); + method public int describeContents(); + method @NonNull public String getCallingPackage(); + method @NonNull public android.os.Bundle getData(); + method @NonNull public String getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginCreateCredentialRequest> CREATOR; + } + + public final class BeginCreateCredentialResponse implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.service.credentials.CreateEntry> getCreateEntries(); + method @Nullable public android.service.credentials.CreateEntry getRemoteCreateEntry(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.BeginCreateCredentialResponse> CREATOR; + } + + public static final class BeginCreateCredentialResponse.Builder { + ctor public BeginCreateCredentialResponse.Builder(); + method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder addCreateEntry(@NonNull android.service.credentials.CreateEntry); + method @NonNull public android.service.credentials.BeginCreateCredentialResponse build(); + method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setCreateEntries(@NonNull java.util.List<android.service.credentials.CreateEntry>); + method @NonNull public android.service.credentials.BeginCreateCredentialResponse.Builder setRemoteCreateEntry(@Nullable android.service.credentials.CreateEntry); + } + + public final class CreateCredentialRequest implements android.os.Parcelable { + ctor public CreateCredentialRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle); + method public int describeContents(); + method @NonNull public String getCallingPackage(); + method @NonNull public android.os.Bundle getData(); + method @NonNull public String getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CreateCredentialRequest> CREATOR; + } + + public final class CreateEntry implements android.os.Parcelable { + ctor public CreateEntry(@NonNull android.app.slice.Slice, @NonNull android.app.PendingIntent); + method public int describeContents(); + method @NonNull public android.app.PendingIntent getPendingIntent(); + method @NonNull public android.app.slice.Slice getSlice(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CreateEntry> CREATOR; + } + + public final class CredentialEntry implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.credentials.Credential getCredential(); + method @Nullable public android.app.PendingIntent getPendingIntent(); + method @NonNull public android.app.slice.Slice getSlice(); + method @NonNull public String getType(); + method public boolean isAutoSelectAllowed(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CredentialEntry> CREATOR; + } + + public static final class CredentialEntry.Builder { + ctor public CredentialEntry.Builder(@NonNull String, @NonNull android.app.slice.Slice, @NonNull android.app.PendingIntent); + ctor public CredentialEntry.Builder(@NonNull String, @NonNull android.app.slice.Slice, @NonNull android.credentials.Credential); + method @NonNull public android.service.credentials.CredentialEntry build(); + method @NonNull public android.service.credentials.CredentialEntry.Builder setAutoSelectAllowed(@NonNull boolean); + } + + public class CredentialProviderException extends java.lang.Exception { + ctor public CredentialProviderException(int, @NonNull String, @NonNull Throwable); + ctor public CredentialProviderException(int, @NonNull String); + ctor public CredentialProviderException(int, @NonNull Throwable); + ctor public CredentialProviderException(int); + method public int getErrorCode(); + field public static final int ERROR_UNKNOWN = 0; // 0x0 + } + + public abstract class CredentialProviderService extends android.app.Service { + ctor public CredentialProviderService(); + method public abstract void onBeginCreateCredential(@NonNull android.service.credentials.BeginCreateCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,android.service.credentials.CredentialProviderException>); + method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public abstract void onGetCredentials(@NonNull android.service.credentials.GetCredentialsRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.GetCredentialsResponse,android.service.credentials.CredentialProviderException>); + field public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; + field public static final String EXTRA_CREATE_CREDENTIAL_REQUEST = "android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST"; + field public static final String EXTRA_CREATE_CREDENTIAL_RESULT = "android.service.credentials.extra.CREATE_CREDENTIAL_RESULT"; + field public static final String EXTRA_CREDENTIAL_RESULT = "android.service.credentials.extra.CREDENTIAL_RESULT"; + field public static final String EXTRA_ERROR = "android.service.credentials.extra.ERROR"; + field public static final String EXTRA_GET_CREDENTIALS_CONTENT_RESULT = "android.service.credentials.extra.GET_CREDENTIALS_CONTENT_RESULT"; + field public static final String SERVICE_INTERFACE = "android.service.credentials.CredentialProviderService"; + } + + public final class CredentialsResponseContent implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.service.credentials.Action> getActions(); + method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries(); + method @Nullable public android.service.credentials.CredentialEntry getRemoteCredentialEntry(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.CredentialsResponseContent> CREATOR; + } + + public static final class CredentialsResponseContent.Builder { + ctor public CredentialsResponseContent.Builder(); + method @NonNull public android.service.credentials.CredentialsResponseContent.Builder addAction(@NonNull android.service.credentials.Action); + method @NonNull public android.service.credentials.CredentialsResponseContent.Builder addCredentialEntry(@NonNull android.service.credentials.CredentialEntry); + method @NonNull public android.service.credentials.CredentialsResponseContent build(); + method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setActions(@NonNull java.util.List<android.service.credentials.Action>); + method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setCredentialEntries(@NonNull java.util.List<android.service.credentials.CredentialEntry>); + method @NonNull public android.service.credentials.CredentialsResponseContent.Builder setRemoteCredentialEntry(@Nullable android.service.credentials.CredentialEntry); + } + + public final class GetCredentialsRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getCallingPackage(); + method @NonNull public java.util.List<android.credentials.GetCredentialOption> getGetCredentialOptions(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.GetCredentialsRequest> CREATOR; + } + + public static final class GetCredentialsRequest.Builder { + ctor public GetCredentialsRequest.Builder(@NonNull String); + method @NonNull public android.service.credentials.GetCredentialsRequest.Builder addGetCredentialOption(@NonNull android.credentials.GetCredentialOption); + method @NonNull public android.service.credentials.GetCredentialsRequest build(); + method @NonNull public android.service.credentials.GetCredentialsRequest.Builder setGetCredentialOptions(@NonNull java.util.List<android.credentials.GetCredentialOption>); + } + + public final class GetCredentialsResponse implements android.os.Parcelable { + method @NonNull public static android.service.credentials.GetCredentialsResponse createWithAuthentication(@NonNull android.service.credentials.Action); + method @NonNull public static android.service.credentials.GetCredentialsResponse createWithResponseContent(@NonNull android.service.credentials.CredentialsResponseContent); + method public int describeContents(); + method @Nullable public android.service.credentials.Action getAuthenticationAction(); + method @Nullable public android.service.credentials.CredentialsResponseContent getCredentialsResponseContent(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.credentials.GetCredentialsResponse> CREATOR; + } + +} + package android.service.dreams { public class DreamService extends android.app.Service implements android.view.Window.Callback { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index eac990d671cd..057c1ada8f00 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5534,6 +5534,7 @@ package android.hardware.usb { method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long); field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE"; field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; + field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_COMPLIANCE_CHANGED = "android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED"; field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END"; field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START"; @@ -5561,6 +5562,7 @@ package android.hardware.usb { method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USB) public android.hardware.usb.UsbPortStatus getStatus(); method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbPort(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setRoles(int, int); + method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public boolean supportsComplianceWarnings(); field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1; // 0x1 field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2; // 0x2 field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4; // 0x4 @@ -5586,6 +5588,7 @@ package android.hardware.usb { public final class UsbPortStatus implements android.os.Parcelable { method public int describeContents(); + method @CheckResult @NonNull public int[] getComplianceWarnings(); method public int getCurrentDataRole(); method public int getCurrentMode(); method public int getCurrentPowerRole(); @@ -5596,6 +5599,10 @@ package android.hardware.usb { method public boolean isPowerTransferLimited(); method public boolean isRoleCombinationSupported(int, int); method public void writeToParcel(android.os.Parcel, int); + field public static final int COMPLIANCE_WARNING_BC_1_2 = 3; // 0x3 + field public static final int COMPLIANCE_WARNING_DEBUG_ACCESSORY = 2; // 0x2 + field public static final int COMPLIANCE_WARNING_MISSING_RP = 4; // 0x4 + field public static final int COMPLIANCE_WARNING_OTHER = 1; // 0x1 field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR; field public static final int DATA_ROLE_DEVICE = 2; // 0x2 field public static final int DATA_ROLE_HOST = 1; // 0x1 @@ -6474,7 +6481,6 @@ package android.media { field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2 - field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_UNKNOWN = -1; // 0xffffffff field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_VOLUME_SHAPER = 32; // 0x20 field public static final int PLAYER_STATE_IDLE = 1; // 0x1 field public static final int PLAYER_STATE_PAUSED = 3; // 0x3 @@ -7269,6 +7275,7 @@ package android.media.tv.tuner { method @NonNull public java.util.List<android.media.tv.tuner.frontend.FrontendStatusReadiness> getFrontendStatusReadiness(@NonNull int[]); method @IntRange(from=0xffffffff) public int getMaxNumberOfFrontends(int); method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean hasUnusedFrontend(int); + method public boolean isLnaSupported(); method public boolean isLowestPriority(int); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler(); method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener); @@ -12818,14 +12825,14 @@ package android.telephony { method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy(); } - @Deprecated public final class CallAttributes implements android.os.Parcelable { - ctor @Deprecated public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality); - method @Deprecated public int describeContents(); - method @Deprecated @NonNull public android.telephony.CallQuality getCallQuality(); - method @Deprecated public int getNetworkType(); - method @Deprecated @NonNull public android.telephony.PreciseCallState getPreciseCallState(); - method @Deprecated public void writeToParcel(android.os.Parcel, int); - field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR; + public final class CallAttributes implements android.os.Parcelable { + ctor public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality); + method public int describeContents(); + method @NonNull public android.telephony.CallQuality getCallQuality(); + method public int getNetworkType(); + method @NonNull public android.telephony.PreciseCallState getPreciseCallState(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR; } public final class CallForwardingInfo implements android.os.Parcelable { @@ -12905,28 +12912,6 @@ package android.telephony { method @NonNull public android.telephony.CallQuality.Builder setUplinkCallQualityLevel(int); } - public final class CallState implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.telephony.CallQuality getCallQuality(); - method public int getCallState(); - method public int getImsCallServiceType(); - method @Nullable public String getImsCallSessionId(); - method public int getImsCallType(); - method public int getNetworkType(); - method public void writeToParcel(@Nullable android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallState> CREATOR; - } - - public static final class CallState.Builder { - ctor public CallState.Builder(int); - method @NonNull public android.telephony.CallState build(); - method @NonNull public android.telephony.CallState.Builder setCallQuality(@Nullable android.telephony.CallQuality); - method @NonNull public android.telephony.CallState.Builder setImsCallServiceType(int); - method @NonNull public android.telephony.CallState.Builder setImsCallSessionId(@Nullable String); - method @NonNull public android.telephony.CallState.Builder setImsCallType(int); - method @NonNull public android.telephony.CallState.Builder setNetworkType(int); - } - public class CarrierConfigManager { method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName(); method @NonNull public static android.os.PersistableBundle getDefaultConfig(); @@ -13677,8 +13662,7 @@ package android.telephony { } public static interface TelephonyCallback.CallAttributesListener { - method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public default void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public default void onCallStatesChanged(@NonNull java.util.List<android.telephony.CallState>); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); } public static interface TelephonyCallback.DataEnabledListener { @@ -14779,7 +14763,6 @@ package android.telephony.ims { field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3 field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0 field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1 - field public static final int CALL_TYPE_NONE = 0; // 0x0 field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3 field public static final int CALL_TYPE_VOICE = 2; // 0x2 field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1 @@ -16144,6 +16127,12 @@ package android.view.accessibility { public abstract class AccessibilityDisplayProxy { ctor public AccessibilityDisplayProxy(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.accessibilityservice.AccessibilityServiceInfo>); method public int getDisplayId(); + method @NonNull public final java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAndEnabledServices(); + method @NonNull public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); + method public void interrupt(); + method public void onAccessibilityEvent(@NonNull android.view.accessibility.AccessibilityEvent); + method public void onProxyConnected(); + method public void setInstalledAndEnabledServices(@NonNull java.util.List<android.accessibilityservice.AccessibilityServiceInfo>); } public final class AccessibilityManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 121741e07d43..35e01f1a9c7b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -459,6 +459,8 @@ package android.app { public class WallpaperManager { method @Nullable public android.graphics.Bitmap getBitmap(); + method @Nullable public android.graphics.Rect peekBitmapDimensions(); + method @Nullable public android.graphics.Rect peekBitmapDimensions(int); method public boolean shouldEnableWideColorGamut(); method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5aa8f1fda03c..884870bff3af 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -6426,23 +6426,28 @@ public final class ActivityThread extends ClientTransactionHandler } private void handleTrimMemory(int level) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory"); + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory: " + level); + } if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level); - if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { - PropertyInvalidatedCache.onTrimMemory(); - } + try { + if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { + PropertyInvalidatedCache.onTrimMemory(); + } - final ArrayList<ComponentCallbacks2> callbacks = - collectComponentCallbacks(true /* includeUiContexts */); + final ArrayList<ComponentCallbacks2> callbacks = + collectComponentCallbacks(true /* includeUiContexts */); - final int N = callbacks.size(); - for (int i = 0; i < N; i++) { - callbacks.get(i).onTrimMemory(level); + final int N = callbacks.size(); + for (int i = 0; i < N; i++) { + callbacks.get(i).onTrimMemory(level); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } WindowManagerGlobal.getInstance().trimMemory(level); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); if (SystemProperties.getInt("debug.am.run_gc_trim_level", Integer.MAX_VALUE) <= level) { unscheduleGcIdler(); diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java index 818bdc26523c..9bf8550df6d0 100644 --- a/core/java/android/app/ForegroundServiceTypePolicy.java +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -54,6 +54,7 @@ import android.content.pm.ServiceInfo.ForegroundServiceType; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; +import android.healthconnect.HealthConnectManager; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArraySet; @@ -66,8 +67,10 @@ import com.android.internal.util.ArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.HashMap; import java.util.Optional; +import java.util.Set; /** * This class enforces the policies around the foreground service types. @@ -655,11 +658,12 @@ public abstract class ForegroundServiceTypePolicy { * * For test only. */ - public @NonNull Optional<String[]> getRequiredAllOfPermissionsForTest() { + public @NonNull Optional<String[]> getRequiredAllOfPermissionsForTest( + @NonNull Context context) { if (mAllOfPermissions == null) { return Optional.empty(); } - return Optional.of(mAllOfPermissions.toStringArray()); + return Optional.of(mAllOfPermissions.toStringArray(context)); } /** @@ -668,11 +672,12 @@ public abstract class ForegroundServiceTypePolicy { * * For test only. */ - public @NonNull Optional<String[]> getRequiredAnyOfPermissionsForTest() { + public @NonNull Optional<String[]> getRequiredAnyOfPermissionsForTest( + @NonNull Context context) { if (mAnyOfPermissions == null) { return Optional.empty(); } - return Optional.of(mAnyOfPermissions.toStringArray()); + return Optional.of(mAnyOfPermissions.toStringArray(context)); } /** @@ -808,12 +813,12 @@ public abstract class ForegroundServiceTypePolicy { return sb.toString(); } - @NonNull String[] toStringArray() { - final String[] names = new String[mPermissions.length]; + @NonNull String[] toStringArray(Context context) { + final ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < mPermissions.length; i++) { - names[i] = mPermissions[i].mName; + mPermissions[i].addToList(context, list); } - return names; + return list.toArray(new String[list.size()]); } } @@ -826,7 +831,7 @@ public abstract class ForegroundServiceTypePolicy { /** * The name of this permission. */ - final @NonNull String mName; + protected final @NonNull String mName; /** * Constructor. @@ -846,6 +851,10 @@ public abstract class ForegroundServiceTypePolicy { public String toString() { return mName; } + + void addToList(@NonNull Context context, @NonNull ArrayList<String> list) { + list.add(mName); + } } /** @@ -859,15 +868,23 @@ public abstract class ForegroundServiceTypePolicy { @Override @SuppressLint("AndroidFrameworkRequiresPermission") @PackageManager.PermissionResult - public int checkPermission(Context context, int callerUid, int callerPid, + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, String packageName, boolean allowWhileInUse) { + return checkPermission(context, mName, callerUid, callerPid, packageName, + allowWhileInUse); + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + @PackageManager.PermissionResult + int checkPermission(@NonNull Context context, @NonNull String name, int callerUid, + int callerPid, String packageName, boolean allowWhileInUse) { // Simple case, check if it's already granted. - if (context.checkPermission(mName, callerPid, callerUid) == PERMISSION_GRANTED) { + if (context.checkPermission(name, callerPid, callerUid) == PERMISSION_GRANTED) { return PERMISSION_GRANTED; } if (allowWhileInUse) { // Check its appops - final int opCode = AppOpsManager.permissionToOpCode(mName); + final int opCode = AppOpsManager.permissionToOpCode(name); final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); if (opCode != AppOpsManager.OP_NONE) { final int currentMode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, callerUid, @@ -895,7 +912,7 @@ public abstract class ForegroundServiceTypePolicy { @Override @PackageManager.PermissionResult - public int checkPermission(Context context, int callerUid, int callerPid, + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, String packageName, boolean allowWhileInUse) { final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); final int mode = appOpsManager.unsafeCheckOpRawNoThrow(mOpCode, callerUid, packageName); @@ -915,7 +932,7 @@ public abstract class ForegroundServiceTypePolicy { @Override @SuppressLint("AndroidFrameworkRequiresPermission") @PackageManager.PermissionResult - public int checkPermission(Context context, int callerUid, int callerPid, + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, String packageName, boolean allowWhileInUse) { final UsbManager usbManager = context.getSystemService(UsbManager.class); final HashMap<String, UsbDevice> devices = usbManager.getDeviceList(); @@ -941,7 +958,7 @@ public abstract class ForegroundServiceTypePolicy { @Override @SuppressLint("AndroidFrameworkRequiresPermission") @PackageManager.PermissionResult - public int checkPermission(Context context, int callerUid, int callerPid, + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, String packageName, boolean allowWhileInUse) { final UsbManager usbManager = context.getSystemService(UsbManager.class); final UsbAccessory[] accessories = usbManager.getAccessoryList(); @@ -956,6 +973,45 @@ public abstract class ForegroundServiceTypePolicy { } } + static class HealthConnectPermission extends RegularPermission { + private @Nullable String[] mPermissionNames; + + HealthConnectPermission() { + super("Health Connect"); + } + + @Override + @SuppressLint("AndroidFrameworkRequiresPermission") + @PackageManager.PermissionResult + public int checkPermission(@NonNull Context context, int callerUid, int callerPid, + String packageName, boolean allowWhileInUse) { + final String[] perms = getPermissions(context); + for (String perm : perms) { + if (checkPermission(context, perm, callerUid, callerPid, + packageName, allowWhileInUse) == PERMISSION_GRANTED) { + return PERMISSION_GRANTED; + } + } + return PERMISSION_DENIED; + } + + @Override + void addToList(@NonNull Context context, @NonNull ArrayList<String> list) { + final String[] perms = getPermissions(context); + for (String perm : perms) { + list.add(perm); + } + } + + private @NonNull String[] getPermissions(@NonNull Context context) { + if (mPermissionNames != null) { + return mPermissionNames; + } + final Set<String> healthPerms = HealthConnectManager.getHealthPermissions(context); + return mPermissionNames = healthPerms.toArray(new String[healthPerms.size()]); + } + } + /** * The default policy for the foreground service types. * diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index e54a0841dd26..5d87012ec7e7 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -58,6 +58,7 @@ import android.companion.CompanionDeviceManager; import android.companion.ICompanionDeviceManager; import android.companion.virtual.IVirtualDeviceManager; import android.companion.virtual.VirtualDeviceManager; +import android.compat.Compatibility; import android.content.ClipboardManager; import android.content.ContentCaptureOptions; import android.content.Context; @@ -1092,7 +1093,10 @@ public final class SystemServiceRegistry { new CachedServiceFetcher<OverlayManager>() { @Override public OverlayManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.OVERLAY_SERVICE); + final IBinder b = + (Compatibility.isChangeEnabled(OverlayManager.SELF_TARGETING_OVERLAY)) + ? ServiceManager.getService(Context.OVERLAY_SERVICE) + : ServiceManager.getServiceOrThrow(Context.OVERLAY_SERVICE); return new OverlayManager(ctx, IOverlayManager.Stub.asInterface(b)); }}); diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index ef10c0b245d1..f133c8aa5899 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -201,6 +201,23 @@ "file_patterns": [ "(/|^)PropertyInvalidatedCache.java" ] + }, + { + "name": "FrameworksCoreGameManagerTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.app" + } + ], + "file_patterns": [ + "(/|^)GameManager[^/]*", "(/|^)GameMode[^/]*" + ] } ], "presubmit-large": [ diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 8685259217c5..f5d657c4b388 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -358,13 +358,37 @@ public class WallpaperManager { } } + /** + * Convenience class representing a cached wallpaper bitmap and associated data. + */ + private static class CachedWallpaper { + final Bitmap mCachedWallpaper; + final int mCachedWallpaperUserId; + @SetWallpaperFlags final int mWhich; + + CachedWallpaper(Bitmap cachedWallpaper, int cachedWallpaperUserId, + @SetWallpaperFlags int which) { + mCachedWallpaper = cachedWallpaper; + mCachedWallpaperUserId = cachedWallpaperUserId; + mWhich = which; + } + + /** + * Returns true if this object represents a valid cached bitmap for the given parameters, + * otherwise false. + */ + boolean isValid(int userId, @SetWallpaperFlags int which) { + return userId == mCachedWallpaperUserId && which == mWhich + && !mCachedWallpaper.isRecycled(); + } + } + private static class Globals extends IWallpaperManagerCallback.Stub { private final IWallpaperManager mService; private boolean mColorCallbackRegistered; private final ArrayList<Pair<OnColorsChangedListener, Handler>> mColorListeners = new ArrayList<>(); - private Bitmap mCachedWallpaper; - private int mCachedWallpaperUserId; + private CachedWallpaper mCachedWallpaper; private Bitmap mDefaultWallpaper; private Handler mMainLooperHandler; private ArrayMap<LocalWallpaperColorConsumer, ArraySet<RectF>> mLocalColorCallbackAreas = @@ -536,6 +560,15 @@ public class WallpaperManager { false /* hardware */, cmProxy); } + /** + * Retrieves the current wallpaper Bitmap, caching the result. If this fails and + * `returnDefault` is set, returns the Bitmap for the default wallpaper; otherwise returns + * null. + * + * More sophisticated caching might a) store and compare the wallpaper ID so that + * consecutive calls for FLAG_SYSTEM and FLAG_LOCK could return the cached wallpaper if + * no lock screen wallpaper is set, or b) separately cache home and lock screen wallpaper. + */ public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId, boolean hardware, ColorManagementProxy cmProxy) { @@ -549,16 +582,14 @@ public class WallpaperManager { } } synchronized (this) { - if (mCachedWallpaper != null && mCachedWallpaperUserId == userId - && !mCachedWallpaper.isRecycled()) { - return mCachedWallpaper; + if (mCachedWallpaper != null && mCachedWallpaper.isValid(userId, which)) { + return mCachedWallpaper.mCachedWallpaper; } mCachedWallpaper = null; - mCachedWallpaperUserId = 0; + Bitmap currentWallpaper = null; try { - mCachedWallpaper = getCurrentWallpaperLocked( - context, userId, hardware, cmProxy); - mCachedWallpaperUserId = userId; + currentWallpaper = getCurrentWallpaperLocked( + context, which, userId, hardware, cmProxy); } catch (OutOfMemoryError e) { Log.w(TAG, "Out of memory loading the current wallpaper: " + e); } catch (SecurityException e) { @@ -570,8 +601,9 @@ public class WallpaperManager { throw e; } } - if (mCachedWallpaper != null) { - return mCachedWallpaper; + if (currentWallpaper != null) { + mCachedWallpaper = new CachedWallpaper(currentWallpaper, userId, which); + return currentWallpaper; } } if (returnDefault) { @@ -587,7 +619,9 @@ public class WallpaperManager { return null; } - public Rect peekWallpaperDimensions(Context context, boolean returnDefault, int userId) { + @Nullable + public Rect peekWallpaperDimensions(Context context, boolean returnDefault, + @SetWallpaperFlags int which, int userId) { if (mService != null) { try { if (!mService.isWallpaperSupported(context.getOpPackageName())) { @@ -600,11 +634,10 @@ public class WallpaperManager { Rect dimensions = null; synchronized (this) { - ParcelFileDescriptor pfd = null; - try { - Bundle params = new Bundle(); - pfd = mService.getWallpaperWithFeature(context.getOpPackageName(), - context.getAttributionTag(), this, FLAG_SYSTEM, params, userId); + Bundle params = new Bundle(); + try (ParcelFileDescriptor pfd = mService.getWallpaperWithFeature( + context.getOpPackageName(), context.getAttributionTag(), this, which, + params, userId)) { // Let's peek user wallpaper first. if (pfd != null) { BitmapFactory.Options options = new BitmapFactory.Options(); @@ -614,19 +647,14 @@ public class WallpaperManager { } } catch (RemoteException ex) { Log.w(TAG, "peek wallpaper dimensions failed", ex); - } finally { - if (pfd != null) { - try { - pfd.close(); - } catch (IOException ignored) { - } - } + } catch (IOException ignored) { + // This is only thrown on close and can be safely ignored. } } // If user wallpaper is unavailable, may be the default one instead. if ((dimensions == null || dimensions.width() == 0 || dimensions.height() == 0) && returnDefault) { - InputStream is = openDefaultWallpaper(context, FLAG_SYSTEM); + InputStream is = openDefaultWallpaper(context, which); if (is != null) { try { BitmapFactory.Options options = new BitmapFactory.Options(); @@ -644,13 +672,12 @@ public class WallpaperManager { void forgetLoadedWallpaper() { synchronized (this) { mCachedWallpaper = null; - mCachedWallpaperUserId = 0; mDefaultWallpaper = null; } } - private Bitmap getCurrentWallpaperLocked(Context context, int userId, boolean hardware, - ColorManagementProxy cmProxy) { + private Bitmap getCurrentWallpaperLocked(Context context, @SetWallpaperFlags int which, + int userId, boolean hardware, ColorManagementProxy cmProxy) { if (mService == null) { Log.w(TAG, "WallpaperService not running"); return null; @@ -659,7 +686,7 @@ public class WallpaperManager { try { Bundle params = new Bundle(); ParcelFileDescriptor pfd = mService.getWallpaperWithFeature( - context.getOpPackageName(), context.getAttributionTag(), this, FLAG_SYSTEM, + context.getOpPackageName(), context.getAttributionTag(), this, which, params, userId); if (pfd != null) { @@ -1148,10 +1175,10 @@ public class WallpaperManager { * @return the dimensions of system wallpaper * @hide */ + @TestApi @Nullable public Rect peekBitmapDimensions() { - return sGlobals.peekWallpaperDimensions( - mContext, true /* returnDefault */, mContext.getUserId()); + return peekBitmapDimensions(FLAG_SYSTEM); } /** @@ -1162,9 +1189,12 @@ public class WallpaperManager { * @return the dimensions of system wallpaper * @hide */ + @TestApi @Nullable public Rect peekBitmapDimensions(@SetWallpaperFlags int which) { - return peekBitmapDimensions(); + checkExactlyOneWallpaperFlagSet(which); + return sGlobals.peekWallpaperDimensions(mContext, true /* returnDefault */, which, + mContext.getUserId()); } /** diff --git a/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java b/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java index ec10d8431e74..6b5e66758d94 100644 --- a/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java +++ b/core/java/android/app/time/LocationTimeZoneAlgorithmStatus.java @@ -19,6 +19,7 @@ package android.app.time; import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING; import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED; import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING; +import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN; import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusFromString; import static android.app.time.DetectorStatusTypes.detectionAlgorithmStatusToString; import static android.app.time.DetectorStatusTypes.requireValidDetectionAlgorithmStatus; @@ -319,6 +320,40 @@ public final class LocationTimeZoneAlgorithmStatus implements Parcelable { mSecondaryProviderStatus, mSecondaryProviderReportedStatus); } + /** + * Returns {@code true} if the algorithm status could allow the time zone detector to enter + * telephony fallback mode. + */ + public boolean couldEnableTelephonyFallback() { + if (mStatus == DETECTION_ALGORITHM_STATUS_UNKNOWN + || mStatus == DETECTION_ALGORITHM_STATUS_NOT_RUNNING + || mStatus == DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED) { + // This method is not expected to be called on objects with these statuses. Fallback + // should not be enabled if it is. + return false; + } + + // mStatus == DETECTOR_STATUS_RUNNING. + + boolean primarySuggestsFallback = false; + if (mPrimaryProviderStatus == PROVIDER_STATUS_NOT_PRESENT) { + primarySuggestsFallback = true; + } else if (mPrimaryProviderStatus == PROVIDER_STATUS_IS_UNCERTAIN + && mPrimaryProviderReportedStatus != null) { + primarySuggestsFallback = mPrimaryProviderReportedStatus.couldEnableTelephonyFallback(); + } + + boolean secondarySuggestsFallback = false; + if (mSecondaryProviderStatus == PROVIDER_STATUS_NOT_PRESENT) { + secondarySuggestsFallback = true; + } else if (mSecondaryProviderStatus == PROVIDER_STATUS_IS_UNCERTAIN + && mSecondaryProviderReportedStatus != null) { + secondarySuggestsFallback = + mSecondaryProviderReportedStatus.couldEnableTelephonyFallback(); + } + return primarySuggestsFallback && secondarySuggestsFallback; + } + /** @hide */ @VisibleForTesting @NonNull diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java index 94275aea0c4d..812f6b0fc34b 100644 --- a/core/java/android/content/om/OverlayManager.java +++ b/core/java/android/content/om/OverlayManager.java @@ -92,6 +92,21 @@ public class OverlayManager { private static final long THROW_SECURITY_EXCEPTIONS = 147340954; /** + * Applications can use OverlayManager to create overlays to overlay on itself resources. The + * overlay target is itself and the work range is only in caller application. + * + * <p>In {@link android.content.Context#getSystemService(String)}, it crashes because of {@link + * java.lang.NullPointerException} if the parameter is OverlayManager. if the self-targeting is + * enabled, the caller application can get the OverlayManager instance to use self-targeting + * functionality. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final long SELF_TARGETING_OVERLAY = 205919743; + + /** * Creates a new instance. * * @param context The current context in which to operate. diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 1fc6bdaa963a..7d9c64add492 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -61,4 +61,6 @@ interface IPackageInstallerSession { int getInstallFlags(); void requestUserPreapproval(in PackageInstaller.PreapprovalDetails details, in IntentSender statusReceiver); + + boolean isKeepApplicationEnabledSetting(); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index d7686e22756e..35518276ac09 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1717,6 +1717,18 @@ public class PackageInstaller { e.rethrowFromSystemServer(); } } + + /** + * @return {@code true} if this session will keep the existing application enabled setting + * after installation. + */ + public boolean isKeepApplicationEnabledSetting() { + try { + return mSession.isKeepApplicationEnabledSetting(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } /** @@ -1855,6 +1867,8 @@ public class PackageInstaller { public boolean forceQueryableOverride; /** {@hide} */ public int requireUserAction = USER_ACTION_UNSPECIFIED; + /** {@hide} */ + public boolean keepApplicationEnabledSetting = false; /** * Construct parameters for a new package install session. @@ -1899,6 +1913,7 @@ public class PackageInstaller { rollbackDataPolicy = source.readInt(); requireUserAction = source.readInt(); packageSource = source.readInt(); + keepApplicationEnabledSetting = source.readBoolean(); } /** {@hide} */ @@ -1929,6 +1944,7 @@ public class PackageInstaller { ret.rollbackDataPolicy = rollbackDataPolicy; ret.requireUserAction = requireUserAction; ret.packageSource = packageSource; + ret.keepApplicationEnabledSetting = keepApplicationEnabledSetting; return ret; } @@ -2415,6 +2431,14 @@ public class PackageInstaller { this.installScenario = installScenario; } + /** + * Request to keep the original application enabled setting. This will prevent the + * application from being enabled if it was previously in a disabled state. + */ + public void setKeepApplicationEnabledSetting() { + this.keepApplicationEnabledSetting = true; + } + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); @@ -2443,6 +2467,7 @@ public class PackageInstaller { pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode); pw.printPair("dataLoaderParams", dataLoaderParams); pw.printPair("rollbackDataPolicy", rollbackDataPolicy); + pw.printPair("keepApplicationEnabledSetting", keepApplicationEnabledSetting); pw.println(); } @@ -2483,6 +2508,7 @@ public class PackageInstaller { dest.writeInt(rollbackDataPolicy); dest.writeInt(requireUserAction); dest.writeInt(packageSource); + dest.writeBoolean(keepApplicationEnabledSetting); } public static final Parcelable.Creator<SessionParams> @@ -2684,6 +2710,9 @@ public class PackageInstaller { /** @hide */ public boolean isPreapprovalRequested; + /** @hide */ + public boolean keepApplicationEnabledSetting; + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public SessionInfo() { @@ -2737,6 +2766,7 @@ public class PackageInstaller { requireUserAction = source.readInt(); installerUid = source.readInt(); packageSource = source.readInt(); + keepApplicationEnabledSetting = source.readBoolean(); } /** @@ -3268,6 +3298,14 @@ public class PackageInstaller { return installerUid; } + /** + * Returns {@code true} if this session will keep the existing application enabled setting + * after installation. + */ + public boolean isKeepApplicationEnabledSetting() { + return keepApplicationEnabledSetting; + } + @Override public int describeContents() { return 0; @@ -3317,6 +3355,7 @@ public class PackageInstaller { dest.writeInt(requireUserAction); dest.writeInt(installerUid); dest.writeInt(packageSource); + dest.writeBoolean(keepApplicationEnabledSetting); } public static final Parcelable.Creator<SessionInfo> diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 485d04db82d5..88b5e021882a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1959,6 +1959,14 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_MISSING_SPLIT = -28; /** + * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS} + * if the new package targets a deprecated SDK version. + * + * @hide + */ + public static final int INSTALL_FAILED_DEPRECATED_SDK_VERSION = -29; + + /** * Installation parse return code: this is passed in the * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a * file, or does not end with the expected '.apk' extension. @@ -9618,6 +9626,7 @@ public abstract class PackageManager { case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED"; case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA"; case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT"; + case INSTALL_FAILED_DEPRECATED_SDK_VERSION: return "INSTALL_FAILED_DEPRECATED_SDK_VERSION"; case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE"; case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION"; case INSTALL_FAILED_PROCESS_NOT_DEFINED: return "INSTALL_FAILED_PROCESS_NOT_DEFINED"; @@ -9675,6 +9684,7 @@ public abstract class PackageManager { case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED; case INSTALL_FAILED_MISSING_SPLIT: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; case INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE: return PackageInstaller.STATUS_FAILURE_BLOCKED; + case INSTALL_FAILED_DEPRECATED_SDK_VERSION: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; default: return PackageInstaller.STATUS_FAILURE; } } diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 3b7ed07c41bc..9e6cf62f2397 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -308,7 +308,9 @@ public class ServiceInfo extends ComponentInfo * permissions: * {@link android.Manifest.permission#ACTIVITY_RECOGNITION}, * {@link android.Manifest.permission#BODY_SENSORS}, - * {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}. + * {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}, + * or one of the {@code "android.permission.health.*"} permissions defined in the + * {@link android.healthconnect.HealthPermissions}. */ @RequiresPermission( allOf = { @@ -424,7 +426,7 @@ public class ServiceInfo extends ComponentInfo * android:name=".MySpecialForegroundService" * android:foregroundServiceType="specialUse|foo"> * <property - * android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"" + * android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" * android:value="foo" * /> * </service> diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 6ce22422643e..ce6e1c7c676f 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -563,6 +563,9 @@ public class CompatibilityInfo implements Parcelable { if (applyToSize) { inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f); inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f); + + float fontScale = inoutDm.scaledDensity / inoutDm.density; + inoutDm.fontScaleConverter = FontScaleConverterFactory.forScale(fontScale); } } diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java new file mode 100644 index 000000000000..c7fdb16682e3 --- /dev/null +++ b/core/java/android/content/res/FontScaleConverter.java @@ -0,0 +1,148 @@ +/* + * 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 android.content.res; + +import android.annotation.NonNull; +import android.util.MathUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Arrays; + +/** + * A lookup table for non-linear font scaling. Converts font sizes given in "sp" dimensions to a + * "dp" dimension according to a non-linear curve. + * + * <p>This is meant to improve readability at larger font scales: larger fonts will scale up more + * slowly than smaller fonts, so we don't get ridiculously huge fonts that don't fit on the screen. + * + * <p>The thinking here is that large fonts are already big enough to read, but we still want to + * scale them slightly to preserve the visual hierarchy when compared to smaller fonts. + * + * @hide + */ +public class FontScaleConverter { + /** + * How close the given SP should be to a canonical SP in the array before they are considered + * the same for lookup purposes. + */ + private static final float THRESHOLD_FOR_MATCHING_SP = 0.02f; + + @VisibleForTesting + final float[] mFromSpValues; + @VisibleForTesting + final float[] mToDpValues; + + /** + * Creates a lookup table for the given conversions. + * + * <p>Any "sp" value not in the lookup table will be derived via linear interpolation. + * + * <p>The arrays must be sorted ascending and monotonically increasing. + * + * @param fromSp array of dimensions in SP + * @param toDp array of dimensions in DP that correspond to an SP value in fromSp + * + * @throws IllegalArgumentException if the array lengths don't match or are empty + * @hide + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public FontScaleConverter(@NonNull float[] fromSp, @NonNull float[] toDp) { + if (fromSp.length != toDp.length || fromSp.length == 0) { + throw new IllegalArgumentException("Array lengths must match and be nonzero"); + } + + mFromSpValues = fromSp; + mToDpValues = toDp; + } + + /** + * Convert a dimension in "sp" to "dp" using the lookup table. + * + * @hide + */ + public float convertSpToDp(float sp) { + final float spPositive = Math.abs(sp); + // TODO(b/247861374): find a match at a higher index? + final int spRounded = Math.round(spPositive); + final float sign = Math.signum(sp); + final int index = Arrays.binarySearch(mFromSpValues, spRounded); + if (index >= 0 && Math.abs(spRounded - spPositive) < THRESHOLD_FOR_MATCHING_SP) { + // exact match, return the matching dp + return sign * mToDpValues[index]; + } else { + // must be a value in between index and index + 1: interpolate. + final int lowerIndex = -(index + 1) - 1; + + final float startSp; + final float endSp; + final float startDp; + final float endDp; + + if (lowerIndex >= mFromSpValues.length - 1) { + // It's past our lookup table. Determine the last elements' scaling factor and use. + startSp = mFromSpValues[mFromSpValues.length - 1]; + startDp = mToDpValues[mFromSpValues.length - 1]; + + if (startSp == 0) return 0; + + final float scalingFactor = startDp / startSp; + return sp * scalingFactor; + } else if (lowerIndex == -1) { + // It's smaller than the smallest value in our table. Interpolate from 0. + startSp = 0; + startDp = 0; + endSp = mFromSpValues[0]; + endDp = mToDpValues[0]; + } else { + startSp = mFromSpValues[lowerIndex]; + endSp = mFromSpValues[lowerIndex + 1]; + startDp = mToDpValues[lowerIndex]; + endDp = mToDpValues[lowerIndex + 1]; + } + + return sign * MathUtils.constrainedMap(startDp, endDp, startSp, endSp, spPositive); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (!(o instanceof FontScaleConverter)) return false; + FontScaleConverter that = (FontScaleConverter) o; + return Arrays.equals(mFromSpValues, that.mFromSpValues) + && Arrays.equals(mToDpValues, that.mToDpValues); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(mFromSpValues); + result = 31 * result + Arrays.hashCode(mToDpValues); + return result; + } + + @Override + public String toString() { + return "FontScaleConverter{" + + "fromSpValues=" + + Arrays.toString(mFromSpValues) + + ", toDpValues=" + + Arrays.toString(mToDpValues) + + '}'; + } +} diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java new file mode 100644 index 000000000000..c77a372f1939 --- /dev/null +++ b/core/java/android/content/res/FontScaleConverterFactory.java @@ -0,0 +1,119 @@ +/* + * 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 android.content.res; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Stores lookup tables for creating {@link FontScaleConverter}s at various scales. + * + * @hide + */ +public class FontScaleConverterFactory { + private static final float SCALE_KEY_MULTIPLIER = 100f; + + @VisibleForTesting + static final SparseArray<FontScaleConverter> LOOKUP_TABLES = new SparseArray<>(); + + static { + // These were generated by frameworks/base/tools/fonts/font-scaling-array-generator.js and + // manually tweaked for optimum readability. + put( + /* scaleKey= */ 1.15f, + new FontScaleConverter( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] { 9.2f, 11.5f, 13.8f, 16.1f, 20.7f, 23f, 27.6f, 34.5f, 115}) + ); + + put( + /* scaleKey= */ 1.3f, + new FontScaleConverter( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] {10.4f, 13f, 15.6f, 18.2f, 23.4f, 26f, 31.2f, 39f, 130}) + ); + + put( + /* scaleKey= */ 1.5f, + new FontScaleConverter( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] { 12f, 15f, 18f, 21f, 27f, 30f, 36f, 45f, 150}) + ); + + put( + /* scaleKey= */ 1.8f, + new FontScaleConverter( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] {14.4f, 18f, 21.6f, 25.2f, 32.4f, 36f, 43.2f, 54f, 180}) + ); + + put( + /* scaleKey= */ 2f, + new FontScaleConverter( + /* fromSp= */ + new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100}, + /* toDp= */ + new float[] { 16f, 20f, 24f, 28f, 36f, 40f, 48f, 60f, 200}) + ); + + } + + private FontScaleConverterFactory() {} + + /** + * Finds a matching FontScaleConverter for the given fontScale factor. + * + * @param fontScale the scale factor, usually from {@link Configuration#fontScale}. + * + * @return a converter for the given scale, or null if non-linear scaling should not be used. + * + * @hide + */ + @Nullable + public static FontScaleConverter forScale(float fontScale) { + if (fontScale <= 1) { + // We don't need non-linear curves for shrinking text or for 100%. + // Also, fontScale==0 should not have a curve either + return null; + } + + FontScaleConverter lookupTable = get(fontScale); + // TODO(b/247861716): interpolate between two tables when null + + return lookupTable; + } + + private static void put(float scaleKey, @NonNull FontScaleConverter fontScaleConverter) { + LOOKUP_TABLES.put((int) (scaleKey * SCALE_KEY_MULTIPLIER), fontScaleConverter); + } + + @Nullable + private static FontScaleConverter get(float scaleKey) { + return LOOKUP_TABLES.get((int) (scaleKey * SCALE_KEY_MULTIPLIER)); + } +} diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 09d24d47cc7a..c2b37694c0c3 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -434,6 +434,8 @@ public class ResourcesImpl { // Protect against an unset fontScale. mMetrics.scaledDensity = mMetrics.density * (mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f); + mMetrics.fontScaleConverter = + FontScaleConverterFactory.forScale(mConfiguration.fontScale); final int width, height; if (mMetrics.widthPixels >= mMetrics.heightPixels) { diff --git a/telephony/java/android/telephony/CallState.aidl b/core/java/android/credentials/ClearCredentialStateRequest.aidl index dd5af8e65921..2679ee45c313 100644 --- a/telephony/java/android/telephony/CallState.aidl +++ b/core/java/android/credentials/ClearCredentialStateRequest.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 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. @@ -14,7 +14,6 @@ * limitations under the License. */ -package android.telephony; - -parcelable CallState; +package android.credentials; +parcelable ClearCredentialStateRequest;
\ No newline at end of file diff --git a/core/java/android/credentials/ClearCredentialStateRequest.java b/core/java/android/credentials/ClearCredentialStateRequest.java new file mode 100644 index 000000000000..33afbed73815 --- /dev/null +++ b/core/java/android/credentials/ClearCredentialStateRequest.java @@ -0,0 +1,85 @@ +/* + * Copyright 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 android.credentials; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.AnnotationValidations; + +/** + * A request class for clearing a user's credential state from the credential providers. + */ +public final class ClearCredentialStateRequest implements Parcelable { + + /** The request data. */ + @NonNull + private final Bundle mData; + + /** Returns the request data. */ + @NonNull + public Bundle getData() { + return mData; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBundle(mData); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "ClearCredentialStateRequest {data=" + mData + "}"; + } + + /** + * Constructs a {@link ClearCredentialStateRequest}. + * + * @param data the request data + */ + public ClearCredentialStateRequest(@NonNull Bundle data) { + mData = requireNonNull(data, "data must not be null"); + } + + private ClearCredentialStateRequest(@NonNull Parcel in) { + Bundle data = in.readBundle(); + mData = data; + AnnotationValidations.validate(NonNull.class, null, mData); + } + + public static final @NonNull Creator<ClearCredentialStateRequest> CREATOR = + new Creator<ClearCredentialStateRequest>() { + @Override + public ClearCredentialStateRequest[] newArray(int size) { + return new ClearCredentialStateRequest[size]; + } + + @Override + public ClearCredentialStateRequest createFromParcel(@NonNull Parcel in) { + return new ClearCredentialStateRequest(in); + } + }; +} diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index 04d57ad8993f..1efac6c2f332 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -22,6 +22,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; +import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.IntentSender; @@ -65,17 +66,20 @@ public final class CredentialManager { * credential, display a picker when multiple credentials exist, etc. * * @param request the request specifying type(s) of credentials to get from the user + * @param activity the activity used to launch any UI needed * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails */ public void executeGetCredential( @NonNull GetCredentialRequest request, + @NonNull Activity activity, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver< GetCredentialResponse, CredentialManagerException> callback) { requireNonNull(request, "request must not be null"); + requireNonNull(activity, "activity must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); @@ -88,8 +92,7 @@ public final class CredentialManager { try { cancelRemote = mService.executeGetCredential( request, - // TODO: use a real activity instead of context. - new GetCredentialTransport(mContext, executor, callback), + new GetCredentialTransport(activity, executor, callback), mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -107,17 +110,20 @@ public final class CredentialManager { * or storing the new credential, etc. * * @param request the request specifying type(s) of credentials to get from the user + * @param activity the activity used to launch any UI needed * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails */ public void executeCreateCredential( @NonNull CreateCredentialRequest request, + @NonNull Activity activity, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver< CreateCredentialResponse, CredentialManagerException> callback) { requireNonNull(request, "request must not be null"); + requireNonNull(activity, "activity must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); @@ -129,8 +135,7 @@ public final class CredentialManager { ICancellationSignal cancelRemote = null; try { cancelRemote = mService.executeCreateCredential(request, - // TODO: use a real activity instead of context. - new CreateCredentialTransport(mContext, executor, callback), + new CreateCredentialTransport(activity, executor, callback), mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -142,18 +147,24 @@ public final class CredentialManager { } /** - * Clears the current user credential session from all credential providers. + * Clears the current user credential state from all credential providers. * - * <p>Usually invoked after your user signs out of your app so that they will not be - * automatically signed in the next time. + * You should invoked this api after your user signs out of your app to notify all credential + * providers that any stored credential session for the given app should be cleared. * + * A credential provider may have stored an active credential session and use it to limit + * sign-in options for future get-credential calls. For example, it may prioritize the active + * credential over any other available credential. When your user explicitly signs out of your + * app and in order to get the holistic sign-in options the next time, you should call this API + * to let the provider clear any stored credential session. + * + * @param request the request data * @param cancellationSignal an optional signal that allows for cancelling this call * @param executor the callback will take place on this {@link Executor} * @param callback the callback invoked when the request succeeds or fails - * - * @hide */ - public void clearCredentialSession( + public void clearCredentialState( + @NonNull ClearCredentialStateRequest request, @Nullable CancellationSignal cancellationSignal, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<Void, CredentialManagerException> callback) { @@ -167,8 +178,8 @@ public final class CredentialManager { ICancellationSignal cancelRemote = null; try { - cancelRemote = mService.clearCredentialSession( - new ClearCredentialSessionTransport(executor, callback), + cancelRemote = mService.clearCredentialState(request, + new ClearCredentialStateTransport(executor, callback), mContext.getOpPackageName()); } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -182,14 +193,14 @@ public final class CredentialManager { private static class GetCredentialTransport extends IGetCredentialCallback.Stub { // TODO: listen for cancellation to release callback. - private final Context mActivityContext; + private final Activity mActivity; private final Executor mExecutor; private final OutcomeReceiver< GetCredentialResponse, CredentialManagerException> mCallback; - private GetCredentialTransport(Context activityContext, Executor executor, + private GetCredentialTransport(Activity activity, Executor executor, OutcomeReceiver<GetCredentialResponse, CredentialManagerException> callback) { - mActivityContext = activityContext; + mActivity = activity; mExecutor = executor; mCallback = callback; } @@ -197,7 +208,7 @@ public final class CredentialManager { @Override public void onPendingIntent(PendingIntent pendingIntent) { try { - mActivityContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0); + mActivity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(), e); @@ -220,14 +231,14 @@ public final class CredentialManager { private static class CreateCredentialTransport extends ICreateCredentialCallback.Stub { // TODO: listen for cancellation to release callback. - private final Context mActivityContext; + private final Activity mActivity; private final Executor mExecutor; private final OutcomeReceiver< CreateCredentialResponse, CredentialManagerException> mCallback; - private CreateCredentialTransport(Context activityContext, Executor executor, + private CreateCredentialTransport(Activity activity, Executor executor, OutcomeReceiver<CreateCredentialResponse, CredentialManagerException> callback) { - mActivityContext = activityContext; + mActivity = activity; mExecutor = executor; mCallback = callback; } @@ -235,7 +246,7 @@ public final class CredentialManager { @Override public void onPendingIntent(PendingIntent pendingIntent) { try { - mActivityContext.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0); + mActivity.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "startIntentSender() failed for intent:" + pendingIntent.getIntentSender(), e); @@ -255,14 +266,14 @@ public final class CredentialManager { } } - private static class ClearCredentialSessionTransport - extends IClearCredentialSessionCallback.Stub { + private static class ClearCredentialStateTransport + extends IClearCredentialStateCallback.Stub { // TODO: listen for cancellation to release callback. private final Executor mExecutor; private final OutcomeReceiver<Void, CredentialManagerException> mCallback; - private ClearCredentialSessionTransport(Executor executor, + private ClearCredentialStateTransport(Executor executor, OutcomeReceiver<Void, CredentialManagerException> callback) { mExecutor = executor; mCallback = callback; diff --git a/core/java/android/credentials/IClearCredentialSessionCallback.aidl b/core/java/android/credentials/IClearCredentialStateCallback.aidl index 903e7f56cd2e..f8b7ae440680 100644 --- a/core/java/android/credentials/IClearCredentialSessionCallback.aidl +++ b/core/java/android/credentials/IClearCredentialStateCallback.aidl @@ -17,11 +17,11 @@ package android.credentials; /** - * Listener for clearCredentialSession request. + * Listener for clearCredentialState request. * * @hide */ -interface IClearCredentialSessionCallback { +interface IClearCredentialStateCallback { oneway void onSuccess(); oneway void onError(int errorCode, String message); }
\ No newline at end of file diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl index 35688d7fe113..c5497bdbdc0f 100644 --- a/core/java/android/credentials/ICredentialManager.aidl +++ b/core/java/android/credentials/ICredentialManager.aidl @@ -16,9 +16,10 @@ package android.credentials; +import android.credentials.ClearCredentialStateRequest; import android.credentials.CreateCredentialRequest; import android.credentials.GetCredentialRequest; -import android.credentials.IClearCredentialSessionCallback; +import android.credentials.IClearCredentialStateCallback; import android.credentials.ICreateCredentialCallback; import android.credentials.IGetCredentialCallback; import android.os.ICancellationSignal; @@ -34,5 +35,5 @@ interface ICredentialManager { @nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage); - @nullable ICancellationSignal clearCredentialSession(in IClearCredentialSessionCallback callback, String callingPackage); + @nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage); } diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java index 19655edf9fb2..7fc282c141ba 100644 --- a/core/java/android/hardware/usb/ParcelableUsbPort.java +++ b/core/java/android/hardware/usb/ParcelableUsbPort.java @@ -34,11 +34,13 @@ public final class ParcelableUsbPort implements Parcelable { private final int mSupportedContaminantProtectionModes; private final boolean mSupportsEnableContaminantPresenceProtection; private final boolean mSupportsEnableContaminantPresenceDetection; + private final boolean mSupportsComplianceWarnings; private ParcelableUsbPort(@NonNull String id, int supportedModes, int supportedContaminantProtectionModes, boolean supportsEnableContaminantPresenceProtection, - boolean supportsEnableContaminantPresenceDetection) { + boolean supportsEnableContaminantPresenceDetection, + boolean supportsComplianceWarnings) { mId = id; mSupportedModes = supportedModes; mSupportedContaminantProtectionModes = supportedContaminantProtectionModes; @@ -46,6 +48,8 @@ public final class ParcelableUsbPort implements Parcelable { supportsEnableContaminantPresenceProtection; mSupportsEnableContaminantPresenceDetection = supportsEnableContaminantPresenceDetection; + mSupportsComplianceWarnings = + supportsComplianceWarnings; } /** @@ -59,7 +63,8 @@ public final class ParcelableUsbPort implements Parcelable { return new ParcelableUsbPort(port.getId(), port.getSupportedModes(), port.getSupportedContaminantProtectionModes(), port.supportsEnableContaminantPresenceProtection(), - port.supportsEnableContaminantPresenceDetection()); + port.supportsEnableContaminantPresenceDetection(), + port.supportsComplianceWarnings()); } /** @@ -72,7 +77,8 @@ public final class ParcelableUsbPort implements Parcelable { public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) { return new UsbPort(usbManager, mId, mSupportedModes, mSupportedContaminantProtectionModes, mSupportsEnableContaminantPresenceProtection, - mSupportsEnableContaminantPresenceDetection); + mSupportsEnableContaminantPresenceDetection, + mSupportsComplianceWarnings); } @Override @@ -87,6 +93,7 @@ public final class ParcelableUsbPort implements Parcelable { dest.writeInt(mSupportedContaminantProtectionModes); dest.writeBoolean(mSupportsEnableContaminantPresenceProtection); dest.writeBoolean(mSupportsEnableContaminantPresenceDetection); + dest.writeBoolean(mSupportsComplianceWarnings); } public static final @android.annotation.NonNull Creator<ParcelableUsbPort> CREATOR = @@ -98,11 +105,13 @@ public final class ParcelableUsbPort implements Parcelable { int supportedContaminantProtectionModes = in.readInt(); boolean supportsEnableContaminantPresenceProtection = in.readBoolean(); boolean supportsEnableContaminantPresenceDetection = in.readBoolean(); + boolean supportsComplianceWarnings = in.readBoolean(); return new ParcelableUsbPort(id, supportedModes, supportedContaminantProtectionModes, supportsEnableContaminantPresenceProtection, - supportsEnableContaminantPresenceDetection); + supportsEnableContaminantPresenceDetection, + supportsComplianceWarnings); } @Override diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 50dd0064a5cb..342c33665b02 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -113,6 +113,19 @@ public class UsbManager { public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; + /** + * Broadcast Action: A broadcast for USB compliance warning changes. + * + * This intent is sent when a port partner's + * (USB power source/cable/accessory) compliance warnings change state. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_USB) + public static final String ACTION_USB_PORT_COMPLIANCE_CHANGED = + "android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED"; + /** * Activity intent sent when user attaches a USB device. * diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java index 7c5a4c6d2b87..e0f9cad0835a 100644 --- a/core/java/android/hardware/usb/UsbPort.java +++ b/core/java/android/hardware/usb/UsbPort.java @@ -46,6 +46,10 @@ import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_CONTAMINAN import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DOCK; import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_FORCE; import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DEBUG; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_BC_1_2; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP; +import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_OTHER; import android.Manifest; import android.annotation.CallbackExecutor; @@ -83,6 +87,7 @@ public final class UsbPort { private final int mSupportedContaminantProtectionModes; private final boolean mSupportsEnableContaminantPresenceProtection; private final boolean mSupportsEnableContaminantPresenceDetection; + private final boolean mSupportsComplianceWarnings; private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES; /** @@ -250,6 +255,18 @@ public final class UsbPort { int supportedContaminantProtectionModes, boolean supportsEnableContaminantPresenceProtection, boolean supportsEnableContaminantPresenceDetection) { + this(usbManager, id, supportedModes, supportedContaminantProtectionModes, + supportsEnableContaminantPresenceProtection, + supportsEnableContaminantPresenceDetection, + false); + } + + /** @hide */ + public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes, + int supportedContaminantProtectionModes, + boolean supportsEnableContaminantPresenceProtection, + boolean supportsEnableContaminantPresenceDetection, + boolean supportsComplianceWarnings) { Objects.requireNonNull(id); Preconditions.checkFlagsArgument(supportedModes, MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY); @@ -262,6 +279,7 @@ public final class UsbPort { supportsEnableContaminantPresenceProtection; mSupportsEnableContaminantPresenceDetection = supportsEnableContaminantPresenceDetection; + mSupportsComplianceWarnings = supportsComplianceWarnings; } /** @@ -331,6 +349,21 @@ public final class UsbPort { } /** + * Queries USB Port to see if the port is capable of identifying + * non compliant USB power source/cable/accessory. + * + * @return true when the UsbPort is capable of identifying + * non compliant USB power + * source/cable/accessory. + * @return false otherwise. + */ + @CheckResult + @RequiresPermission(Manifest.permission.MANAGE_USB) + public boolean supportsComplianceWarnings() { + return mSupportsComplianceWarnings; + } + + /** * Sets the desired role combination of the port. * <p> * The supported role combinations depend on what is connected to the port and may be @@ -686,6 +719,37 @@ public final class UsbPort { } /** @hide */ + public static String complianceWarningsToString(@NonNull int[] complianceWarnings) { + StringBuilder complianceWarningString = new StringBuilder(); + complianceWarningString.append("["); + + if (complianceWarnings != null) { + for (int warning : complianceWarnings) { + switch (warning) { + case UsbPortStatus.COMPLIANCE_WARNING_OTHER: + complianceWarningString.append("other, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: + complianceWarningString.append("debug accessory, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_BC_1_2: + complianceWarningString.append("bc12, "); + break; + case UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP: + complianceWarningString.append("missing rp, "); + break; + default: + complianceWarningString.append(String.format("Unknown(%d), ", warning)); + break; + } + } + } + + complianceWarningString.append("]"); + return complianceWarningString.toString().replaceAll(", ]$", "]"); + } + + /** @hide */ public static void checkMode(int powerRole) { Preconditions.checkArgumentInRange(powerRole, Constants.PortMode.NONE, Constants.PortMode.NUM_MODES - 1, "portMode"); @@ -720,10 +784,12 @@ public final class UsbPort { @Override public String toString() { return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) - + "supportedContaminantProtectionModes=" + mSupportedContaminantProtectionModes - + "supportsEnableContaminantPresenceProtection=" + + ", supportedContaminantProtectionModes=" + mSupportedContaminantProtectionModes + + ", supportsEnableContaminantPresenceProtection=" + mSupportsEnableContaminantPresenceProtection - + "supportsEnableContaminantPresenceDetection=" - + mSupportsEnableContaminantPresenceDetection; + + ", supportsEnableContaminantPresenceDetection=" + + mSupportsEnableContaminantPresenceDetection + + ", supportsComplianceWarnings=" + + mSupportsComplianceWarnings; } } diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java index 3221ec8577ac..ed3e40d6b04c 100644 --- a/core/java/android/hardware/usb/UsbPortStatus.java +++ b/core/java/android/hardware/usb/UsbPortStatus.java @@ -16,9 +16,11 @@ package android.hardware.usb; +import android.Manifest; +import android.annotation.CheckResult; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -46,6 +48,7 @@ public final class UsbPortStatus implements Parcelable { private final boolean mPowerTransferLimited; private final @UsbDataStatus int mUsbDataStatus; private final @PowerBrickConnectionStatus int mPowerBrickConnectionStatus; + private final @NonNull @ComplianceWarning int[] mComplianceWarnings; /** * Power role: This USB port does not have a power role. @@ -246,6 +249,41 @@ public final class UsbPortStatus implements Parcelable { */ public static final int POWER_BRICK_STATUS_DISCONNECTED = 2; + /** + * Used to indicate attached sources/cables/accessories/ports + * that do not match the other warnings below and do not meet the + * requirements of specifications including but not limited to + * USB Type-C Cable and Connector, Universal Serial Bus + * Power Delivery, and Universal Serial Bus 1.x/2.0/3.x/4.0. + * In addition, constants introduced after the target sdk will be + * remapped into COMPLIANCE_WARNING_OTHER. + */ + public static final int COMPLIANCE_WARNING_OTHER = 1; + + /** + * Used to indicate Type-C port partner + * (cable/accessory/source) that identifies itself as debug + * accessory source as defined in USB Type-C Cable and + * Connector Specification. However, the specification states + * that this is meant for debug only and shall not be used for + * with commercial products. + */ + public static final int COMPLIANCE_WARNING_DEBUG_ACCESSORY = 2; + + /** + * Used to indicate USB ports that does not + * identify itself as one of the charging port types (SDP/CDP + * DCP etc) as defined by Battery Charging v1.2 Specification. + */ + public static final int COMPLIANCE_WARNING_BC_1_2 = 3; + + /** + * Used to indicate Type-C sources/cables that are missing pull + * up resistors on the CC pins as required by USB Type-C Cable + * and Connector Specification. + */ + public static final int COMPLIANCE_WARNING_MISSING_RP = 4; + @IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = { CONTAMINANT_DETECTION_NOT_SUPPORTED, CONTAMINANT_DETECTION_DISABLED, @@ -275,6 +313,15 @@ public final class UsbPortStatus implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface UsbPortMode{} + @IntDef(prefix = { "COMPLIANCE_WARNING_" }, value = { + COMPLIANCE_WARNING_OTHER, + COMPLIANCE_WARNING_DEBUG_ACCESSORY, + COMPLIANCE_WARNING_BC_1_2, + COMPLIANCE_WARNING_MISSING_RP, + }) + @Retention(RetentionPolicy.SOURCE) + @interface ComplianceWarning{} + /** @hide */ @IntDef(prefix = { "DATA_STATUS_" }, flag = true, value = { DATA_STATUS_UNKNOWN, @@ -302,7 +349,8 @@ public final class UsbPortStatus implements Parcelable { int supportedRoleCombinations, int contaminantProtectionStatus, int contaminantDetectionStatus, @UsbDataStatus int usbDataStatus, boolean powerTransferLimited, - @PowerBrickConnectionStatus int powerBrickConnectionStatus) { + @PowerBrickConnectionStatus int powerBrickConnectionStatus, + @NonNull @ComplianceWarning int[] complianceWarnings) { mCurrentMode = currentMode; mCurrentPowerRole = currentPowerRole; mCurrentDataRole = currentDataRole; @@ -312,21 +360,29 @@ public final class UsbPortStatus implements Parcelable { mUsbDataStatus = usbDataStatus; mPowerTransferLimited = powerTransferLimited; mPowerBrickConnectionStatus = powerBrickConnectionStatus; + mComplianceWarnings = complianceWarnings; + } + + /** @hide */ + public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole, + int supportedRoleCombinations, int contaminantProtectionStatus, + int contaminantDetectionStatus, @UsbDataStatus int usbDataStatus, + boolean powerTransferLimited, + @PowerBrickConnectionStatus int powerBrickConnectionStatus) { + this(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, + contaminantProtectionStatus, contaminantDetectionStatus, + usbDataStatus, powerTransferLimited, powerBrickConnectionStatus, + new int[] {}); } /** @hide */ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, int contaminantDetectionStatus) { - mCurrentMode = currentMode; - mCurrentPowerRole = currentPowerRole; - mCurrentDataRole = currentDataRole; - mSupportedRoleCombinations = supportedRoleCombinations; - mContaminantProtectionStatus = contaminantProtectionStatus; - mContaminantDetectionStatus = contaminantDetectionStatus; - mUsbDataStatus = DATA_STATUS_UNKNOWN; - mPowerBrickConnectionStatus = POWER_BRICK_STATUS_UNKNOWN; - mPowerTransferLimited = false; + this(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, + contaminantProtectionStatus, contaminantDetectionStatus, + DATA_STATUS_UNKNOWN, false, POWER_BRICK_STATUS_UNKNOWN, + new int[] {}); } /** @@ -443,6 +499,21 @@ public final class UsbPortStatus implements Parcelable { return mPowerBrickConnectionStatus; } + /** + * Returns non compliant reasons, if any, for the connected + * charger/cable/accessory/USB port. + * + * @return array including {@link #NON_COMPLIANT_REASON_DEBUG_ACCESSORY}, + * {@link #NON_COMPLIANT_REASON_BC12}, + * {@link #NON_COMPLIANT_REASON_MISSING_RP}, + * or {@link #NON_COMPLIANT_REASON_TYPEC} + */ + @CheckResult + @NonNull + public @ComplianceWarning int[] getComplianceWarnings() { + return mComplianceWarnings; + } + @NonNull @Override public String toString() { @@ -460,9 +531,11 @@ public final class UsbPortStatus implements Parcelable { + UsbPort.usbDataStatusToString(getUsbDataStatus()) + ", isPowerTransferLimited=" + isPowerTransferLimited() - +", powerBrickConnectionStatus=" + + ", powerBrickConnectionStatus=" + UsbPort .powerBrickConnectionStatusToString(getPowerBrickConnectionStatus()) + + ", complianceWarnings=" + + UsbPort.complianceWarningsToString(getComplianceWarnings()) + "}"; } @@ -482,6 +555,7 @@ public final class UsbPortStatus implements Parcelable { dest.writeInt(mUsbDataStatus); dest.writeBoolean(mPowerTransferLimited); dest.writeInt(mPowerBrickConnectionStatus); + dest.writeIntArray(mComplianceWarnings); } public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR = @@ -497,10 +571,12 @@ public final class UsbPortStatus implements Parcelable { int usbDataStatus = in.readInt(); boolean powerTransferLimited = in.readBoolean(); int powerBrickConnectionStatus = in.readInt(); + @ComplianceWarning int[] complianceWarnings = in.createIntArray(); return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, contaminantProtectionStatus, contaminantDetectionStatus, usbDataStatus, powerTransferLimited, - powerBrickConnectionStatus); + powerBrickConnectionStatus, + complianceWarnings); } @Override @@ -524,6 +600,7 @@ public final class UsbPortStatus implements Parcelable { private boolean mPowerTransferLimited; private @UsbDataStatus int mUsbDataStatus; private @PowerBrickConnectionStatus int mPowerBrickConnectionStatus; + private @ComplianceWarning int[] mComplianceWarnings; public Builder() { mCurrentMode = MODE_NONE; @@ -533,6 +610,7 @@ public final class UsbPortStatus implements Parcelable { mContaminantDetectionStatus = CONTAMINANT_DETECTION_NOT_SUPPORTED; mUsbDataStatus = DATA_STATUS_UNKNOWN; mPowerBrickConnectionStatus = POWER_BRICK_STATUS_UNKNOWN; + mComplianceWarnings = new int[] {}; } /** @@ -619,6 +697,20 @@ public final class UsbPortStatus implements Parcelable { } /** + * Sets the non-compliant charger reasons of {@link UsbPortStatus} + * + * @return Instance of {@link Builder} + */ + @NonNull + public Builder setComplianceWarnings( + @NonNull int[] complianceWarnings) { + mComplianceWarnings = complianceWarnings == null ? new int[] {} : + complianceWarnings; + return this; + } + + + /** * Creates the {@link UsbPortStatus} object. */ @NonNull @@ -626,7 +718,7 @@ public final class UsbPortStatus implements Parcelable { UsbPortStatus status = new UsbPortStatus(mCurrentMode, mCurrentPowerRole, mCurrentDataRole, mSupportedRoleCombinations, mContaminantProtectionStatus, mContaminantDetectionStatus, mUsbDataStatus, mPowerTransferLimited, - mPowerBrickConnectionStatus); + mPowerBrickConnectionStatus, mComplianceWarnings); return status; } }; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 630ad6c31560..2adbbcd454e5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -708,7 +708,7 @@ public final class Settings { "android.settings.WIFI_SETTINGS"; /** - * Activity Action: Show settings to allow configuration of MTE. + * Activity Action: Show settings to allow configuration of Advanced memory protection. * <p> * Memory Tagging Extension (MTE) is a CPU extension that allows to protect against certain * classes of security problems at a small runtime performance cost overhead. @@ -720,8 +720,8 @@ public final class Settings { * Output: Nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_MEMTAG_SETTINGS = - "android.settings.MEMTAG_SETTINGS"; + public static final String ACTION_ADVANCED_MEMORY_PROTECTION_SETTINGS = + "android.settings.ADVANCED_MEMORY_PROTECTION_SETTINGS"; /** * Activity Action: Show settings to allow configuration of a static IP diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java index 553a32419533..77570813e6c3 100644 --- a/core/java/android/service/credentials/Action.java +++ b/core/java/android/service/credentials/Action.java @@ -27,8 +27,6 @@ import java.util.Objects; /** * An action defined by the provider that intents into the provider's app for specific * user actions. - * - * @hide */ public final class Action implements Parcelable { /** Slice object containing display content to be displayed with this action on the UI. */ @@ -39,6 +37,13 @@ public final class Action implements Parcelable { /** * Constructs an action to be displayed on the UI. * + * <p> Actions must be used for any provider related operations, such as opening the provider + * app, intenting straight into certain app activities like 'manage credentials', top + * level authentication before displaying any content etc. + * + * <p> See details on usage of {@code Action} for various actionable entries in + * {@link BeginCreateCredentialResponse} and {@link GetCredentialsResponse}. + * * @param slice the display content to be displayed on the UI, along with this action * @param pendingIntent the intent to be invoked when the user selects this action */ diff --git a/core/java/android/service/credentials/BeginCreateCredentialRequest.aidl b/core/java/android/service/credentials/BeginCreateCredentialRequest.aidl new file mode 100644 index 000000000000..30cab8df443b --- /dev/null +++ b/core/java/android/service/credentials/BeginCreateCredentialRequest.aidl @@ -0,0 +1,3 @@ +package android.service.credentials; + +parcelable BeginCreateCredentialRequest;
\ No newline at end of file diff --git a/core/java/android/service/credentials/BeginCreateCredentialRequest.java b/core/java/android/service/credentials/BeginCreateCredentialRequest.java new file mode 100644 index 000000000000..1918d8c88ccc --- /dev/null +++ b/core/java/android/service/credentials/BeginCreateCredentialRequest.java @@ -0,0 +1,102 @@ +/* + * 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 android.service.credentials; + +import android.annotation.NonNull; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.Objects; + +/** + * Request for beginning a create credential request. + * + * See {@link BeginCreateCredentialResponse} for the counterpart response + */ +public final class BeginCreateCredentialRequest implements Parcelable { + private final @NonNull String mCallingPackage; + private final @NonNull String mType; + private final @NonNull Bundle mData; + + /** + * Constructs a new instance. + * + * @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is + * null or empty. + * @throws NullPointerException If {@code data} is null. + */ + public BeginCreateCredentialRequest(@NonNull String callingPackage, + @NonNull String type, @NonNull Bundle data) { + mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage, + "callingPackage must not be null or empty"); + mType = Preconditions.checkStringNotEmpty(type, + "type must not be null or empty"); + mData = Objects.requireNonNull(data, "data must not be null"); + } + + private BeginCreateCredentialRequest(@NonNull Parcel in) { + mCallingPackage = in.readString8(); + mType = in.readString8(); + mData = in.readBundle(Bundle.class.getClassLoader()); + } + + public static final @NonNull Creator<BeginCreateCredentialRequest> CREATOR = + new Creator<BeginCreateCredentialRequest>() { + @Override + public BeginCreateCredentialRequest createFromParcel(@NonNull Parcel in) { + return new BeginCreateCredentialRequest(in); + } + + @Override + public BeginCreateCredentialRequest[] newArray(int size) { + return new BeginCreateCredentialRequest[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mCallingPackage); + dest.writeString8(mType); + dest.writeBundle(mData); + } + + /** Returns the calling package of the calling app. */ + @NonNull + public String getCallingPackage() { + return mCallingPackage; + } + + /** Returns the type of the credential to be created. */ + @NonNull + public String getType() { + return mType; + } + + /** Returns the data to be used while resolving the credential to create. */ + @NonNull + public Bundle getData() { + return mData; + } +} diff --git a/core/java/android/service/credentials/CreateCredentialResponse.aidl b/core/java/android/service/credentials/BeginCreateCredentialResponse.aidl index 73c9147b0ae4..d2a1408f0681 100644 --- a/core/java/android/service/credentials/CreateCredentialResponse.aidl +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.aidl @@ -16,4 +16,4 @@ package android.service.credentials; -parcelable CreateCredentialResponse; +parcelable BeginCreateCredentialResponse; diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java new file mode 100644 index 000000000000..022678ea49bd --- /dev/null +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java @@ -0,0 +1,150 @@ +/* + * 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 android.service.credentials; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Response to a {@link BeginCreateCredentialRequest}. + */ +public final class BeginCreateCredentialResponse implements Parcelable { + private final @NonNull List<CreateEntry> mCreateEntries; + private final @Nullable CreateEntry mRemoteCreateEntry; + + private BeginCreateCredentialResponse(@NonNull Parcel in) { + List<CreateEntry> createEntries = new ArrayList<>(); + in.readTypedList(createEntries, CreateEntry.CREATOR); + mCreateEntries = createEntries; + mRemoteCreateEntry = in.readTypedObject(CreateEntry.CREATOR); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mCreateEntries); + dest.writeTypedObject(mRemoteCreateEntry, flags); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<BeginCreateCredentialResponse> CREATOR = + new Creator<BeginCreateCredentialResponse>() { + @Override + public BeginCreateCredentialResponse createFromParcel(@NonNull Parcel in) { + return new BeginCreateCredentialResponse(in); + } + + @Override + public BeginCreateCredentialResponse[] newArray(int size) { + return new BeginCreateCredentialResponse[size]; + } + }; + + /* package-private */ BeginCreateCredentialResponse( + @NonNull List<CreateEntry> createEntries, + @Nullable CreateEntry remoteCreateEntry) { + this.mCreateEntries = createEntries; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mCreateEntries); + this.mRemoteCreateEntry = remoteCreateEntry; + } + + /** Returns the list of create entries to be displayed on the UI. */ + public @NonNull List<CreateEntry> getCreateEntries() { + return mCreateEntries; + } + + /** Returns the remote create entry to be displayed on the UI. */ + public @Nullable CreateEntry getRemoteCreateEntry() { + return mRemoteCreateEntry; + } + + /** + * A builder for {@link BeginCreateCredentialResponse} + */ + @SuppressWarnings("WeakerAccess") /* synthetic access */ + public static final class Builder { + private @NonNull List<CreateEntry> mCreateEntries = new ArrayList<>(); + private @Nullable CreateEntry mRemoteCreateEntry; + + /** + * Sets the list of create entries to be shown on the UI. + * + * @throws IllegalArgumentException If {@code createEntries} is empty. + * @throws NullPointerException If {@code createEntries} is null, or any of its elements + * are null. + */ + public @NonNull Builder setCreateEntries(@NonNull List<CreateEntry> createEntries) { + Preconditions.checkCollectionNotEmpty(createEntries, "createEntries"); + mCreateEntries = Preconditions.checkCollectionElementsNotNull( + createEntries, "createEntries"); + return this; + } + + /** + * Adds an entry to the list of create entries to be shown on the UI. + * + * @throws NullPointerException If {@code createEntry} is null. + */ + public @NonNull Builder addCreateEntry(@NonNull CreateEntry createEntry) { + mCreateEntries.add(Objects.requireNonNull(createEntry)); + return this; + } + + /** + * Sets a remote create entry to be shown on the UI. Provider must set this entry if they + * wish to create the credential on a different device. + * + * <p> When constructing the {@link CreateEntry} object, the {@code pendingIntent} must be + * set such that it leads to an activity that can provide UI to fulfill the request on + * a remote device. When user selects this {@code remoteCreateEntry}, the system will + * invoke the {@code pendingIntent} set on the {@link CreateEntry}. + * + * <p> Once the remote credential flow is complete, the {@link android.app.Activity} + * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the + * {@link CredentialProviderService#EXTRA_CREATE_CREDENTIAL_RESULT} key should be populated + * with a {@link android.credentials.CreateCredentialResponse} object. + */ + public @NonNull Builder setRemoteCreateEntry(@Nullable CreateEntry remoteCreateEntry) { + mRemoteCreateEntry = remoteCreateEntry; + return this; + } + + /** + * Builds a new instance of {@link BeginCreateCredentialResponse}. + * + * @throws NullPointerException If {@code createEntries} is null. + * @throws IllegalArgumentException If {@code createEntries} is empty. + */ + public @NonNull BeginCreateCredentialResponse build() { + Preconditions.checkCollectionNotEmpty(mCreateEntries, "createEntries must " + + "not be null, or empty"); + return new BeginCreateCredentialResponse(mCreateEntries, mRemoteCreateEntry); + } + } +} diff --git a/core/java/android/service/credentials/CreateCredentialRequest.aidl b/core/java/android/service/credentials/CreateCredentialRequest.aidl deleted file mode 100644 index eb7fba9405f7..000000000000 --- a/core/java/android/service/credentials/CreateCredentialRequest.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.service.credentials; - -parcelable CreateCredentialRequest;
\ No newline at end of file diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java index e6da349a2fbe..aee85abcc8b8 100644 --- a/core/java/android/service/credentials/CreateCredentialRequest.java +++ b/core/java/android/service/credentials/CreateCredentialRequest.java @@ -27,8 +27,6 @@ import java.util.Objects; /** * Request for creating a credential. - * - * @hide */ public final class CreateCredentialRequest implements Parcelable { private final @NonNull String mCallingPackage; diff --git a/core/java/android/service/credentials/CreateCredentialResponse.java b/core/java/android/service/credentials/CreateCredentialResponse.java deleted file mode 100644 index f69dca81950d..000000000000 --- a/core/java/android/service/credentials/CreateCredentialResponse.java +++ /dev/null @@ -1,143 +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 android.service.credentials; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Response to a {@link CreateCredentialRequest}. - * - * @hide - */ -public final class CreateCredentialResponse implements Parcelable { - private final @NonNull List<SaveEntry> mSaveEntries; - private final @Nullable Action mRemoteSaveEntry; - //TODO : Add actions if needed - - private CreateCredentialResponse(@NonNull Parcel in) { - List<SaveEntry> saveEntries = new ArrayList<>(); - in.readTypedList(saveEntries, SaveEntry.CREATOR); - mSaveEntries = saveEntries; - mRemoteSaveEntry = in.readTypedObject(Action.CREATOR); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeTypedList(mSaveEntries); - dest.writeTypedObject(mRemoteSaveEntry, flags); - } - - @Override - public int describeContents() { - return 0; - } - - public static final @NonNull Creator<CreateCredentialResponse> CREATOR = - new Creator<CreateCredentialResponse>() { - @Override - public CreateCredentialResponse createFromParcel(@NonNull Parcel in) { - return new CreateCredentialResponse(in); - } - - @Override - public CreateCredentialResponse[] newArray(int size) { - return new CreateCredentialResponse[size]; - } - }; - - /* package-private */ CreateCredentialResponse( - @NonNull List<SaveEntry> saveEntries, - @Nullable Action remoteSaveEntry) { - this.mSaveEntries = saveEntries; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mSaveEntries); - this.mRemoteSaveEntry = remoteSaveEntry; - } - - /** Returns the list of save entries to be displayed on the UI. */ - public @NonNull List<SaveEntry> getSaveEntries() { - return mSaveEntries; - } - - /** Returns the remote save entry to be displayed on the UI. */ - public @NonNull Action getRemoteSaveEntry() { - return mRemoteSaveEntry; - } - - /** - * A builder for {@link CreateCredentialResponse} - */ - @SuppressWarnings("WeakerAccess") - public static final class Builder { - private @NonNull List<SaveEntry> mSaveEntries = new ArrayList<>(); - private @Nullable Action mRemoteSaveEntry; - - /** - * Sets the list of save entries to be shown on the UI. - * - * @throws IllegalArgumentException If {@code saveEntries} is empty. - * @throws NullPointerException If {@code saveEntries} is null, or any of its elements - * are null. - */ - public @NonNull Builder setSaveEntries(@NonNull List<SaveEntry> saveEntries) { - Preconditions.checkCollectionNotEmpty(saveEntries, "saveEntries"); - mSaveEntries = Preconditions.checkCollectionElementsNotNull( - saveEntries, "saveEntries"); - return this; - } - - /** - * Adds an entry to the list of save entries to be shown on the UI. - * - * @throws NullPointerException If {@code saveEntry} is null. - */ - public @NonNull Builder addSaveEntry(@NonNull SaveEntry saveEntry) { - mSaveEntries.add(Objects.requireNonNull(saveEntry)); - return this; - } - - /** - * Sets a remote save entry to be shown on the UI. - */ - public @NonNull Builder setRemoteSaveEntry(@Nullable Action remoteSaveEntry) { - mRemoteSaveEntry = remoteSaveEntry; - return this; - } - - /** - * Builds the instance. - * - * @throws IllegalArgumentException If {@code saveEntries} is empty. - */ - public @NonNull CreateCredentialResponse build() { - Preconditions.checkCollectionNotEmpty(mSaveEntries, "saveEntries must " - + "not be empty"); - return new CreateCredentialResponse( - mSaveEntries, - mRemoteSaveEntry); - } - } -} diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/CreateEntry.java index 55ff6ff9755f..eb25e25a725a 100644 --- a/core/java/android/service/credentials/SaveEntry.java +++ b/core/java/android/service/credentials/CreateEntry.java @@ -25,27 +25,25 @@ import android.os.Parcelable; /** * An entry to be shown on the UI. This entry represents where the credential to be created will * be stored. Examples include user's account, family group etc. - * - * @hide */ -public final class SaveEntry implements Parcelable { +public final class CreateEntry implements Parcelable { private final @NonNull Slice mSlice; private final @NonNull PendingIntent mPendingIntent; - private SaveEntry(@NonNull Parcel in) { + private CreateEntry(@NonNull Parcel in) { mSlice = in.readTypedObject(Slice.CREATOR); mPendingIntent = in.readTypedObject(PendingIntent.CREATOR); } - public static final @NonNull Creator<SaveEntry> CREATOR = new Creator<SaveEntry>() { + public static final @NonNull Creator<CreateEntry> CREATOR = new Creator<CreateEntry>() { @Override - public SaveEntry createFromParcel(@NonNull Parcel in) { - return new SaveEntry(in); + public CreateEntry createFromParcel(@NonNull Parcel in) { + return new CreateEntry(in); } @Override - public SaveEntry[] newArray(int size) { - return new SaveEntry[size]; + public CreateEntry[] newArray(int size) { + return new CreateEntry[size]; } }; @@ -61,12 +59,12 @@ public final class SaveEntry implements Parcelable { } /** - * Constructs a save entry to be displayed on the UI. + * Constructs a CreateEntry to be displayed on the UI. * * @param slice the display content to be displayed on the UI, along with this entry * @param pendingIntent the intent to be invoked when the user selects this entry */ - public SaveEntry( + public CreateEntry( @NonNull Slice slice, @NonNull PendingIntent pendingIntent) { this.mSlice = slice; @@ -77,12 +75,12 @@ public final class SaveEntry implements Parcelable { NonNull.class, null, mPendingIntent); } - /** Returns the content to be displayed with this save entry on the UI. */ + /** Returns the content to be displayed with this create entry on the UI. */ public @NonNull Slice getSlice() { return mSlice; } - /** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */ + /** Returns the pendingIntent to be invoked when this create entry on the UI is selectcd. */ public @NonNull PendingIntent getPendingIntent() { return mPendingIntent; } diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java index 98c537a6d6ef..941db02be8d1 100644 --- a/core/java/android/service/credentials/CredentialEntry.java +++ b/core/java/android/service/credentials/CredentialEntry.java @@ -31,8 +31,6 @@ import java.util.Objects; /** * A credential entry that is displayed on the account selector UI. Each entry corresponds to * something that the user can select. - * - * @hide */ public final class CredentialEntry implements Parcelable { /** The type of the credential entry to be shown on the UI. */ @@ -145,61 +143,67 @@ public final class CredentialEntry implements Parcelable { private boolean mAutoSelectAllowed = false; /** - * Builds the instance. + * Creates a builder for a {@link CredentialEntry} that should invoke a + * {@link PendingIntent} when selected by the user. + * + * <p>The {@code pendingIntent} can be used to launch activities that require some user + * engagement before getting the credential corresponding to this entry, + * e.g. authentication, confirmation etc. + * Once the activity fulfills the required user engagement, the + * {@link android.app.Activity} result should be set to + * {@link android.app.Activity#RESULT_OK}, and the + * {@link CredentialProviderService#EXTRA_CREDENTIAL_RESULT} must be set with a + * {@link Credential} object. + * * @param type the type of credential underlying this credential entry * @param slice the content to be displayed with this entry on the UI + * @param pendingIntent the pendingIntent to be invoked when this entry is selected by the + * user * - * @throws IllegalArgumentException If {@code type} is null or empty. - * @throws NullPointerException If {@code slice} is null. + * @throws NullPointerException If {@code slice}, or {@code pendingIntent} is null. + * @throws IllegalArgumentException If {@code type} is null or empty, or if + * {@code pendingIntent} was not created with {@link PendingIntent#getActivity} + * or {@link PendingIntent#getActivities}. */ - public Builder(@NonNull String type, @NonNull Slice slice) { + public Builder(@NonNull String type, @NonNull Slice slice, + @NonNull PendingIntent pendingIntent) { mType = Preconditions.checkStringNotEmpty(type, "type must not be " + "null, or empty"); mSlice = Objects.requireNonNull(slice, "slice must not be null"); - } - - /** - * Sets the pendingIntent to be invoked if the user selects this entry. - * - * The pending intent can be used to launch activities that require some user engagement - * before getting the credential corresponding to this entry, e.g. authentication, - * confirmation etc. - * Once the activity fulfills the required user engagement, a {@link Credential} object - * must be returned as an extra on activity finish. - * - * @throws IllegalStateException If {@code credential} is already set. Must either set the - * {@code credential}, or the {@code pendingIntent}. - */ - public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) { - if (pendingIntent != null) { - Preconditions.checkState(mCredential == null, - "credential is already set. Cannot set both the pendingIntent " - + "and the credential"); + mPendingIntent = Objects.requireNonNull(pendingIntent, + "pendingIntent must not be null"); + if (!mPendingIntent.isActivity()) { + throw new IllegalStateException("Pending intent must start an activity"); } - mPendingIntent = pendingIntent; - return this; } /** - * Sets the credential to be used, if the user selects this entry. + * Creates a builder for a {@link CredentialEntry} that contains a {@link Credential}, + * and does not require further action. + * @param type the type of credential underlying this credential entry + * @param slice the content to be displayed with this entry on the UI + * @param credential the credential to be returned to the client app, when this entry is + * selected by the user * - * @throws IllegalStateException If {@code pendingIntent} is already set. Must either set - * the {@code pendingIntent}, or the {@code credential}. + * @throws IllegalArgumentException If {@code type} is null or empty. + * @throws NullPointerException If {@code slice}, or {@code credential} is null. */ - public @NonNull Builder setCredential(@Nullable Credential credential) { - if (credential != null) { - Preconditions.checkState(mPendingIntent == null, - "pendingIntent is already set. Cannot set both the " - + "pendingIntent and the credential"); - } - mCredential = credential; - return this; + public Builder(@NonNull String type, @NonNull Slice slice, @NonNull Credential credential) { + mType = Preconditions.checkStringNotEmpty(type, "type must not be " + + "null, or empty"); + mSlice = Objects.requireNonNull(slice, + "slice must not be null"); + mCredential = Objects.requireNonNull(credential, + "credential must not be null"); } /** * Sets whether the entry is allowed to be auto selected by the framework. * The default value is set to false. + * + * <p> The entry is only auto selected if it is the only entry on the user selector, + * AND the developer has also enabled auto select, while building the request. */ public @NonNull Builder setAutoSelectAllowed(@NonNull boolean autoSelectAllowed) { mAutoSelectAllowed = autoSelectAllowed; diff --git a/core/java/android/service/credentials/CredentialProviderException.java b/core/java/android/service/credentials/CredentialProviderException.java index 06f0052a29a9..02b74430493c 100644 --- a/core/java/android/service/credentials/CredentialProviderException.java +++ b/core/java/android/service/credentials/CredentialProviderException.java @@ -24,8 +24,6 @@ import java.lang.annotation.RetentionPolicy; /** * Contains custom exceptions to be used by credential providers on failure. - * - * @hide */ public class CredentialProviderException extends Exception { public static final int ERROR_UNKNOWN = 0; @@ -59,6 +57,11 @@ public class CredentialProviderException extends Exception { @Retention(RetentionPolicy.SOURCE) public @interface CredentialProviderError { } + public CredentialProviderException(@CredentialProviderError int errorCode, + @NonNull String message, @NonNull Throwable cause) { + super(message, cause); + mErrorCode = errorCode; + } public CredentialProviderException(@CredentialProviderError int errorCode, @NonNull String message) { diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index 24b7c3c439d4..32646e6fd289 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -37,30 +37,63 @@ import java.util.Objects; /** * Service to be extended by credential providers, in order to return user credentials * to the framework. - * - * @hide */ public abstract class CredentialProviderService extends Service { - /** Extra to be used by provider to populate the credential when ending the activity started - * through the {@code pendingIntent} on the selected {@link SaveEntry}. **/ - public static final String EXTRA_CREATE_CREDENTIAL_RESPONSE = - "android.service.credentials.extra.CREATE_CREDENTIAL_RESPONSE"; + /** + * Intent extra: The {@link android.credentials.CreateCredentialRequest} attached with + * the {@code pendingIntent} that is invoked when the user selects a {@link CreateEntry} + * returned as part of the {@link BeginCreateCredentialResponse} + * + * <p> + * Type: {@link android.credentials.CreateCredentialRequest} + */ + public static final String EXTRA_CREATE_CREDENTIAL_REQUEST = + "android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST"; + + /** + * Intent extra: The result of a create flow operation, to be set on finish of the + * {@link android.app.Activity} invoked through the {@code pendingIntent} set on + * a {@link CreateEntry}. + * + * <p> + * Type: {@link android.credentials.CreateCredentialResponse} + */ + public static final String EXTRA_CREATE_CREDENTIAL_RESULT = + "android.service.credentials.extra.CREATE_CREDENTIAL_RESULT"; - /** Extra to be used by provider to populate the {@link CredentialsDisplayContent} when - * an authentication action entry is selected. **/ - public static final String EXTRA_GET_CREDENTIALS_DISPLAY_CONTENT = - "android.service.credentials.extra.GET_CREDENTIALS_DISPLAY_CONTENT"; + /** + * Intent extra: The result of a get credential flow operation, to be set on finish of the + * {@link android.app.Activity} invoked through the {@code pendingIntent} set on + * a {@link CredentialEntry}. + * + * <p> + * Type: {@link android.credentials.Credential} + */ + public static final String EXTRA_CREDENTIAL_RESULT = + "android.service.credentials.extra.CREDENTIAL_RESULT"; /** - * Provider must read the value against this extra to receive the complete create credential - * request parameters, when a pending intent is launched. + * Intent extra: The result of an authentication flow, to be set on finish of the + * {@link android.app.Activity} invoked through the {@link android.app.PendingIntent} set on + * a {@link GetCredentialsResponse}. This result should contain the actual content, including + * credential entries and action entries, to be shown on the selector. + * + * <p> + * Type: {@link CredentialsResponseContent} */ - public static final String EXTRA_CREATE_CREDENTIAL_REQUEST_PARAMS = - "android.service.credentials.extra.CREATE_CREDENTIAL_REQUEST_PARAMS"; + public static final String EXTRA_GET_CREDENTIALS_CONTENT_RESULT = + "android.service.credentials.extra.GET_CREDENTIALS_CONTENT_RESULT"; - /** Extra to be used by the provider when setting the credential result. */ - public static final String EXTRA_GET_CREDENTIAL = - "android.service.credentials.extra.GET_CREDENTIAL"; + /** + * Intent extra: The error result of any {@link android.app.PendingIntent} flow, to be set + * on finish of the corresponding {@link android.app.Activity}. This result should contain an + * error code, representing the error encountered by the provider. + * + * <p> + * Type: {@link String} + */ + public static final String EXTRA_ERROR = + "android.service.credentials.extra.ERROR"; private static final String TAG = "CredProviderService"; @@ -129,20 +162,21 @@ public abstract class CredentialProviderService extends Service { } @Override - public ICancellationSignal onCreateCredential(CreateCredentialRequest request, - ICreateCredentialCallback callback) { + public ICancellationSignal onBeginCreateCredential(BeginCreateCredentialRequest request, + IBeginCreateCredentialCallback callback) { Objects.requireNonNull(request); Objects.requireNonNull(callback); ICancellationSignal transport = CancellationSignal.createTransport(); mHandler.sendMessage(obtainMessage( - CredentialProviderService::onCreateCredential, + CredentialProviderService::onBeginCreateCredential, CredentialProviderService.this, request, CancellationSignal.fromTransport(transport), - new OutcomeReceiver<CreateCredentialResponse, CredentialProviderException>() { + new OutcomeReceiver< + BeginCreateCredentialResponse, CredentialProviderException>() { @Override - public void onResult(CreateCredentialResponse result) { + public void onResult(BeginCreateCredentialResponse result) { try { callback.onSuccess(result); } catch (RemoteException e) { @@ -182,8 +216,8 @@ public abstract class CredentialProviderService extends Service { * the android system. * @param callback Object used to relay the response of the credential creation request. */ - public abstract void onCreateCredential(@NonNull CreateCredentialRequest request, + public abstract void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request, @NonNull CancellationSignal cancellationSignal, - @NonNull OutcomeReceiver<CreateCredentialResponse, + @NonNull OutcomeReceiver<BeginCreateCredentialResponse, CredentialProviderException> callback); } diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsResponseContent.java index 4b23800891a8..32cab5004ac0 100644 --- a/core/java/android/service/credentials/CredentialsDisplayContent.java +++ b/core/java/android/service/credentials/CredentialsResponseContent.java @@ -28,12 +28,10 @@ import java.util.List; import java.util.Objects; /** - * Content to be displayed on the account selector UI, including credential entries, - * actions etc. - * - * @hide + * The content to be displayed on the account selector UI, including credential entries, + * actions etc. Returned as part of {@link GetCredentialsResponse} */ -public final class CredentialsDisplayContent implements Parcelable { +public final class CredentialsResponseContent implements Parcelable { /** List of credential entries to be displayed on the UI. */ private final @NonNull List<CredentialEntry> mCredentialEntries; @@ -41,36 +39,36 @@ public final class CredentialsDisplayContent implements Parcelable { private final @NonNull List<Action> mActions; /** Remote credential entry to get the response from a different device. */ - private final @Nullable Action mRemoteCredentialEntry; + private final @Nullable CredentialEntry mRemoteCredentialEntry; - private CredentialsDisplayContent(@NonNull List<CredentialEntry> credentialEntries, + private CredentialsResponseContent(@NonNull List<CredentialEntry> credentialEntries, @NonNull List<Action> actions, - @Nullable Action remoteCredentialEntry) { + @Nullable CredentialEntry remoteCredentialEntry) { mCredentialEntries = credentialEntries; mActions = actions; mRemoteCredentialEntry = remoteCredentialEntry; } - private CredentialsDisplayContent(@NonNull Parcel in) { + private CredentialsResponseContent(@NonNull Parcel in) { List<CredentialEntry> credentialEntries = new ArrayList<>(); in.readTypedList(credentialEntries, CredentialEntry.CREATOR); mCredentialEntries = credentialEntries; List<Action> actions = new ArrayList<>(); in.readTypedList(actions, Action.CREATOR); mActions = actions; - mRemoteCredentialEntry = in.readTypedObject(Action.CREATOR); + mRemoteCredentialEntry = in.readTypedObject(CredentialEntry.CREATOR); } - public static final @NonNull Creator<CredentialsDisplayContent> CREATOR = - new Creator<CredentialsDisplayContent>() { + public static final @NonNull Creator<CredentialsResponseContent> CREATOR = + new Creator<CredentialsResponseContent>() { @Override - public CredentialsDisplayContent createFromParcel(@NonNull Parcel in) { - return new CredentialsDisplayContent(in); + public CredentialsResponseContent createFromParcel(@NonNull Parcel in) { + return new CredentialsResponseContent(in); } @Override - public CredentialsDisplayContent[] newArray(int size) { - return new CredentialsDisplayContent[size]; + public CredentialsResponseContent[] newArray(int size) { + return new CredentialsResponseContent[size]; } }; @@ -103,22 +101,34 @@ public final class CredentialsDisplayContent implements Parcelable { /** * Returns the remote credential entry to be displayed on the UI. */ - public @Nullable Action getRemoteCredentialEntry() { + public @Nullable CredentialEntry getRemoteCredentialEntry() { return mRemoteCredentialEntry; } /** - * Builds an instance of {@link CredentialsDisplayContent}. + * Builds an instance of {@link CredentialsResponseContent}. */ public static final class Builder { private List<CredentialEntry> mCredentialEntries = new ArrayList<>(); private List<Action> mActions = new ArrayList<>(); - private Action mRemoteCredentialEntry; + private CredentialEntry mRemoteCredentialEntry; /** - * Sets the remote credential entry to be displayed on the UI. + * Sets a remote credential entry to be shown on the UI. Provider must set this if they + * wish to get the credential from a different device. + * + * <p> When constructing the {@link CredentialEntry} object, the {@code pendingIntent} + * must be set such that it leads to an activity that can provide UI to fulfill the request + * on a remote device. When user selects this {@code remoteCredentialEntry}, the system will + * invoke the {@code pendingIntent} set on the {@link CredentialEntry}. + * + * <p> Once the remote credential flow is complete, the {@link android.app.Activity} + * result should be set to {@link android.app.Activity#RESULT_OK} and an extra with the + * {@link CredentialProviderService#EXTRA_CREDENTIAL_RESULT} key should be populated + * with a {@link android.credentials.Credential} object. */ - public @NonNull Builder setRemoteCredentialEntry(@Nullable Action remoteCredentialEntry) { + public @NonNull Builder setRemoteCredentialEntry(@Nullable CredentialEntry + remoteCredentialEntry) { mRemoteCredentialEntry = remoteCredentialEntry; return this; } @@ -138,6 +148,11 @@ public final class CredentialsDisplayContent implements Parcelable { * Adds an {@link Action} to the list of actions to be displayed on * the UI. * + * <p> An {@code action} must be used for independent user actions, + * such as opening the app, intenting directly into a certain app activity etc. The + * {@code pendingIntent} set with the {@code action} must invoke the corresponding + * activity. + * * @throws NullPointerException If {@code action} is null. */ public @NonNull Builder addAction(@NonNull Action action) { @@ -175,17 +190,16 @@ public final class CredentialsDisplayContent implements Parcelable { /** * Builds a {@link GetCredentialsResponse} instance. * - * @throws NullPointerException If {@code credentialEntries} is null. - * @throws IllegalStateException if both {@code credentialEntries} and - * {@code actions} are empty. + * @throws IllegalStateException if {@code credentialEntries}, {@code actions} + * and {@code remoteCredentialEntry} are all null or empty. */ - public @NonNull CredentialsDisplayContent build() { + public @NonNull CredentialsResponseContent build() { if (mCredentialEntries != null && mCredentialEntries.isEmpty() - && mActions != null && mActions.isEmpty()) { + && mActions != null && mActions.isEmpty() && mRemoteCredentialEntry == null) { throw new IllegalStateException("credentialEntries and actions must not both " + "be empty"); } - return new CredentialsDisplayContent(mCredentialEntries, mActions, + return new CredentialsResponseContent(mCredentialEntries, mActions, mRemoteCredentialEntry); } } diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java index 03ba20e1df27..9052b54c8291 100644 --- a/core/java/android/service/credentials/GetCredentialsRequest.java +++ b/core/java/android/service/credentials/GetCredentialsRequest.java @@ -29,8 +29,6 @@ import java.util.Objects; /** * Request for getting user's credentials from a given credential provider. - * - * @hide */ public final class GetCredentialsRequest implements Parcelable { /** Calling package of the app requesting for credentials. */ diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java index 979a6993c3d4..5263141f982a 100644 --- a/core/java/android/service/credentials/GetCredentialsResponse.java +++ b/core/java/android/service/credentials/GetCredentialsResponse.java @@ -26,12 +26,10 @@ import java.util.Objects; /** * Response from a credential provider, containing credential entries and other associated * data to be shown on the account selector UI. - * - * @hide */ public final class GetCredentialsResponse implements Parcelable { /** Content to be used for the UI. */ - private final @Nullable CredentialsDisplayContent mCredentialsDisplayContent; + private final @Nullable CredentialsResponseContent mCredentialsResponseContent; /** * Authentication action that must be launched and completed before showing any content @@ -40,11 +38,17 @@ public final class GetCredentialsResponse implements Parcelable { private final @Nullable Action mAuthenticationAction; /** - * Creates a {@link GetCredentialsRequest} instance with an authentication action set. + * Creates a {@link GetCredentialsResponse} instance with an authentication {@link Action} set. * Providers must use this method when no content can be shown before authentication. * - * Once the authentication action activity is launched, and the user is authenticated, providers - * should create another response with {@link CredentialsDisplayContent} using + * <p> When the user selects this {@code authenticationAction}, the system invokes the + * corresponding {@code pendingIntent}. Once the authentication flow is complete, + * the {@link android.app.Activity} result should be set + * to {@link android.app.Activity#RESULT_OK} and the + * {@link CredentialProviderService#EXTRA_GET_CREDENTIALS_CONTENT_RESULT} extra should be set + * with a fully populated {@link CredentialsResponseContent} object. + * the authentication action activity is launched, and the user is authenticated, providers + * should create another response with {@link CredentialsResponseContent} using * {@code createWithDisplayContent}, and add that response to the result of the authentication * activity. * @@ -58,27 +62,27 @@ public final class GetCredentialsResponse implements Parcelable { } /** - * Creates a {@link GetCredentialsRequest} instance with display content to be shown on the UI. + * Creates a {@link GetCredentialsRequest} instance with content to be shown on the UI. * Providers must use this method when there is content to be shown without top level - * authentication required. + * authentication required, including credential entries, action entries or a remote entry, * - * @throws NullPointerException If {@code credentialsDisplayContent} is null. + * @throws NullPointerException If {@code credentialsResponseContent} is null. */ - public static @NonNull GetCredentialsResponse createWithDisplayContent( - @NonNull CredentialsDisplayContent credentialsDisplayContent) { - Objects.requireNonNull(credentialsDisplayContent, - "credentialsDisplayContent must not be null"); - return new GetCredentialsResponse(credentialsDisplayContent, null); + public static @NonNull GetCredentialsResponse createWithResponseContent( + @NonNull CredentialsResponseContent credentialsResponseContent) { + Objects.requireNonNull(credentialsResponseContent, + "credentialsResponseContent must not be null"); + return new GetCredentialsResponse(credentialsResponseContent, null); } - private GetCredentialsResponse(@Nullable CredentialsDisplayContent credentialsDisplayContent, + private GetCredentialsResponse(@Nullable CredentialsResponseContent credentialsResponseContent, @Nullable Action authenticationAction) { - mCredentialsDisplayContent = credentialsDisplayContent; + mCredentialsResponseContent = credentialsResponseContent; mAuthenticationAction = authenticationAction; } private GetCredentialsResponse(@NonNull Parcel in) { - mCredentialsDisplayContent = in.readTypedObject(CredentialsDisplayContent.CREATOR); + mCredentialsResponseContent = in.readTypedObject(CredentialsResponseContent.CREATOR); mAuthenticationAction = in.readTypedObject(Action.CREATOR); } @@ -102,23 +106,23 @@ public final class GetCredentialsResponse implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeTypedObject(mCredentialsDisplayContent, flags); + dest.writeTypedObject(mCredentialsResponseContent, flags); dest.writeTypedObject(mAuthenticationAction, flags); } /** - * Returns the authentication action to be invoked before any other content - * can be shown to the user. + * If this response represents a top level authentication action, returns the authentication + * action to be invoked before any other content can be shown to the user. */ public @Nullable Action getAuthenticationAction() { return mAuthenticationAction; } /** - * Returns the credentialDisplayContent that does not require authentication, and - * can be shown to the user on the account selector UI. + * Returns the actual content to be displayed on the selector, if this response does not + * require any top level authentication. */ - public @Nullable CredentialsDisplayContent getCredentialsDisplayContent() { - return mCredentialsDisplayContent; + public @Nullable CredentialsResponseContent getCredentialsResponseContent() { + return mCredentialsResponseContent; } } diff --git a/core/java/android/service/credentials/ICreateCredentialCallback.aidl b/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl index 4cc76a4a341b..ec0bc364a457 100644 --- a/core/java/android/service/credentials/ICreateCredentialCallback.aidl +++ b/core/java/android/service/credentials/IBeginCreateCredentialCallback.aidl @@ -1,13 +1,13 @@ package android.service.credentials; -import android.service.credentials.CreateCredentialResponse; +import android.service.credentials.BeginCreateCredentialResponse; /** * Interface from the system to a credential provider service. * * @hide */ -oneway interface ICreateCredentialCallback { - void onSuccess(in CreateCredentialResponse request); +oneway interface IBeginCreateCredentialCallback { + void onSuccess(in BeginCreateCredentialResponse request); void onFailure(int errorCode, in CharSequence message); }
\ No newline at end of file diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl index c21cefab701a..b9eb3ed9571a 100644 --- a/core/java/android/service/credentials/ICredentialProviderService.aidl +++ b/core/java/android/service/credentials/ICredentialProviderService.aidl @@ -18,9 +18,9 @@ package android.service.credentials; import android.os.ICancellationSignal; import android.service.credentials.GetCredentialsRequest; -import android.service.credentials.CreateCredentialRequest; +import android.service.credentials.BeginCreateCredentialRequest; import android.service.credentials.IGetCredentialsCallback; -import android.service.credentials.ICreateCredentialCallback; +import android.service.credentials.IBeginCreateCredentialCallback; import android.os.ICancellationSignal; /** @@ -30,5 +30,5 @@ import android.os.ICancellationSignal; */ interface ICredentialProviderService { ICancellationSignal onGetCredentials(in GetCredentialsRequest request, in IGetCredentialsCallback callback); - ICancellationSignal onCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback); + ICancellationSignal onBeginCreateCredential(in BeginCreateCredentialRequest request, in IBeginCreateCredentialCallback callback); } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 09d0fc5fe59a..e5c9adba46a9 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -26,6 +26,7 @@ import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; +import android.telephony.Annotation.CallState; import android.telephony.Annotation.DisconnectCauses; import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; @@ -725,7 +726,7 @@ public class PhoneStateListener { */ @Deprecated @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) - public void onCallStateChanged(@Annotation.CallState int state, String phoneNumber) { + public void onCallStateChanged(@CallState int state, String phoneNumber) { // default implementation empty } @@ -1568,48 +1569,12 @@ public class PhoneStateListener { () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state))); } - public void onCallStatesChanged(List<CallState> callStateList) { + public void onCallAttributesChanged(CallAttributes callAttributes) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; - if (callStateList == null) return; - CallAttributes ca; - if (callStateList.isEmpty()) { - ca = new CallAttributes( - new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_IDLE, - PreciseCallState.PRECISE_CALL_STATE_IDLE, - PreciseCallState.PRECISE_CALL_STATE_IDLE, - DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID), - TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality()); - } else { - int foregroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; - int backgroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; - int ringingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; - for (CallState cs : callStateList) { - switch (cs.getCallClassification()) { - case CallState.CALL_CLASSIFICATION_FOREGROUND: - foregroundCallState = cs.getCallState(); - break; - case CallState.CALL_CLASSIFICATION_BACKGROUND: - backgroundCallState = cs.getCallState(); - break; - case CallState.CALL_CLASSIFICATION_RINGING: - ringingCallState = cs.getCallState(); - break; - default: - break; - } - } - ca = new CallAttributes( - new PreciseCallState( - foregroundCallState, backgroundCallState, ringingCallState, - DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID), - callStateList.get(0).getNetworkType(), - callStateList.get(0).getCallQuality()); - } Binder.withCleanCallingIdentity( - () -> mExecutor.execute( - () -> psl.onCallAttributesChanged(ca))); + () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes))); } public void onActiveDataSubIdChanged(int subId) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 54c4b668bfb3..e8960b8e35cd 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -27,7 +27,6 @@ import android.os.Binder; import android.os.Build; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; -import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IPhoneStateListener; @@ -63,7 +62,7 @@ import java.util.concurrent.Executor; * appropriate sub-interfaces. */ public class TelephonyCallback { - private static final String LOG_TAG = "TelephonyCallback"; + /** * Experiment flag to set the per-pid registration limit for TelephonyCallback * @@ -1333,9 +1332,7 @@ public class TelephonyCallback { @SystemApi public interface CallAttributesListener { /** - * Callback invoked when the call attributes changes on the active call on the registered - * subscription. If the user swaps between a foreground and background call the call - * attributes will be reported for the active call only. + * Callback invoked when the call attributes changes on the registered subscription. * Note, the registration subscription ID comes from {@link TelephonyManager} object * which registers TelephonyCallback by * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. @@ -1349,77 +1346,9 @@ public class TelephonyCallback { * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}. * * @param callAttributes the call attributes - * @deprecated Use onCallStatesChanged({@link List<CallState>}) to get each of call - * state for all ongoing calls on the subscription. */ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - @Deprecated - default void onCallAttributesChanged(@NonNull CallAttributes callAttributes) { - Log.w(LOG_TAG, "onCallAttributesChanged(List<CallAttributes>) should be " - + "overridden."); - } - - /** - * Callback invoked when the call attributes changes on the ongoing calls on the registered - * subscription. If there are 1 foreground and 1 background call, Two {@link CallState} - * will be passed. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers TelephonyCallback by - * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * In the event that there are no active(state is not - * {@link PreciseCallState#PRECISE_CALL_STATE_IDLE}) calls, this API will report empty list. - * - * The calling app should have carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the - * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}. - * - * @param callStateList the list of call states for each ongoing call. If there are - * a active call and a holding call, 1 call attributes for - * {@link PreciseCallState#PRECISE_CALL_STATE_ACTIVE} and another - * for {@link PreciseCallState#PRECISE_CALL_STATE_HOLDING} - * will be in this list. - */ - // Added as default for backward compatibility - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - default void onCallStatesChanged(@NonNull List<CallState> callStateList) { - if (callStateList.size() > 0) { - int foregroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; - int backgroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; - int ringingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; - for (CallState cs : callStateList) { - switch (cs.getCallClassification()) { - case CallState.CALL_CLASSIFICATION_FOREGROUND: - foregroundCallState = cs.getCallState(); - break; - case CallState.CALL_CLASSIFICATION_BACKGROUND: - backgroundCallState = cs.getCallState(); - break; - case CallState.CALL_CLASSIFICATION_RINGING: - ringingCallState = cs.getCallState(); - break; - default: - break; - } - } - onCallAttributesChanged(new CallAttributes( - new PreciseCallState( - ringingCallState, foregroundCallState, backgroundCallState, - DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID), - callStateList.get(0).getNetworkType(), - callStateList.get(0).getCallQuality())); - } else { - onCallAttributesChanged(new CallAttributes( - new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_IDLE, - PreciseCallState.PRECISE_CALL_STATE_IDLE, - PreciseCallState.PRECISE_CALL_STATE_IDLE, - DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID), - TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality())); - } - } + void onCallAttributesChanged(@NonNull CallAttributes callAttributes); } /** @@ -1773,13 +1702,14 @@ public class TelephonyCallback { () -> mExecutor.execute(() -> listener.onRadioPowerStateChanged(state))); } - public void onCallStatesChanged(List<CallState> callStateList) { + public void onCallAttributesChanged(CallAttributes callAttributes) { CallAttributesListener listener = (CallAttributesListener) mTelephonyCallbackWeakRef.get(); if (listener == null) return; Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onCallStatesChanged(callStateList))); + () -> mExecutor.execute(() -> listener.onCallAttributesChanged( + callAttributes))); } public void onActiveDataSubIdChanged(int subId) { diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 0a1538de9f5d..a3696e398668 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -32,13 +32,13 @@ import android.telephony.Annotation.CallState; import android.telephony.Annotation.DataActivityType; import android.telephony.Annotation.DisconnectCauses; import android.telephony.Annotation.NetworkType; +import android.telephony.Annotation.PreciseCallStates; import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; import android.telephony.TelephonyManager.CarrierPrivilegesCallback; import android.telephony.emergency.EmergencyNumber; -import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; import android.util.ArraySet; import android.util.Log; @@ -741,20 +741,17 @@ public class TelephonyRegistryManager { * @param slotIndex for which precise call state changed. Can be derived from subId except when * subId is invalid. * @param subId for which precise call state changed. - * @param callStates Array of PreciseCallState of foreground, background & ringing calls. - * @param imsCallIds Array of IMS call session ID{@link ImsCallSession#getCallId} for - * ringing, foreground & background calls. - * @param imsServiceTypes Array of IMS call service type for ringing, foreground & - * background calls. - * @param imsCallTypes Array of IMS call type for ringing, foreground & background calls. + * @param ringCallPreciseState ringCall state. + * @param foregroundCallPreciseState foreground call state. + * @param backgroundCallPreciseState background call state. */ public void notifyPreciseCallState(int slotIndex, int subId, - @Annotation.PreciseCallStates int[] callStates, String[] imsCallIds, - @Annotation.ImsCallServiceType int[] imsServiceTypes, - @Annotation.ImsCallType int[] imsCallTypes) { + @PreciseCallStates int ringCallPreciseState, + @PreciseCallStates int foregroundCallPreciseState, + @PreciseCallStates int backgroundCallPreciseState) { try { - sRegistry.notifyPreciseCallState(slotIndex, subId, callStates, - imsCallIds, imsServiceTypes, imsCallTypes); + sRegistry.notifyPreciseCallState(slotIndex, subId, ringCallPreciseState, + foregroundCallPreciseState, backgroundCallPreciseState); } catch (RemoteException ex) { // system process is dead throw ex.rethrowFromSystemServer(); diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 517d98222093..959295b883d5 100755 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -18,6 +18,7 @@ package android.util; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.content.res.FontScaleConverter; import android.os.SystemProperties; /** @@ -273,6 +274,15 @@ public class DisplayMetrics { * increments at runtime based on a user preference for the font size. */ public float scaledDensity; + + /** + * If non-null, this will be used to calculate font sizes instead of {@link #scaledDensity}. + * + * @hide + */ + @Nullable + public FontScaleConverter fontScaleConverter; + /** * The exact physical pixels per inch of the screen in the X dimension. */ @@ -350,6 +360,7 @@ public class DisplayMetrics { noncompatScaledDensity = o.noncompatScaledDensity; noncompatXdpi = o.noncompatXdpi; noncompatYdpi = o.noncompatYdpi; + fontScaleConverter = o.fontScaleConverter; } public void setToDefaults() { @@ -367,6 +378,7 @@ public class DisplayMetrics { noncompatScaledDensity = scaledDensity; noncompatXdpi = xdpi; noncompatYdpi = ydpi; + fontScaleConverter = null; } @Override diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index 44318bbc5468..7e054fc3f952 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -408,7 +408,14 @@ public class TypedValue { case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: - return value * metrics.scaledDensity; + if (metrics.fontScaleConverter != null) { + return applyDimension( + COMPLEX_UNIT_DIP, + metrics.fontScaleConverter.convertSpToDp(value), + metrics); + } else { + return value * metrics.scaledDensity; + } case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index ef18458d3244..57b2d39a3ba8 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -728,8 +728,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void releaseSurfaces(boolean releaseSurfacePackage) { mAlpha = 1f; + mSurface.destroy(); synchronized (mSurfaceControlLock) { - mSurface.destroy(); if (mBlastBufferQueue != null) { mBlastBufferQueue.destroy(); mBlastBufferQueue = null; diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index d77e499357b9..03b25c25ab99 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -29,6 +29,7 @@ import static android.view.WindowInsets.Type.STATUS_BARS; import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT; import static android.view.WindowInsets.Type.all; +import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; import static android.view.WindowInsets.Type.systemBars; @@ -597,7 +598,10 @@ public final class WindowInsets { return new WindowInsets(null, null, mTypeVisibilityMap, mIsRound, mAlwaysConsumeSystemBars, - displayCutoutCopyConstructorArgument(this), + // If the system window insets types contain displayCutout, we should also consume + // it. + (mCompatInsetsTypes & displayCutout()) != 0 + ? null : displayCutoutCopyConstructorArgument(this), mRoundedCorners, mPrivacyIndicatorBounds, mCompatInsetsTypes, mCompatIgnoreVisibility); } diff --git a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java index 85f5056e4116..44b6deb3015f 100644 --- a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java +++ b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java @@ -20,13 +20,18 @@ import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; +import android.accessibilityservice.IAccessibilityServiceConnection; import android.accessibilityservice.MagnificationConfig; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.ResolveInfo; import android.graphics.Region; import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; @@ -34,14 +39,15 @@ import android.view.inputmethod.EditorInfo; import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; /** * Allows a privileged app - an app with MANAGE_ACCESSIBILITY permission and SystemAPI access - to * interact with the windows in the display that this proxy represents. Proxying the default display - * or a display that is not tracked will throw an exception. Only the real user has access to global - * clients like SystemUI. + * or a display that is not tracked by accessibility, such as private displays, will throw an + * exception. Only the real user has access to global clients like SystemUI. * * <p> * To register and unregister a proxy, use @@ -49,7 +55,16 @@ import java.util.concurrent.Executor; * and {@link AccessibilityManager#unregisterDisplayProxy(AccessibilityDisplayProxy)}. If the app * that has registered the proxy dies, the system will remove the proxy. * - * TODO(241429275): Complete proxy impl and add additional support (if necessary) like cache methods + * <p> + * Avoid using the app's main thread. Proxy methods such as {@link #getWindows} and node methods + * like {@link AccessibilityNodeInfo#getChild(int)} will happen frequently. Node methods may also + * wait on the displayed app's UI thread to obtain accurate screen data. + * + * <p> + * To get a list of {@link AccessibilityServiceInfo}s that have populated {@link ComponentName}s and + * {@link ResolveInfo}s, retrieve the list using {@link #getInstalledAndEnabledServices()} after + * {@link #onProxyConnected()} has been called. + * * @hide */ @SystemApi @@ -91,7 +106,134 @@ public abstract class AccessibilityDisplayProxy { } /** - * An IAccessibilityServiceClient that handles interrupts and accessibility events. + * Handles {@link android.view.accessibility.AccessibilityEvent}s. + * <p> + * AccessibilityEvents represent changes to the UI, or what parts of the node tree have changed. + * AccessibilityDisplayProxy should use these to query new UI and send appropriate feedback + * to their users. + * <p> + * For example, a {@link AccessibilityEvent#TYPE_WINDOWS_CHANGED} indicates a change in windows, + * so a proxy may query {@link #getWindows} to obtain updated UI and potentially inform of a new + * window title. Or a proxy may emit an earcon on a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. + */ + public void onAccessibilityEvent(@NonNull AccessibilityEvent event) { + // Default no-op + } + + /** + * Handles a successful system connection after + * {@link AccessibilityManager#registerDisplayProxy(AccessibilityDisplayProxy)} is called. + * + * <p> + * At this point, querying for UI is available and {@link AccessibilityEvent}s will begin being + * sent. An AccessibilityDisplayProxy may instantiate core infrastructure components here. + */ + public void onProxyConnected() { + // Default no-op + } + + /** + * Handles a request to interrupt the accessibility feedback. + * <p> + * AccessibilityDisplayProxy should interrupt the accessibility activity occurring on its + * display. For example, a screen reader may interrupt speech. + * + * @see AccessibilityManager#interrupt() + * @see AccessibilityService#onInterrupt() + */ + public void interrupt() { + // Default no-op + } + + /** + * Gets the focus of the window specified by {@code windowInfo}. + * + * @param windowInfo the window to search + * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or + * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. + * @return The node info of the focused view or null. + * @hide + * TODO(254545943): Do not expose until support for accessibility focus and/or input is in place + */ + @Nullable + public AccessibilityNodeInfo findFocus(@NonNull AccessibilityWindowInfo windowInfo, int focus) { + AccessibilityNodeInfo windowRoot = windowInfo.getRoot(); + return windowRoot != null ? windowRoot.findFocus(focus) : null; + } + + /** + * Gets the windows of the tracked display. + * + * @see AccessibilityService#getWindows() + */ + @NonNull + public List<AccessibilityWindowInfo> getWindows() { + return AccessibilityInteractionClient.getInstance().getWindowsOnDisplay(mConnectionId, + mDisplayId); + } + + /** + * Sets the list of {@link AccessibilityServiceInfo}s describing the services interested in the + * {@link AccessibilityDisplayProxy}'s display. + * + * <p>These represent a11y features and services that are installed and running. These should + * not include {@link AccessibilityService}s installed on the phone. + * + * @param installedAndEnabledServices the list of installed and running a11y services. + */ + public void setInstalledAndEnabledServices( + @NonNull List<AccessibilityServiceInfo> installedAndEnabledServices) { + mInstalledAndEnabledServices = installedAndEnabledServices; + sendServiceInfos(); + } + + /** + * Sets the {@link AccessibilityServiceInfo} for this service if the latter is + * properly set and there is an {@link IAccessibilityServiceConnection} to the + * AccessibilityManagerService. + */ + private void sendServiceInfos() { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (mInstalledAndEnabledServices != null && mInstalledAndEnabledServices.size() > 0 + && connection != null) { + try { + connection.setInstalledAndEnabledServices(mInstalledAndEnabledServices); + AccessibilityInteractionClient.getInstance().clearCache(mConnectionId); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfos", re); + re.rethrowFromSystemServer(); + } + } + mInstalledAndEnabledServices = null; + } + + /** + * Gets the list of {@link AccessibilityServiceInfo}s describing the services interested in the + * {@link AccessibilityDisplayProxy}'s display. + * + * @return The {@link AccessibilityServiceInfo}s of interested services. + * @see AccessibilityServiceInfo + */ + @NonNull + public final List<AccessibilityServiceInfo> getInstalledAndEnabledServices() { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + return connection.getInstalledAndEnabledServices(); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); + re.rethrowFromSystemServer(); + } + } + return Collections.emptyList(); + } + + /** + * An IAccessibilityServiceClient that handles interrupts, accessibility events, and system + * connection. */ private class IAccessibilityServiceClientImpl extends AccessibilityService.IAccessibilityServiceClientWrapper { @@ -100,17 +242,24 @@ public abstract class AccessibilityDisplayProxy { super(context, executor, new AccessibilityService.Callbacks() { @Override public void onAccessibilityEvent(AccessibilityEvent event) { - // TODO: call AccessiiblityProxy.onAccessibilityEvent + // TODO(254545943): Remove check when event processing is done more upstream in + // AccessibilityManagerService. + if (event.getDisplayId() == mDisplayId) { + AccessibilityDisplayProxy.this.onAccessibilityEvent(event); + } } @Override public void onInterrupt() { - // TODO: call AccessiiblityProxy.onInterrupt + AccessibilityDisplayProxy.this.interrupt(); } + @Override public void onServiceConnected() { - // TODO: send service infos and call AccessiiblityProxy.onProxyConnected + AccessibilityDisplayProxy.this.sendServiceInfos(); + AccessibilityDisplayProxy.this.onProxyConnected(); } + @Override public void init(int connectionId, IBinder windowToken) { mConnectionId = connectionId; diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 7030ab54b790..06a6de9a9df2 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -458,14 +458,20 @@ public final class AccessibilityInteractionClient * @return The {@link AccessibilityWindowInfo} list. */ public List<AccessibilityWindowInfo> getWindows(int connectionId) { + return getWindowsOnDisplay(connectionId, Display.DEFAULT_DISPLAY); + } + + /** + * Gets the info for all windows of the specified display. + * + * @param connectionId The id of a connection for interacting with the system. + * @return The {@link AccessibilityWindowInfo} list belonging to {@code displayId}. + */ + public List<AccessibilityWindowInfo> getWindowsOnDisplay(int connectionId, int displayId) { final SparseArray<List<AccessibilityWindowInfo>> windows = getWindowsOnAllDisplays(connectionId); - if (windows.size() > 0) { - return windows.valueAt(Display.DEFAULT_DISPLAY); - } - return Collections.emptyList(); + return windows.get(displayId, Collections.emptyList()); } - /** * Gets the info for all windows of all displays. * diff --git a/core/java/com/android/internal/content/om/OverlayManagerImpl.java b/core/java/com/android/internal/content/om/OverlayManagerImpl.java index 76e068d11d74..6ceccd1b544b 100644 --- a/core/java/com/android/internal/content/om/OverlayManagerImpl.java +++ b/core/java/com/android/internal/content/om/OverlayManagerImpl.java @@ -17,14 +17,20 @@ package com.android.internal.content.om; import static android.content.Context.MODE_PRIVATE; +import static android.content.om.OverlayManagerTransaction.Request.BUNDLE_FABRICATED_OVERLAY; +import static android.content.om.OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED; +import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; import static com.android.internal.content.om.OverlayConfig.DEFAULT_PRIORITY; import android.annotation.NonNull; +import android.annotation.NonUiContext; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; +import android.content.om.OverlayManagerTransaction; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.parsing.FrameworkParsingPackageUtils; @@ -48,6 +54,7 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -129,10 +136,9 @@ public class OverlayManagerImpl { } } - /** - * Ensure the base dir for self-targeting is valid. - */ + /** Ensure the base dir for self-targeting is valid. */ @VisibleForTesting + @NonUiContext public void ensureBaseDir() { final String baseApkPath = mContext.getApplicationInfo().getBaseCodePath(); final Path baseApkFolderName = Path.of(baseApkPath).getParent().getFileName(); @@ -170,6 +176,16 @@ public class OverlayManagerImpl { mBasePath = baseFile.toPath(); } + private boolean isSameWithTargetSignature(final String targetPackage) { + final PackageManager packageManager = mContext.getPackageManager(); + final String packageName = mContext.getPackageName(); + if (TextUtils.equals(packageName, targetPackage)) { + return true; + } + return packageManager.checkSignatures(packageName, targetPackage) + == PackageManager.SIGNATURE_MATCH; + } + /** * Check if the overlay name is valid or not. * @@ -202,8 +218,12 @@ public class OverlayManagerImpl { /** * Save FabricatedOverlay instance as frro and idmap files. * + * <p>In order to fill the overlayable policy, it's necessary to collect the information from + * app. And then, the information is passed to native layer to fill the overlayable policy + * * @param overlayInternal the FabricatedOverlayInternal to be saved. */ + @NonUiContext public void registerFabricatedOverlay(@NonNull FabricatedOverlayInternal overlayInternal) throws IOException, PackageManager.NameNotFoundException { ensureBaseDir(); @@ -214,6 +234,9 @@ public class OverlayManagerImpl { final String overlayName = checkOverlayNameValid(overlayInternal.overlayName); checkPackageName(overlayInternal.packageName); checkPackageName(overlayInternal.targetPackageName); + Preconditions.checkStringNotEmpty( + overlayInternal.targetOverlayable, + "Target overlayable should be neither null nor empty string."); final ApplicationInfo applicationInfo = mContext.getApplicationInfo(); final String targetPackage = Preconditions.checkStringNotEmpty( @@ -223,7 +246,17 @@ public class OverlayManagerImpl { createFrroFile(frroPath.toString(), overlayInternal); try { - createIdmapFile(targetPackage, frroPath.toString(), idmapPath.toString(), overlayName); + createIdmapFile( + targetPackage, + frroPath.toString(), + idmapPath.toString(), + overlayName, + applicationInfo.isSystemApp() || applicationInfo.isSystemExt() /* isSystem */, + applicationInfo.isVendor(), + applicationInfo.isProduct(), + isSameWithTargetSignature(overlayInternal.targetPackageName), + applicationInfo.isOdm(), + applicationInfo.isOem()); } catch (IOException e) { if (!frroPath.toFile().delete()) { Log.w(TAG, "Failed to delete file " + frroPath); @@ -237,6 +270,7 @@ public class OverlayManagerImpl { * * @param overlayName the specific name */ + @NonUiContext public void unregisterFabricatedOverlay(@NonNull String overlayName) { ensureBaseDir(); checkOverlayNameValid(overlayName); @@ -252,6 +286,46 @@ public class OverlayManagerImpl { } /** + * Commit the overlay manager transaction + * + * @param transaction the overlay manager transaction + */ + @NonUiContext + public void commit(@NonNull OverlayManagerTransaction transaction) + throws PackageManager.NameNotFoundException, IOException { + Objects.requireNonNull(transaction); + + for (Iterator<OverlayManagerTransaction.Request> it = transaction.iterator(); + it.hasNext(); ) { + final OverlayManagerTransaction.Request request = it.next(); + if (request.type == TYPE_REGISTER_FABRICATED) { + final FabricatedOverlayInternal fabricatedOverlayInternal = + Objects.requireNonNull( + request.extras.getParcelable( + BUNDLE_FABRICATED_OVERLAY, + FabricatedOverlayInternal.class)); + + // populate the mandatory data + if (TextUtils.isEmpty(fabricatedOverlayInternal.packageName)) { + fabricatedOverlayInternal.packageName = mContext.getPackageName(); + } else { + if (!TextUtils.equals( + fabricatedOverlayInternal.packageName, mContext.getPackageName())) { + throw new IllegalArgumentException("Unknown package name in transaction"); + } + } + + registerFabricatedOverlay(fabricatedOverlayInternal); + } else if (request.type == TYPE_UNREGISTER_FABRICATED) { + final OverlayIdentifier overlayIdentifier = Objects.requireNonNull(request.overlay); + unregisterFabricatedOverlay(overlayIdentifier.getOverlayName()); + } else { + throw new IllegalArgumentException("Unknown request in transaction " + request); + } + } + } + + /** * Get the list of overlays information for the target package name. * * @param targetPackage the target package name @@ -315,7 +389,13 @@ public class OverlayManagerImpl { @NonNull String targetPath, @NonNull String overlayPath, @NonNull String idmapPath, - @NonNull String overlayName) + @NonNull String overlayName, + boolean isSystem, + boolean isVendor, + boolean isProduct, + boolean isSameWithTargetSignature, + boolean isOdm, + boolean isOem) throws IOException; private static native FabricatedOverlayInfo getFabricatedOverlayInfo( diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 9cb2e68229f0..4b1753a82762 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -17,7 +17,7 @@ package com.android.internal.telephony; import android.telephony.BarringInfo; -import android.telephony.CallState; +import android.telephony.CallAttributes; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; @@ -62,7 +62,7 @@ oneway interface IPhoneStateListener { void onPhoneCapabilityChanged(in PhoneCapability capability); void onActiveDataSubIdChanged(in int subId); void onRadioPowerStateChanged(in int state); - void onCallStatesChanged(in List<CallState> callStateList); + void onCallAttributesChanged(in CallAttributes callAttributes); @SuppressWarnings(value={"untyped-collection"}) void onEmergencyNumberListChanged(in Map emergencyNumberList); void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber, int subscriptionId); diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 7ba26866ee03..c7fa757ac0b7 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -66,8 +66,8 @@ interface ITelephonyRegistry { void notifyCellLocationForSubscriber(in int subId, in CellIdentity cellLocation); @UnsupportedAppUsage void notifyCellInfo(in List<CellInfo> cellInfo); - void notifyPreciseCallState(int phoneId, int subId, in int[] callStates, in String[] imsCallIds, - in int[] imsCallServiceTypes, in int[] imsCallTypes); + void notifyPreciseCallState(int phoneId, int subId, int ringingCallState, + int foregroundCallState, int backgroundCallState); void notifyDisconnectCause(int phoneId, int subId, int disconnectCause, int preciseDisconnectCause); void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo); diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java index 1eb446eff171..f974d9d10efd 100644 --- a/core/java/com/android/internal/usb/DumpUtils.java +++ b/core/java/com/android/internal/usb/DumpUtils.java @@ -174,6 +174,9 @@ public class DumpUtils { } else { dump.write("supported_modes", UsbPortProto.SUPPORTED_MODES, UsbPort.modeToString(mode)); } + dump.write("supports_compliance_warnings", + UsbPortProto.SUPPORTS_COMPLIANCE_WARNINGS, + port.supportsComplianceWarnings()); dump.end(token); } @@ -250,6 +253,8 @@ public class DumpUtils { status.isPowerTransferLimited()); dump.write("usb_power_brick_status", UsbPortStatusProto.USB_POWER_BRICK_STATUS, UsbPort.powerBrickConnectionStatusToString(status.getPowerBrickConnectionStatus())); + dump.write("compliance_warning_status", UsbPortStatusProto.COMPLIANCE_WARNINGS_STRING, + UsbPort.complianceWarningsToString(status.getComplianceWarnings())); dump.end(token); } } diff --git a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp index df55e424f2ed..bba1760bc45c 100644 --- a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp +++ b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp @@ -123,8 +123,12 @@ public: bool callCreateIdmapFile(std::string& out_error, const std::string& targetPath, const std::string& overlayPath, const std::string& idmapPath, - const std::string& overlayName) { - return createIdmapFileFuncPtr_(out_error, targetPath, overlayPath, idmapPath, overlayName); + const std::string& overlayName, const bool isSystem, + const bool isVendor, const bool isProduct, + const bool isTargetSignature, const bool isOdm, const bool isOem) { + return createIdmapFileFuncPtr_(out_error, targetPath, overlayPath, idmapPath, overlayName, + isSystem, isVendor, isProduct, isTargetSignature, isOdm, + isOem); } bool callGetFabricatedOverlayInfo(std::string& out_error, const std::string& overlay_path, @@ -158,7 +162,10 @@ private: typedef bool (*CreateIdmapFileFunc)(std::string& out_error, const std::string& targetPath, const std::string& overlayPath, const std::string& idmapPath, - const std::string& overlayName); + const std::string& overlayName, const jboolean isSystem, + const jboolean isVendor, const jboolean isProduct, + const jboolean isSameWithTargetSignature, + const jboolean isOdm, const jboolean isOem); typedef bool (*GetFabricatedOverlayInfoFunc)(std::string& out_error, const std::string& overlay_path, @@ -295,7 +302,9 @@ static void CreateFrroFile(JNIEnv* env, jclass /*clazz*/, jstring jsFrroFilePath } static void CreateIdmapFile(JNIEnv* env, jclass /* clazz */, jstring jsTargetPath, - jstring jsOverlayPath, jstring jsIdmapPath, jstring jsOverlayName) { + jstring jsOverlayPath, jstring jsIdmapPath, jstring jsOverlayName, + jboolean isSystem, jboolean isVendor, jboolean isProduct, + jboolean isTargetSignature, jboolean isOdm, jboolean isOem) { DynamicLibraryLoader& dlLoader = EnsureDynamicLibraryLoader(env); if (!dlLoader) { jniThrowNullPointerException(env, "libidmap2 is not loaded"); @@ -327,7 +336,10 @@ static void CreateIdmapFile(JNIEnv* env, jclass /* clazz */, jstring jsTargetPat std::string err_result; if (!dlLoader.callCreateIdmapFile(err_result, targetPath.c_str(), overlayPath.c_str(), - idmapPath.c_str(), overlayName.c_str())) { + idmapPath.c_str(), overlayName.c_str(), + (isSystem == JNI_TRUE), (isVendor == JNI_TRUE), + (isProduct == JNI_TRUE), (isTargetSignature == JNI_TRUE), + (isOdm == JNI_TRUE), (isOem == JNI_TRUE))) { jniThrowException(env, kIOException, err_result.c_str()); return; } @@ -374,7 +386,7 @@ static const JNINativeMethod gOverlayManagerMethods[] = { {"createFrroFile", "(Ljava/lang/String;Landroid/os/FabricatedOverlayInternal;)V", reinterpret_cast<void*>(self_targeting::CreateFrroFile)}, {"createIdmapFile", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZZZZ)V", reinterpret_cast<void*>(self_targeting::CreateIdmapFile)}, {"getFabricatedOverlayInfo", "(Ljava/lang/String;)Landroid/os/FabricatedOverlayInfo;", reinterpret_cast<void*>(self_targeting::GetFabricatedOverlayInfo)}, diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto index df5e0a942ca7..607fd108c999 100644 --- a/core/proto/android/service/usb.proto +++ b/core/proto/android/service/usb.proto @@ -240,6 +240,7 @@ message UsbPortProto { // ID of the port. A device (eg: Chromebooks) might have multiple ports. optional string id = 1; repeated Mode supported_modes = 2; + optional bool supports_compliance_warnings = 3; } message UsbPortStatusProto { @@ -268,6 +269,7 @@ message UsbPortStatusProto { optional string usb_data_status = 7; optional bool is_power_transfer_limited = 8; optional string usb_power_brick_status = 9; + optional string compliance_warnings_string = 10; } message UsbPortStatusRoleCombinationProto { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5a7abcce6b75..ecc397974419 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -293,6 +293,7 @@ <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" /> <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" /> + <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE" /> diff --git a/core/tests/GameManagerTests/AndroidManifest.xml b/core/tests/GameManagerTests/AndroidManifest.xml index 6a01abee3e1c..f1ab6969e99f 100644 --- a/core/tests/GameManagerTests/AndroidManifest.xml +++ b/core/tests/GameManagerTests/AndroidManifest.xml @@ -17,7 +17,9 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.app.gamemanagertests" - android:sharedUserId="android.uid.system" > + android:sharedUserId="com.android.uid.test" > + + <uses-permission android:name="android.permission.MANAGE_GAME_MODE" /> <application android:appCategory="game"> <uses-library android:name="android.test.runner" /> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 48cfc87d7d55..e811bb67fb99 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -43,10 +43,12 @@ android_test { "mockwebserver", "guava", "androidx.core_core", + "androidx.core_core-ktx", "androidx.test.espresso.core", "androidx.test.ext.junit", "androidx.test.runner", "androidx.test.rules", + "junit-params", "kotlin-test", "mockito-target-minus-junit4", "ub-uiautomator", diff --git a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java b/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java index a648a885aea2..fc69f6914658 100644 --- a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java +++ b/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java @@ -17,19 +17,26 @@ package android.app.time; import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_RUNNING; +import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED; import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_RUNNING; +import static android.app.time.DetectorStatusTypes.DETECTION_ALGORITHM_STATUS_UNKNOWN; import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_CERTAIN; import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_IS_UNCERTAIN; import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_PRESENT; import static android.app.time.LocationTimeZoneAlgorithmStatus.PROVIDER_STATUS_NOT_READY; import static android.app.time.ParcelableTestSupport.assertEqualsAndHashCode; import static android.app.time.ParcelableTestSupport.assertRoundTripParcelable; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_NOT_APPLICABLE; import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_NOT_APPLICABLE; import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import android.app.time.LocationTimeZoneAlgorithmStatus.ProviderStatus; import android.service.timezone.TimeZoneProviderStatus; @@ -207,4 +214,114 @@ public class LocationTimeZoneAlgorithmStatusTest { assertEquals(status, LocationTimeZoneAlgorithmStatus.parseCommandlineArg(status.toString())); } + + @Test + public void testCouldEnableTelephonyFallback_notRunning() { + LocationTimeZoneAlgorithmStatus notRunning = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_RUNNING, + PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null); + assertFalse(notRunning.couldEnableTelephonyFallback()); + } + + @Test + public void testCouldEnableTelephonyFallback_unknown() { + // DETECTION_ALGORITHM_STATUS_UNKNOWN must never allow fallback + LocationTimeZoneAlgorithmStatus unknown = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_UNKNOWN, + PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null); + assertFalse(unknown.couldEnableTelephonyFallback()); + } + + @Test + public void testCouldEnableTelephonyFallback_notSupported() { + // DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED must never allow fallback + LocationTimeZoneAlgorithmStatus notSupported = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_NOT_SUPPORTED, + PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null); + assertFalse(notSupported.couldEnableTelephonyFallback()); + } + + @Test + public void testCouldEnableTelephonyFallback_running() { + // DETECTION_ALGORITHM_STATUS_RUNNING may allow fallback + + // Sample provider-reported statuses that do / do not enable fallback. + TimeZoneProviderStatus enableTelephonyFallbackProviderStatus = + new TimeZoneProviderStatus.Builder() + .setLocationDetectionDependencyStatus( + DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT) + .setConnectivityDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE) + .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_NOT_APPLICABLE) + .build(); + assertTrue(enableTelephonyFallbackProviderStatus.couldEnableTelephonyFallback()); + + TimeZoneProviderStatus notEnableTelephonyFallbackProviderStatus = + new TimeZoneProviderStatus.Builder() + .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE) + .setConnectivityDependencyStatus(DEPENDENCY_STATUS_NOT_APPLICABLE) + .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_NOT_APPLICABLE) + .build(); + assertFalse(notEnableTelephonyFallbackProviderStatus.couldEnableTelephonyFallback()); + + // Provider not ready: Never enable fallback + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_NOT_READY, null, PROVIDER_STATUS_NOT_READY, null); + assertFalse(status.couldEnableTelephonyFallback()); + } + + // Provider uncertain without reported status: Never enable fallback + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, null, PROVIDER_STATUS_NOT_READY, null); + assertFalse(status.couldEnableTelephonyFallback()); + } + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, null, PROVIDER_STATUS_NOT_PRESENT, null); + assertFalse(status.couldEnableTelephonyFallback()); + } + + // Provider uncertain with reported status: Fallback is based on the status for present + // providers that report their status. All present providers must have reported status and + // agree that fallback is a good idea. + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus, + PROVIDER_STATUS_NOT_READY, null); + assertFalse(status.couldEnableTelephonyFallback()); + } + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus, + PROVIDER_STATUS_NOT_PRESENT, null); + assertTrue(status.couldEnableTelephonyFallback()); + } + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus, + PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus); + assertTrue(status.couldEnableTelephonyFallback()); + } + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus, + PROVIDER_STATUS_IS_UNCERTAIN, notEnableTelephonyFallbackProviderStatus); + assertFalse(status.couldEnableTelephonyFallback()); + } + { + LocationTimeZoneAlgorithmStatus status = + new LocationTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_NOT_PRESENT, null, + PROVIDER_STATUS_IS_UNCERTAIN, enableTelephonyFallbackProviderStatus); + assertTrue(status.couldEnableTelephonyFallback()); + } + } } diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt new file mode 100644 index 000000000000..cfca0375bb96 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt @@ -0,0 +1,78 @@ +/* + * 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 android.content.res + +import androidx.core.util.forEach +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class FontScaleConverterFactoryTest { + + @Test + fun scale200IsTwiceAtSmallSizes() { + val table = FontScaleConverterFactory.forScale(2F)!! + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(2f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(16f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(20f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(10f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + @SmallTest + fun missingLookupTableReturnsNull() { + assertThat(FontScaleConverterFactory.forScale(3F)).isNull() + } + + @SmallTest + fun missingLookupTable105ReturnsNull() { + assertThat(FontScaleConverterFactory.forScale(1.05F)).isNull() + } + + @SmallTest + fun missingLookupTableNegativeReturnsNull() { + assertThat(FontScaleConverterFactory.forScale(-1F)).isNull() + } + + @SmallTest + fun unnecessaryFontScalesReturnsNull() { + assertThat(FontScaleConverterFactory.forScale(0F)).isNull() + assertThat(FontScaleConverterFactory.forScale(1F)).isNull() + assertThat(FontScaleConverterFactory.forScale(0.85F)).isNull() + } + + @SmallTest + fun tablesMatchAndAreMonotonicallyIncreasing() { + FontScaleConverterFactory.LOOKUP_TABLES.forEach { _, lookupTable -> + assertThat(lookupTable.mToDpValues).hasLength(lookupTable.mFromSpValues.size) + assertThat(lookupTable.mToDpValues).isNotEmpty() + + assertThat(lookupTable.mFromSpValues.asList()).isInStrictOrder() + assertThat(lookupTable.mToDpValues.asList()).isInStrictOrder() + + assertThat(lookupTable.mFromSpValues.asList()).containsNoDuplicates() + assertThat(lookupTable.mToDpValues.asList()).containsNoDuplicates() + } + } + + companion object { + private const val CONVERSION_TOLERANCE = 0.05f + } +} diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt new file mode 100644 index 000000000000..e405c55a53e3 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt @@ -0,0 +1,95 @@ +/* + * 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 android.content.res + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class FontScaleConverterTest { + + @Test + fun straightInterpolation() { + val table = createTable(8f to 8f, 10f to 10f, 20f to 20f) + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(10f) + assertThat(table.convertSpToDp(30F)).isWithin(CONVERSION_TOLERANCE).of(30f) + assertThat(table.convertSpToDp(20F)).isWithin(CONVERSION_TOLERANCE).of(20f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + @Test + fun interpolate200Percent() { + val table = createTable(8f to 16f, 10f to 20f, 30f to 60f) + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(2f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(16f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(20f) + assertThat(table.convertSpToDp(30F)).isWithin(CONVERSION_TOLERANCE).of(60f) + assertThat(table.convertSpToDp(20F)).isWithin(CONVERSION_TOLERANCE).of(40f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(10f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + @Test + fun interpolate150Percent() { + val table = createTable(2f to 3f, 10f to 15f, 20f to 30f, 100f to 150f) + assertThat(table.convertSpToDp(2F)).isWithin(CONVERSION_TOLERANCE).of(3f) + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1.5f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(12f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(15f) + assertThat(table.convertSpToDp(20F)).isWithin(CONVERSION_TOLERANCE).of(30f) + assertThat(table.convertSpToDp(50F)).isWithin(CONVERSION_TOLERANCE).of(75f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(7.5f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + @Test + fun pastEndsUsesLastScalingFactor() { + val table = createTable(8f to 16f, 10f to 20f, 30f to 60f) + assertThat(table.convertSpToDp(100F)).isWithin(CONVERSION_TOLERANCE).of(200f) + assertThat(table.convertSpToDp(31F)).isWithin(CONVERSION_TOLERANCE).of(62f) + assertThat(table.convertSpToDp(1000F)).isWithin(CONVERSION_TOLERANCE).of(2000f) + assertThat(table.convertSpToDp(2000F)).isWithin(CONVERSION_TOLERANCE).of(4000f) + assertThat(table.convertSpToDp(10000F)).isWithin(CONVERSION_TOLERANCE).of(20000f) + } + + @Test + fun negativeSpIsNegativeDp() { + val table = createTable(8f to 16f, 10f to 20f, 30f to 60f) + assertThat(table.convertSpToDp(-1F)).isWithin(CONVERSION_TOLERANCE).of(-2f) + assertThat(table.convertSpToDp(-8F)).isWithin(CONVERSION_TOLERANCE).of(-16f) + assertThat(table.convertSpToDp(-10F)).isWithin(CONVERSION_TOLERANCE).of(-20f) + assertThat(table.convertSpToDp(-30F)).isWithin(CONVERSION_TOLERANCE).of(-60f) + assertThat(table.convertSpToDp(-20F)).isWithin(CONVERSION_TOLERANCE).of(-40f) + assertThat(table.convertSpToDp(-5F)).isWithin(CONVERSION_TOLERANCE).of(-10f) + assertThat(table.convertSpToDp(-0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + private fun createTable(vararg pairs: Pair<Float, Float>) = + FontScaleConverter( + pairs.map { it.first }.toFloatArray(), + pairs.map { it.second }.toFloatArray() + ) + + companion object { + private const val CONVERSION_TOLERANCE = 0.05f + } +} diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java index c0325caf1425..8e63a0fe3364 100644 --- a/core/tests/coretests/src/android/os/EnvironmentTest.java +++ b/core/tests/coretests/src/android/os/EnvironmentTest.java @@ -47,29 +47,6 @@ public class EnvironmentTest { return InstrumentationRegistry.getContext(); } - /** - * Sets {@code mode} for the given {@code ops} and the given {@code uid}. - * - * <p>This method drops shell permission identity. - */ - private static void setAppOpsModeForUid(int uid, int mode, String... ops) { - if (ops == null) { - return; - } - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .adoptShellPermissionIdentity(); - try { - for (String op : ops) { - getContext().getSystemService(AppOpsManager.class).setUidMode(op, uid, mode); - } - } finally { - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .dropShellPermissionIdentity(); - } - } - @Before public void setUp() throws Exception { dir = getContext().getDir("testing", Context.MODE_PRIVATE); @@ -127,17 +104,4 @@ public class EnvironmentTest { Environment.buildPath(dir, "Taxes.pdf").createNewFile(); assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir)); } - - @Test - public void testIsExternalStorageManager() throws Exception { - assertFalse(Environment.isExternalStorageManager()); - try { - setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_ALLOWED, - AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE); - assertTrue(Environment.isExternalStorageManager()); - } finally { - setAppOpsModeForUid(Process.myUid(), AppOpsManager.MODE_DEFAULT, - AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE); - } - } } diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java index 9006cd91d616..0c1630e8fefd 100644 --- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java +++ b/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java @@ -16,15 +16,31 @@ package android.service.timezone; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT; import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS; import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_UNKNOWN; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED; import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN; import static org.junit.Assert.assertEquals; +import android.service.timezone.TimeZoneProviderStatus.DependencyStatus; +import android.service.timezone.TimeZoneProviderStatus.OperationStatus; + import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; /** Non-SDK tests. See CTS for SDK API tests. */ +@RunWith(JUnitParamsRunner.class) public class TimeZoneProviderStatusTest { @Test @@ -37,4 +53,51 @@ public class TimeZoneProviderStatusTest { assertEquals(status, TimeZoneProviderStatus.parseProviderStatus(status.toString())); } + + @Test + @Parameters(method = "couldEnableTelephonyFallbackParams") + public void couldEnableTelephonyFallback(@DependencyStatus int locationDetectionStatus, + @DependencyStatus int connectivityStatus, @OperationStatus int tzResolutionStatus) { + TimeZoneProviderStatus providerStatus = + new TimeZoneProviderStatus.Builder() + .setLocationDetectionDependencyStatus(locationDetectionStatus) + .setConnectivityDependencyStatus(connectivityStatus) + .setTimeZoneResolutionOperationStatus(tzResolutionStatus) + .build(); + boolean locationDetectionStatusCouldEnableFallback = + (locationDetectionStatus == DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT + || locationDetectionStatus == DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS); + boolean connectivityStatusCouldEnableFallback = + (connectivityStatus == DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT + || connectivityStatus == DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS); + boolean tzResolutionStatusCouldEnableFallback = false; + + assertEquals(locationDetectionStatusCouldEnableFallback + || connectivityStatusCouldEnableFallback + || tzResolutionStatusCouldEnableFallback, + providerStatus.couldEnableTelephonyFallback()); + } + + public static Integer[][] couldEnableTelephonyFallbackParams() { + List<Integer[]> params = new ArrayList<>(); + @DependencyStatus int[] dependencyStatuses = + IntStream.rangeClosed( + DEPENDENCY_STATUS_UNKNOWN, DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS).toArray(); + @OperationStatus int[] operationStatuses = + IntStream.rangeClosed(OPERATION_STATUS_UNKNOWN, OPERATION_STATUS_FAILED).toArray(); + + // Cartesian product: dependencyStatus x dependencyStatus x operationStatus + for (@DependencyStatus int locationDetectionStatus : dependencyStatuses) { + for (@DependencyStatus int connectivityStatus : dependencyStatuses) { + for (@OperationStatus int tzResolutionStatus : operationStatuses) { + params.add(new Integer[] { + locationDetectionStatus, + connectivityStatus, + tzResolutionStatus + }); + } + } + } + return params.toArray(new Integer[0][0]); + } } diff --git a/core/tests/coretests/src/android/util/TypedValueTest.kt b/core/tests/coretests/src/android/util/TypedValueTest.kt index 7a05d970de33..7d98a7d1faa1 100644 --- a/core/tests/coretests/src/android/util/TypedValueTest.kt +++ b/core/tests/coretests/src/android/util/TypedValueTest.kt @@ -16,16 +16,17 @@ package android.util +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import kotlin.math.abs +import kotlin.math.min +import kotlin.math.roundToInt import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock -import kotlin.math.abs -import kotlin.math.min -import kotlin.math.roundToInt @RunWith(AndroidJUnit4::class) class TypedValueTest { @@ -152,4 +153,19 @@ class TypedValueTest { val widthPx = TypedValue.complexToDimensionPixelSize(widthDimen, metrics) assertEquals(widthFloat.roundToInt(), widthPx) } -}
\ No newline at end of file + + @SmallTest + @Test + fun testNonLinearFontScaling_nullLookupFallsBackToScaledDensity() { + val metrics: DisplayMetrics = mock(DisplayMetrics::class.java) + val fontScale = 2f + metrics.density = 1f + metrics.scaledDensity = fontScale * metrics.density + metrics.fontScaleConverter = null + + assertThat(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, metrics)) + .isEqualTo(20f) + assertThat(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 50f, metrics)) + .isEqualTo(100f) + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java index ee1e10f9009e..7cbf3ffa509c 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java @@ -263,10 +263,24 @@ public class AccessibilityManagerTest { } private class MyAccessibilityProxy extends AccessibilityDisplayProxy { - // TODO(241429275): Will override A11yProxy methods in the future. MyAccessibilityProxy(int displayId, @NonNull List<AccessibilityServiceInfo> serviceInfos) { super(displayId, Executors.newSingleThreadExecutor(), serviceInfos); } + + @Override + public void onAccessibilityEvent(@NonNull AccessibilityEvent event) { + + } + + @Override + public void onProxyConnected() { + + } + + @Override + public void interrupt() { + + } } } diff --git a/core/tests/overlaytests/device_self_targeting/Android.bp b/core/tests/overlaytests/device_self_targeting/Android.bp index 82998dbdae27..063c5694ab5b 100644 --- a/core/tests/overlaytests/device_self_targeting/Android.bp +++ b/core/tests/overlaytests/device_self_targeting/Android.bp @@ -29,6 +29,7 @@ android_test { "androidx.test.rules", "androidx.test.runner", "androidx.test.ext.junit", + "mockito-target-minus-junit4", "truth-prebuilt", ], diff --git a/core/tests/overlaytests/device_self_targeting/res/values/overlayable.xml b/core/tests/overlaytests/device_self_targeting/res/values/overlayable.xml new file mode 100644 index 000000000000..5cc214d859d2 --- /dev/null +++ b/core/tests/overlaytests/device_self_targeting/res/values/overlayable.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ 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. + --> + +<resources> + <overlayable name="PublicOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="public"> + <item type="color" name="public_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="SignatureOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="signature"> + <item type="color" name="mycolor" /> + <item type="color" name="signature_overlayable_color" /> + <item type="string" name="mystring" /> + <item type="drawable" name="mydrawable" /> + </policy> + </overlayable> + + <overlayable name="SystemAppOverlayable" actor="overlay://theme"> + <!-- The app in system partition can overlay the below resources --> + <policy type="system"> + <item type="color" name="system_app_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="OdmOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="odm"> + <item type="color" name="odm_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="OemOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="oem"> + <item type="color" name="oem_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="VendorOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="vendor"> + <item type="color" name="vendor_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="ProductOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="product"> + <item type="color" name="product_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="ActorOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="actor"> + <item type="color" name="actor_overlayable_color" /> + </policy> + </overlayable> + + <overlayable name="ConfigOverlayable" actor="overlay://theme"> + <!-- The app with the same signature can overlay the below resources --> + <policy type="config_signature"> + <item type="color" name="config_overlayable_color" /> + </policy> + </overlayable> + +</resources> diff --git a/core/tests/overlaytests/device_self_targeting/res/values/values.xml b/core/tests/overlaytests/device_self_targeting/res/values/values.xml index f0b4a6fe8969..d82de9717717 100644 --- a/core/tests/overlaytests/device_self_targeting/res/values/values.xml +++ b/core/tests/overlaytests/device_self_targeting/res/values/values.xml @@ -17,4 +17,14 @@ <resources> <color name="mycolor">#ff112233</color> <string name="mystring">hello</string> + + <color name="public_overlayable_color">#000</color> + <color name="signature_overlayable_color">#000</color> + <color name="system_app_overlayable_color">#000</color> + <color name="odm_overlayable_color">#000</color> + <color name="oem_overlayable_color">#000</color> + <color name="vendor_overlayable_color">#000</color> + <color name="product_overlayable_color">#000</color> + <color name="actor_overlayable_color">#000</color> + <color name="config_overlayable_color">#000</color> </resources> diff --git a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java index ca584106a910..40d0bef2fb0f 100644 --- a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java +++ b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java @@ -17,15 +17,24 @@ package com.android.overlaytest; import static android.content.Context.MODE_PRIVATE; +import static android.content.pm.PackageManager.SIGNATURE_NO_MATCH; import static com.android.internal.content.om.OverlayManagerImpl.SELF_TARGET; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.content.Context; import android.content.ContextWrapper; +import android.content.om.FabricatedOverlay; import android.content.om.OverlayInfo; +import android.content.om.OverlayManagerTransaction; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Color; import android.os.FabricatedOverlayInternal; @@ -72,11 +81,23 @@ public class OverlayManagerImplTest { private static final String TARGET_COLOR_RES = "color/mycolor"; private static final String TARGET_STRING_RES = "string/mystring"; private static final String TARGET_DRAWABLE_RES = "drawable/mydrawable"; + private static final String PUBLIC_OVERLAYABLE = "PublicOverlayable"; + private static final String SIGNATURE_OVERLAYABLE = "SignatureOverlayable"; + private static final String SYSTEM_APP_OVERLAYABLE = "SystemAppOverlayable"; + private static final String ODM_OVERLAYABLE = "OdmOverlayable"; + private static final String OEM_OVERLAYABLE = "OemOverlayable"; + private static final String VENDOR_OVERLAYABLE = "VendorOverlayable"; + private static final String PRODUCT_OVERLAYABLE = "ProductOverlayable"; + private static final String ACTOR_OVERLAYABLE = "ActorOverlayable"; + private static final String CONFIG_OVERLAYABLE = "ConfigOverlayable"; private Context mContext; private OverlayManagerImpl mOverlayManagerImpl; private String mOverlayName; + private PackageManager mMockPackageManager; + private ApplicationInfo mMockApplicationInfo; + @Rule public TestName mTestName = new TestName(); @Rule public Expect expect = Expect.create(); @@ -111,7 +132,36 @@ public class OverlayManagerImplTest { public void setUp() throws IOException { clearDir(); mOverlayName = mTestName.getMethodName(); - mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + mMockApplicationInfo = mock(ApplicationInfo.class); + when(mMockApplicationInfo.isSystemApp()).thenReturn(false); + when(mMockApplicationInfo.isSystemExt()).thenReturn(false); + when(mMockApplicationInfo.isOdm()).thenReturn(false); + when(mMockApplicationInfo.isOem()).thenReturn(false); + when(mMockApplicationInfo.isVendor()).thenReturn(false); + when(mMockApplicationInfo.isProduct()).thenReturn(false); + when(mMockApplicationInfo.getBaseCodePath()).thenReturn( + context.getApplicationInfo().getBaseCodePath()); + mMockApplicationInfo.sourceDir = context.getApplicationInfo().sourceDir; + + mMockPackageManager = mock(PackageManager.class); + when(mMockPackageManager.checkSignatures(anyString(), anyString())) + .thenReturn(SIGNATURE_NO_MATCH); + + mContext = + new ContextWrapper(context) { + @Override + public ApplicationInfo getApplicationInfo() { + return mMockApplicationInfo; + } + + @Override + public PackageManager getPackageManager() { + return mMockPackageManager; + } + }; + mOverlayManagerImpl = new OverlayManagerImpl(mContext); } @@ -144,12 +194,14 @@ public class OverlayManagerImplTest { private <T> FabricatedOverlayInternal createOverlayWithName( @NonNull String overlayName, + @NonNull String targetOverlayable, @NonNull String targetPackageName, @NonNull List<Pair<String, Pair<String, T>>> entryDefinitions) { final String packageName = mContext.getPackageName(); FabricatedOverlayInternal overlayInternal = new FabricatedOverlayInternal(); overlayInternal.overlayName = overlayName; overlayInternal.targetPackageName = targetPackageName; + overlayInternal.targetOverlayable = targetOverlayable; overlayInternal.packageName = packageName; addOverlayEntry(overlayInternal, entryDefinitions); @@ -162,6 +214,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SYSTEM_APP_OVERLAYABLE, "android", List.of(Pair.create("color/white", Pair.create(null, Color.BLACK)))); @@ -190,6 +243,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_COLOR_RES, Pair.create(null, Color.WHITE)))); @@ -215,6 +269,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_STRING_RES, Pair.create(null, "HELLO")))); @@ -242,6 +297,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_DRAWABLE_RES, Pair.create(null, parcelFileDescriptor)))); @@ -268,6 +324,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create("color/not_existed", Pair.create(null, "HELLO")))); @@ -301,6 +358,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_COLOR_RES, Pair.create(null, Color.WHITE)))); mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); @@ -309,6 +367,7 @@ public class OverlayManagerImplTest { overlayInternal = createOverlayWithName( secondOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_COLOR_RES, Pair.create(null, Color.WHITE)))); mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); @@ -341,6 +400,7 @@ public class OverlayManagerImplTest { FabricatedOverlayInternal overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_COLOR_RES, Pair.create(null, Color.WHITE)))); mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); @@ -349,6 +409,7 @@ public class OverlayManagerImplTest { overlayInternal = createOverlayWithName( mOverlayName, + SIGNATURE_OVERLAYABLE, mContext.getPackageName(), List.of(Pair.create(TARGET_COLOR_RES, Pair.create(null, Color.WHITE)))); mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); @@ -392,6 +453,40 @@ public class OverlayManagerImplTest { } @Test + public void commit_withNullTransaction_shouldFail() { + assertThrows(NullPointerException.class, () -> mOverlayManagerImpl.commit(null)); + } + + @Test + public void commitRegisterOverlay_fromOtherBuilder_shouldWork() + throws PackageManager.NameNotFoundException, IOException { + FabricatedOverlay overlay = + new FabricatedOverlay.Builder( + mContext.getPackageName(), mOverlayName, mContext.getPackageName()) + .setTargetOverlayable(SIGNATURE_OVERLAYABLE) + .setResourceValue( + TARGET_COLOR_RES, TypedValue.TYPE_INT_COLOR_ARGB8, Color.WHITE) + .build(); + OverlayManagerTransaction transaction = + new OverlayManagerTransaction.Builder().registerFabricatedOverlay(overlay).build(); + + mOverlayManagerImpl.commit(transaction); + + final List<OverlayInfo> overlayInfos = + mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()); + final int firstNumberOfOverlays = overlayInfos.size(); + expect.that(firstNumberOfOverlays).isEqualTo(1); + final OverlayInfo overlayInfo = overlayInfos.get(0); + expect.that(overlayInfo).isNotNull(); + Truth.assertThat(expect.hasFailures()).isFalse(); + expect.that(overlayInfo.isFabricated()).isTrue(); + expect.that(overlayInfo.getOverlayName()).isEqualTo(mOverlayName); + expect.that(overlayInfo.getPackageName()).isEqualTo(mContext.getPackageName()); + expect.that(overlayInfo.getTargetPackageName()).isEqualTo(mContext.getPackageName()); + expect.that(overlayInfo.getUserId()).isEqualTo(mContext.getUserId()); + } + + @Test public void newOverlayManagerImpl_forOtherUser_shouldFail() { Context fakeContext = new ContextWrapper(mContext) { @@ -408,4 +503,177 @@ public class OverlayManagerImplTest { assertThrows(SecurityException.class, () -> new OverlayManagerImpl(fakeContext)); } + + FabricatedOverlayInternal prepareFabricatedOverlayInternal( + String targetOverlayableName, String targetEntryName) { + return createOverlayWithName( + mOverlayName, + targetOverlayableName, + mContext.getPackageName(), + List.of( + Pair.create( + targetEntryName, + Pair.create(null, Color.WHITE)))); + } + + @Test + public void registerOverlayOnSystemOverlayable_selfIsNotSystemApp_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + SYSTEM_APP_OVERLAYABLE, + "color/system_app_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnOdmOverlayable_selfIsNotOdm_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + ODM_OVERLAYABLE, + "color/odm_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnOemOverlayable_selfIsNotOem_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + OEM_OVERLAYABLE, + "color/oem_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnVendorOverlayable_selfIsNotVendor_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + VENDOR_OVERLAYABLE, + "color/vendor_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnProductOverlayable_selfIsNotProduct_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + PRODUCT_OVERLAYABLE, + "color/product_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnActorOverlayable_notSupport_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + ACTOR_OVERLAYABLE, + "color/actor_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnConfigOverlayable_notSupport_shouldFail() { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + CONFIG_OVERLAYABLE, + "color/config_overlayable_color"); + + assertThrows( + IOException.class, + () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); + } + + @Test + public void registerOverlayOnPublicOverlayable_shouldAlwaysSucceed() + throws PackageManager.NameNotFoundException, IOException { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + PUBLIC_OVERLAYABLE, + "color/public_overlayable_color"); + + mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); + + assertThat(mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()).size()) + .isEqualTo(1); + } + + @Test + public void registerOverlayOnSystemOverlayable_selfIsSystemApp_shouldSucceed() + throws PackageManager.NameNotFoundException, IOException { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + SYSTEM_APP_OVERLAYABLE, + "color/system_app_overlayable_color"); + when(mMockApplicationInfo.isSystemApp()).thenReturn(true); + when(mMockApplicationInfo.isSystemExt()).thenReturn(true); + + mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); + + assertThat(mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()).size()) + .isEqualTo(1); + } + + @Test + public void registerOverlayOnOdmOverlayable_selfIsOdm_shouldSucceed() + throws PackageManager.NameNotFoundException, IOException { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + ODM_OVERLAYABLE, + "color/odm_overlayable_color"); + when(mMockApplicationInfo.isOdm()).thenReturn(true); + + mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); + + assertThat(mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()).size()) + .isEqualTo(1); + } + + @Test + public void registerOverlayOnOemOverlayable_selfIsOem_shouldSucceed() + throws PackageManager.NameNotFoundException, IOException { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + OEM_OVERLAYABLE, + "color/oem_overlayable_color"); + when(mMockApplicationInfo.isOem()).thenReturn(true); + + mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); + + assertThat(mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()).size()) + .isEqualTo(1); + } + + @Test + public void registerOverlayOnVendorOverlayable_selfIsVendor_shouldSucceed() + throws PackageManager.NameNotFoundException, IOException { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + VENDOR_OVERLAYABLE, + "color/vendor_overlayable_color"); + when(mMockApplicationInfo.isVendor()).thenReturn(true); + + mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); + + assertThat(mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()).size()) + .isEqualTo(1); + } + + @Test + public void registerOverlayOnProductOverlayable_selfIsProduct_shouldSucceed() + throws PackageManager.NameNotFoundException, IOException { + final FabricatedOverlayInternal overlayInternal = prepareFabricatedOverlayInternal( + PRODUCT_OVERLAYABLE, + "color/product_overlayable_color"); + when(mMockApplicationInfo.isProduct()).thenReturn(true); + + mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal); + + assertThat(mOverlayManagerImpl.getOverlayInfosForTarget(mContext.getPackageName()).size()) + .isEqualTo(1); + } } diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 980f63b2ae6c..59a0f7b88937 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -221,13 +221,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { /** * @hide - * Mute state used for the initial state and when API is accessed without permission. - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public static final int MUTED_BY_UNKNOWN = -1; - /** - * @hide * Flag used when muted by master volume. */ @SystemApi @@ -317,7 +310,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { mPlayerType = pic.mPlayerType; mClientUid = uid; mClientPid = pid; - mMutedState = MUTED_BY_UNKNOWN; + mMutedState = 0; mDeviceId = PLAYER_DEVICEID_INVALID; mPlayerState = PLAYER_STATE_IDLE; mPlayerAttr = pic.mAttributes; @@ -366,7 +359,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { anonymCopy.mPlayerAttr = builder.build(); anonymCopy.mDeviceId = in.mDeviceId; // anonymized data - anonymCopy.mMutedState = MUTED_BY_UNKNOWN; + anonymCopy.mMutedState = 0; anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN; anonymCopy.mClientUid = PLAYER_UPID_INVALID; anonymCopy.mClientPid = PLAYER_UPID_INVALID; @@ -435,14 +428,13 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted() { - return mMutedState != 0 && mMutedState != MUTED_BY_UNKNOWN; + return mMutedState != 0; } /** * @hide * Returns a bitmask expressing the mute state as a combination of MUTED_BY_* flags. - * <br>Note that if the mute state is not set the result will be {@link #MUTED_BY_UNKNOWN}. A - * value of 0 represents a player which is not muted. + * <br>A value of 0 corresponds to an unmuted player. * @return the mute state. */ @SystemApi @@ -602,11 +594,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { } private boolean isMuteAffectingActiveState() { - if (mMutedState == MUTED_BY_UNKNOWN) { - // mute state is not set, therefore it will not affect the active state - return false; - } - return (mMutedState & MUTED_BY_CLIENT_VOLUME) != 0 || (mMutedState & MUTED_BY_VOLUME_SHAPER) != 0 || (mMutedState & MUTED_BY_APP_OPS) != 0; @@ -726,9 +713,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { "/").append(mClientPid).append(" state:").append( toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append( " sessionId:").append(mSessionId).append(" mutedState:"); - if (mMutedState == MUTED_BY_UNKNOWN) { - apcToString.append("unknown "); - } else if (mMutedState == 0) { + if (mMutedState == 0) { apcToString.append("none "); } else { if ((mMutedState & MUTED_BY_MASTER) != 0) { diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 8a03afb77942..d6fe68253be6 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -86,8 +86,10 @@ public abstract class Image implements AutoCloseable { * * <p> * The format is one of the values from - * {@link android.graphics.ImageFormat ImageFormat}. The mapping between the - * formats and the planes is as follows: + * {@link android.graphics.ImageFormat ImageFormat}, + * {@link android.graphics.PixelFormat PixelFormat}, or + * {@link android.hardware.HardwareBuffer HardwareBuffer}. The mapping between the + * formats and the planes is as follows (any formats not listed will have 1 plane): * </p> * * <table> @@ -171,15 +173,18 @@ public abstract class Image implements AutoCloseable { * </tr> * <tr> * <td>{@link android.graphics.ImageFormat#YCBCR_P010 YCBCR_P010}</td> - * <td>1</td> + * <td>3</td> * <td>P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane - * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit - * little-endian value, with the lower 6 bits set to zero. + * followed by a Wx(H/2) Cb and Cr planes. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. Since this is guaranteed to be + * a semi-planar format, the Cb plane can also be treated as an interleaved Cb/Cr plane. * </td> * </tr> * </table> * * @see android.graphics.ImageFormat + * @see android.graphics.PixelFormat + * @see android.hardware.HardwareBuffer */ public abstract int getFormat(); diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 538e64cf095c..e78dc31646ca 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -89,6 +89,7 @@ public class Ringtone { .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); + private boolean mPreferBuiltinDevice; // playback properties, use synchronized with mPlaybackSettingsLock private boolean mIsLooping = false; private float mVolume = 1.0f; @@ -157,6 +158,37 @@ public class Ringtone { } /** + * Finds the output device of type {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}. This device is + * the one on which outgoing audio for SIM calls is played. + * + * @param audioManager the audio manage. + * @return the {@link AudioDeviceInfo} corresponding to the builtin device, or {@code null} if + * none can be found. + */ + private AudioDeviceInfo getBuiltinDevice(AudioManager audioManager) { + AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); + for (AudioDeviceInfo device : deviceList) { + if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) { + return device; + } + } + return null; + } + + /** + * Sets the preferred device of the ringtong playback to the built-in device. + * + * @hide + */ + public boolean preferBuiltinDevice(boolean enable) { + mPreferBuiltinDevice = enable; + if (mLocalPlayer == null) { + return true; + } + return mLocalPlayer.setPreferredDevice(getBuiltinDevice(mAudioManager)); + } + + /** * Creates a local media player for the ringtone using currently set attributes. * @return true if media player creation succeeded or is deferred, * false if it did not succeed and can't be tried remotely. @@ -174,6 +206,8 @@ public class Ringtone { try { mLocalPlayer.setDataSource(mContext, mUri); mLocalPlayer.setAudioAttributes(mAudioAttributes); + mLocalPlayer.setPreferredDevice( + mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null); synchronized (mPlaybackSettingsLock) { applyPlaybackProperties_sync(); } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index fab63aa18097..7039a3e01ac6 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -998,6 +998,7 @@ public class Tuner implements AutoCloseable { private native int nativeScan(int settingsType, FrontendSettings settings, int scanType); private native int nativeStopScan(); private native int nativeSetLnb(Lnb lnb); + private native boolean nativeIsLnaSupported(); private native int nativeSetLna(boolean enable); private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes); private native Integer nativeGetAvSyncHwId(Filter filter); @@ -1382,11 +1383,32 @@ public class Tuner implements AutoCloseable { } /** + * Is Low Noise Amplifier (LNA) supported by the Tuner. + * + * <p>This API is only supported by Tuner HAL 3.0 or higher. + * Unsupported version would throw UnsupportedOperationException. Use + * {@link TunerVersionChecker#getTunerVersion()} to check the version. + * + * @return {@code true} if supported, otherwise {@code false}. + * @throws UnsupportedOperationException if the Tuner HAL version is lower than 3.0 + * @see android.media.tv.tuner.TunerVersionChecker#TUNER_VERSION_3_0 + */ + public boolean isLnaSupported() { + if (!TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_3_0, "isLnaSupported")) { + throw new UnsupportedOperationException("Tuner HAL version " + + TunerVersionChecker.getTunerVersion() + " doesn't support this method."); + } + return nativeIsLnaSupported(); + } + + /** * Enable or Disable Low Noise Amplifier (LNA). * * @param enable {@code true} to activate LNA module; {@code false} to deactivate LNA. * - * @return result status of the operation. + * @return result status of the operation. {@link #RESULT_UNAVAILABLE} if the device doesn't + * support LNA. */ @Result public int setLnaEnabled(boolean enable) { diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index a028c040891b..2afa4d1fdefe 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -1819,6 +1819,13 @@ int JTuner::setLnb(sp<LnbClient> lnbClient) { return (int)result; } +bool JTuner::isLnaSupported() { + if (sTunerClient == nullptr) { + return (int)Result::NOT_INITIALIZED; + } + return sTunerClient->isLnaSupported(); +} + int JTuner::setLna(bool enable) { if (sTunerClient == nullptr) { return (int)Result::NOT_INITIALIZED; @@ -3562,6 +3569,11 @@ static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jobject lnb return tuner->setLnb(lnbClient); } +static bool android_media_tv_Tuner_is_lna_supported(JNIEnv *env, jobject thiz) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->isLnaSupported(); +} + static int android_media_tv_Tuner_set_lna(JNIEnv *env, jobject thiz, jboolean enable) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->setLna(enable); @@ -4810,6 +4822,7 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_scan }, { "nativeStopScan", "()I", (void *)android_media_tv_Tuner_stop_scan }, { "nativeSetLnb", "(Landroid/media/tv/tuner/Lnb;)I", (void *)android_media_tv_Tuner_set_lnb }, + { "nativeIsLnaSupported", "()Z", (void *)android_media_tv_Tuner_is_lna_supported }, { "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna }, { "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;", (void *)android_media_tv_Tuner_get_frontend_status }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index c74b2df6c178..2b69e89a0a2f 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -189,6 +189,7 @@ struct JTuner : public RefBase { int scan(const FrontendSettings& settings, FrontendScanType scanType); int stopScan(); int setLnb(sp<LnbClient> lnbClient); + bool isLnaSupported(); int setLna(bool enable); jobject openLnbByHandle(int handle); jobject openLnbByName(jstring name); diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index 8515874c022f..ab28fb4eca9a 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -210,4 +210,17 @@ int TunerClient::getMaxNumberOfFrontends(FrontendType frontendType) { return -1; } +bool TunerClient::isLnaSupported() { + if (mTunerService != nullptr) { + bool lnaSupported; + Status s = mTunerService->isLnaSupported(&lnaSupported); + if (!s.isOk()) { + return false; + } + return lnaSupported; + } + + return false; +} + } // namespace android diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index 5410c1b185f5..3f8b21cb8899 100644 --- a/media/jni/tuner/TunerClient.h +++ b/media/jni/tuner/TunerClient.h @@ -148,6 +148,11 @@ public: */ int getMaxNumberOfFrontends(FrontendType frontendType); + /** + * Is Low Noise Amplifier (LNA) supported. + */ + bool isLnaSupported(); + private: /** * An AIDL Tuner Service assigned at the first time the Tuner Client connects with diff --git a/packages/CredentialManager/res/drawable/ic_other_sign_in.xml b/packages/CredentialManager/res/drawable/ic_other_sign_in.xml new file mode 100644 index 000000000000..81501972d3ba --- /dev/null +++ b/packages/CredentialManager/res/drawable/ic_other_sign_in.xml @@ -0,0 +1,36 @@ +<!-- + ~ 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. + --> + +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="VectorPath" + android:name="vector" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:name="path" + android:pathData="M 20 19 L 12 19 L 12 21 L 20 21 C 21.1 21 22 20.1 22 19 L 22 5 C 22 3.9 21.1 3 20 3 L 12 3 L 12 5 L 20 5 L 20 19 Z" + android:fillColor="#000" + android:strokeWidth="1"/> + <path + android:name="path_1" + android:pathData="M 12 7 L 10.6 8.4 L 13.2 11 L 8.85 11 C 8.42 9.55 7.09 8.5 5.5 8.5 C 3.57 8.5 2 10.07 2 12 C 2 13.93 3.57 15.5 5.5 15.5 C 7.09 15.5 8.42 14.45 8.85 13 L 13.2 13 L 10.6 15.6 L 12 17 L 17 12 L 12 7 Z M 5.5 13.5 C 4.67 13.5 4 12.83 4 12 C 4 11.17 4.67 10.5 5.5 10.5 C 6.33 10.5 7 11.17 7 12 C 7 12.83 6.33 13.5 5.5 13.5 Z" + android:fillColor="#000" + android:strokeWidth="1"/> +</vector>
\ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/ic_password.xml b/packages/CredentialManager/res/drawable/ic_password.xml new file mode 100644 index 000000000000..bf3056a115c1 --- /dev/null +++ b/packages/CredentialManager/res/drawable/ic_password.xml @@ -0,0 +1,31 @@ +<!-- + ~ 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. + --> + +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="VectorPath" + android:name="vector" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:name="path" + android:pathData="M 8.71 10.29 C 8.52 10.1 8.28 10 8 10 L 7.75 10 L 7.75 8.75 C 7.75 7.98 7.48 7.33 6.95 6.8 C 6.42 6.27 5.77 6 5 6 C 4.23 6 3.58 6.27 3.05 6.8 C 2.52 7.33 2.25 7.98 2.25 8.75 L 2.25 10 L 2 10 C 1.72 10 1.48 10.1 1.29 10.29 C 1.1 10.48 1 10.72 1 11 L 1 16 C 1 16.28 1.1 16.52 1.29 16.71 C 1.48 16.9 1.72 17 2 17 L 8 17 C 8.28 17 8.52 16.9 8.71 16.71 C 8.9 16.52 9 16.28 9 16 L 9 11 C 9 10.72 8.9 10.48 8.71 10.29 Z M 6.25 10 L 3.75 10 L 3.75 8.75 C 3.75 8.4 3.87 8.1 4.11 7.86 C 4.35 7.62 4.65 7.5 5 7.5 C 5.35 7.5 5.65 7.62 5.89 7.86 C 6.13 8.1 6.25 8.4 6.25 8.75 L 6.25 10 Z M 10 14 L 23 14 L 23 16 L 10 16 Z M 21.5 9 C 21.102 9 20.721 9.158 20.439 9.439 C 20.158 9.721 20 10.102 20 10.5 C 20 10.898 20.158 11.279 20.439 11.561 C 20.721 11.842 21.102 12 21.5 12 C 21.898 12 22.279 11.842 22.561 11.561 C 22.842 11.279 23 10.898 23 10.5 C 23 10.102 22.842 9.721 22.561 9.439 C 22.279 9.158 21.898 9 21.5 9 Z M 16.5 9 C 16.102 9 15.721 9.158 15.439 9.439 C 15.158 9.721 15 10.102 15 10.5 C 15 10.898 15.158 11.279 15.439 11.561 C 15.721 11.842 16.102 12 16.5 12 C 16.898 12 17.279 11.842 17.561 11.561 C 17.842 11.279 18 10.898 18 10.5 C 18 10.102 17.842 9.721 17.561 9.439 C 17.279 9.158 16.898 9 16.5 9 Z M 11.5 9 C 11.102 9 10.721 9.158 10.439 9.439 C 10.158 9.721 10 10.102 10 10.5 C 10 10.898 10.158 11.279 10.439 11.561 C 10.721 11.842 11.102 12 11.5 12 C 11.898 12 12.279 11.842 12.561 11.561 C 12.842 11.279 13 10.898 13 10.5 C 13 10.102 12.842 9.721 12.561 9.439 C 12.279 9.158 11.898 9 11.5 9 Z" + android:fillColor="#000" + android:strokeWidth="1"/> +</vector>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 1ee2a26d3acf..c4e9b5710b69 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name">CredentialManager</string> + <!-- The name of this application. Credential Manager is a service that centralizes and provides + access to a user's credentials used to sign in to various apps. [CHAR LIMIT=80] --> + <string name="app_name">Credential Manager</string> <string name="string_cancel">Cancel</string> <string name="string_continue">Continue</string> <string name="string_more_options">More options</string> @@ -34,6 +36,7 @@ <string name="passkey">passkey</string> <string name="password">password</string> <string name="sign_ins">sign-ins</string> + <string name="passkey_before_subtitle">Passkey</string> <string name="another_device">Another device</string> <string name="other_password_manager">Other password managers</string> <!-- TODO: Check the wording here. --> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 2bede9acaed2..530f1c467a76 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -39,16 +39,15 @@ import android.graphics.drawable.Icon import android.os.Binder import android.os.Bundle import android.os.ResultReceiver +import android.service.credentials.CredentialProviderService import com.android.credentialmanager.createflow.ActiveEntry import com.android.credentialmanager.createflow.CreateCredentialUiState import com.android.credentialmanager.createflow.CreateScreenState import com.android.credentialmanager.createflow.EnabledProviderInfo -import com.android.credentialmanager.createflow.RequestDisplayInfo import com.android.credentialmanager.getflow.GetCredentialUiState import com.android.credentialmanager.getflow.GetScreenState -import com.android.credentialmanager.jetpack.developer.CreateCredentialRequest.Companion.createFrom -import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle +import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL // Consider repo per screen, similar to view model? @@ -66,7 +65,7 @@ class CredentialManagerRepo( requestInfo = intent.extras?.getParcelable( RequestInfo.EXTRA_REQUEST_INFO, RequestInfo::class.java - ) ?: testCreateRequestInfo() + ) ?: testCreatePasskeyRequestInfo() providerEnabledList = when (requestInfo.type) { RequestInfo.TYPE_CREATE -> @@ -136,9 +135,10 @@ class CredentialManagerRepo( } fun createCredentialInitialUiState(): CreateCredentialUiState { + val requestDisplayInfo = CreateFlowUtils.toRequestDisplayInfo(requestInfo, context) val providerEnabledList = CreateFlowUtils.toEnabledProviderList( // Handle runtime cast error - providerEnabledList as List<CreateCredentialProviderData>, context) + providerEnabledList as List<CreateCredentialProviderData>, requestDisplayInfo, context) val providerDisabledList = CreateFlowUtils.toDisabledProviderList( // Handle runtime cast error providerDisabledList as List<DisabledProviderData>, context) @@ -147,21 +147,6 @@ class CredentialManagerRepo( providerEnabledList.forEach{providerInfo -> providerInfo.createOptions = providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed() if (providerInfo.isDefault) {hasDefault = true; defaultProvider = providerInfo} } - // TODO: covert from real requestInfo for create passkey - var requestDisplayInfo = RequestDisplayInfo( - "beckett-bakert@gmail.com", - "Elisa Beckett", - TYPE_PUBLIC_KEY_CREDENTIAL, - "tribank") - val createCredentialRequest = requestInfo.createCredentialRequest - val createCredentialRequestJetpack = createCredentialRequest?.let { createFrom(it) } - if (createCredentialRequestJetpack is CreatePasswordRequest) { - requestDisplayInfo = RequestDisplayInfo( - createCredentialRequestJetpack.id, - createCredentialRequestJetpack.password, - TYPE_PASSWORD_CREDENTIAL, - "tribank") - } return CreateCredentialUiState( enabledProviders = providerEnabledList, disabledProviders = providerDisabledList, @@ -390,11 +375,12 @@ class CredentialManagerRepo( intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_ONE_SHOT)) val createPasswordRequest = android.service.credentials.CreateCredentialRequest( - context.applicationInfo.packageName, - "PASSWORD", - toBundle("beckett-bakert@gmail.com", "password123") + context.applicationInfo.packageName, + TYPE_PASSWORD_CREDENTIAL, + toBundle("beckett-bakert@gmail.com", "password123") ) - val fillInIntent = Intent().putExtra("create_request_params", createPasswordRequest) + val fillInIntent = Intent().putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST, + createPasswordRequest) val slice = Slice.Builder( Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1) @@ -438,8 +424,42 @@ class CredentialManagerRepo( ) } - private fun testCreateRequestInfo(): RequestInfo { - val data = toBundle("beckett-bakert@gmail.com", "password123") + private fun testCreatePasskeyRequestInfo(): RequestInfo { + val request = CreatePublicKeyCredentialRequest("{\"extensions\": {\n" + + " \"webauthn.loc\": true\n" + + " },\n" + + " \"attestation\": \"direct\",\n" + + " \"challenge\": \"-rSQHXSQUdaK1N-La5bE-JPt6EVAW4SxX1K_tXhZ_Gk\",\n" + + " \"user\": {\n" + + " \"displayName\": \"testName\",\n" + + " \"name\": \"credManTesting@gmail.com\",\n" + + " \"id\": \"eD4o2KoXLpgegAtnM5cDhhUPvvk2\"\n" + + " },\n" + + " \"excludeCredentials\": [],\n" + + " \"rp\": {\n" + + " \"name\": \"Address Book\",\n" + + " \"id\": \"addressbook-c7876.uc.r.appspot.com\"\n" + + " },\n" + + " \"timeout\": 60000,\n" + + " \"pubKeyCredParams\": [\n" + + " {\n" + + " \"type\": \"public-key\",\n" + + " \"alg\": -7\n" + + " },\n" + + " {\n" + + " \"type\": \"public-key\",\n" + + " \"alg\": -257\n" + + " },\n" + + " {\n" + + " \"type\": \"public-key\",\n" + + " \"alg\": -37\n" + + " }\n" + + " ],\n" + + " \"authenticatorSelection\": {\n" + + " \"residentKey\": \"required\",\n" + + " \"requireResidentKey\": true\n" + + " }}") + val data = request.data return RequestInfo.newCreateRequestInfo( Binder(), CreateCredentialRequest( @@ -451,6 +471,32 @@ class CredentialManagerRepo( ) } + private fun testCreatePasswordRequestInfo(): RequestInfo { + val data = toBundle("beckett-bakert@gmail.com", "password123") + return RequestInfo.newCreateRequestInfo( + Binder(), + CreateCredentialRequest( + TYPE_PASSWORD_CREDENTIAL, + data + ), + /*isFirstUsage=*/false, + "tribank" + ) + } + + private fun testCreateOtherCredentialRequestInfo(): RequestInfo { + val data = Bundle() + return RequestInfo.newCreateRequestInfo( + Binder(), + CreateCredentialRequest( + "other-sign-ins", + data + ), + /*isFirstUsage=*/false, + "tribank" + ) + } + private fun testGetRequestInfo(): RequestInfo { return RequestInfo.newGetRequestInfo( Binder(), diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index 2eb328498571..b96f686c02fb 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -23,17 +23,23 @@ import android.credentials.ui.Entry import android.credentials.ui.GetCredentialProviderData import android.credentials.ui.CreateCredentialProviderData import android.credentials.ui.DisabledProviderData +import android.credentials.ui.RequestInfo import android.graphics.drawable.Drawable import com.android.credentialmanager.createflow.CreateOptionInfo import com.android.credentialmanager.createflow.RemoteInfo +import com.android.credentialmanager.createflow.RequestDisplayInfo import com.android.credentialmanager.getflow.ActionEntryInfo import com.android.credentialmanager.getflow.AuthenticationEntryInfo import com.android.credentialmanager.getflow.CredentialEntryInfo import com.android.credentialmanager.getflow.ProviderInfo import com.android.credentialmanager.getflow.RemoteEntryInfo +import com.android.credentialmanager.jetpack.developer.CreateCredentialRequest +import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest +import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest import com.android.credentialmanager.jetpack.provider.ActionUi import com.android.credentialmanager.jetpack.provider.CredentialEntryUi import com.android.credentialmanager.jetpack.provider.SaveEntryUi +import org.json.JSONObject /** Utility functions for converting CredentialManager data structures to or from UI formats. */ class GetFlowUtils { @@ -172,6 +178,7 @@ class CreateFlowUtils { fun toEnabledProviderList( providerDataList: List<CreateCredentialProviderData>, + requestDisplayInfo: RequestDisplayInfo, context: Context, ): List<com.android.credentialmanager.createflow.EnabledProviderInfo> { // TODO: get from the actual service info @@ -194,7 +201,7 @@ class CreateFlowUtils { name = it.providerFlattenedComponentName, displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(), createOptions = toCreationOptionInfoList( - it.providerFlattenedComponentName, it.saveEntries, context), + it.providerFlattenedComponentName, it.saveEntries, requestDisplayInfo, context), isDefault = it.isDefaultProvider, remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry), ) @@ -219,9 +226,59 @@ class CreateFlowUtils { } } + fun toRequestDisplayInfo( + requestInfo: RequestInfo, + context: Context, + ): RequestDisplayInfo { + val createCredentialRequest = requestInfo.createCredentialRequest + val createCredentialRequestJetpack = createCredentialRequest?.let { + CreateCredentialRequest.createFrom( + it + ) + } + when (createCredentialRequestJetpack) { + is CreatePasswordRequest -> { + return RequestDisplayInfo( + createCredentialRequestJetpack.id, + createCredentialRequestJetpack.password, + createCredentialRequestJetpack.type, + requestInfo.appPackageName, + context.getDrawable(R.drawable.ic_password)!! + ) + } + is CreatePublicKeyCredentialRequest -> { + val requestJson = createCredentialRequestJetpack.requestJson + val json = JSONObject(requestJson) + var name = "" + var displayName = "" + if (json.has("user")) { + val user: JSONObject = json.getJSONObject("user") + name = user.getString("name") + displayName = user.getString("displayName") + } + return RequestDisplayInfo( + name, + displayName, + createCredentialRequestJetpack.type, + requestInfo.appPackageName, + context.getDrawable(R.drawable.ic_passkey)!!) + } + // TODO: correctly parsing for other sign-ins + else -> { + return RequestDisplayInfo( + "beckett-bakert@gmail.com", + "Elisa Beckett", + "other-sign-ins", + requestInfo.appPackageName, + context.getDrawable(R.drawable.ic_other_sign_in)!!) + } + } + } + private fun toCreationOptionInfoList( providerId: String, creationEntries: List<Entry>, + requestDisplayInfo: RequestDisplayInfo, context: Context, ): List<CreateOptionInfo> { return creationEntries.map { @@ -236,7 +293,7 @@ class CreateFlowUtils { fillInIntent = it.frameworkExtrasIntent, userProviderDisplayName = saveEntryUi.userProviderAccountName as String, profileIcon = saveEntryUi.profileIcon?.loadDrawable(context) - ?: context.getDrawable(R.drawable.ic_profile)!!, + ?: requestDisplayInfo.typeIcon, passwordCount = saveEntryUi.passwordCount ?: 0, passkeyCount = saveEntryUi.passkeyCount ?: 0, totalCredentialCount = saveEntryUi.totalCredentialCount ?: 0, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 9f73aefa52b2..c9cb3ce49db5 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -191,13 +191,12 @@ fun ProviderSelectionCard( ) { Card() { Column() { - // TODO: Change the icon for create passwords and sign-ins Icon( - painter = painterResource(R.drawable.ic_passkey), + bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap(), contentDescription = null, tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, modifier = Modifier.align(alignment = Alignment.CenterHorizontally) - .padding(top = 24.dp, bottom = 16.dp) + .padding(top = 24.dp, bottom = 16.dp).size(32.dp) ) Text( text = stringResource( @@ -567,35 +566,52 @@ fun PrimaryCreateOptionRow( Entry( onClick = {onOptionSelected(createOptionInfo)}, icon = { - // TODO: Upload the other two types icons and change it according to request types Icon( - painter = painterResource(R.drawable.ic_passkey), + bitmap = createOptionInfo.profileIcon.toBitmap().asImageBitmap(), contentDescription = null, tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant, - modifier = Modifier.padding(start = 18.dp) + modifier = Modifier.padding(start = 18.dp).size(32.dp) ) }, label = { Column() { // TODO: Add the function to hide/view password when the type is create password - if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL || - requestDisplayInfo.type == TYPE_PASSWORD_CREDENTIAL) { - Text( - text = requestDisplayInfo.title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp) - ) - Text( - text = requestDisplayInfo.subtitle, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(bottom = 16.dp) - ) - } else { - Text( - text = requestDisplayInfo.title, - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(top = 16.dp, bottom = 16.dp) - ) + when (requestDisplayInfo.type) { + TYPE_PUBLIC_KEY_CREDENTIAL -> { + Text( + text = requestDisplayInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp) + ) + Text( + text = if (requestDisplayInfo.subtitle != null) { + stringResource( + R.string.passkey_before_subtitle) + " - " + requestDisplayInfo.subtitle + } else {stringResource(R.string.passkey_before_subtitle)}, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + TYPE_PASSWORD_CREDENTIAL -> { + Text( + text = requestDisplayInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp) + ) + Text( + // This subtitle would never be null for create password + text = requestDisplayInfo.subtitle ?: "", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + else -> { + Text( + text = requestDisplayInfo.title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp, bottom = 16.dp) + ) + } } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index 6dd6afb81dc4..31d0365a821f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -73,9 +73,10 @@ class RemoteInfo( data class RequestDisplayInfo( val title: String, - val subtitle: String, + val subtitle: String?, val type: String, val appDomainName: String, + val typeIcon: Drawable, ) /** diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt index 26d61f9eb7a9..37a4f7633988 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreatePublicKeyCredentialBaseRequest.kt @@ -47,7 +47,7 @@ abstract class CreatePublicKeyCredentialBaseRequest constructor( return when (data.getString(BUNDLE_KEY_SUBTYPE)) { CreatePublicKeyCredentialRequest .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST -> - CreatePublicKeyCredentialRequestPrivileged.createFrom(data) + CreatePublicKeyCredentialRequest.createFrom(data) CreatePublicKeyCredentialRequestPrivileged .BUNDLE_VALUE_SUBTYPE_CREATE_PUBLIC_KEY_CREDENTIAL_REQUEST_PRIVILEGED -> CreatePublicKeyCredentialRequestPrivileged.createFrom(data) diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java index e4bdab89532b..88c10365219b 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; -import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; @@ -104,10 +103,9 @@ public class InstallStart extends Activity { Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (installerPackageNameFromIntent != null) { final String callingPkgName = getLaunchedFromPackage(); - if (installerPackageNameFromIntent.length() >= SessionParams.MAX_PACKAGE_NAME_LENGTH - || (!TextUtils.equals(installerPackageNameFromIntent, callingPkgName) + if (!TextUtils.equals(installerPackageNameFromIntent, callingPkgName) && mPackageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES, - callingPkgName) != PackageManager.PERMISSION_GRANTED)) { + callingPkgName) != PackageManager.PERMISSION_GRANTED) { Log.e(LOG_TAG, "The given installer package name " + installerPackageNameFromIntent + " is invalid. Remove it."); EventLog.writeEvent(0x534e4554, "236687884", getLaunchedFromUid(), diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt index 96e2498e39bf..decc2923cd26 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt @@ -46,7 +46,6 @@ object ActionButtonPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt index a57df0f46f6a..063b61cc5e09 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt @@ -50,7 +50,6 @@ object AlterDialogPageProvider : SettingsPageProvider { ) fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt index 7958d11ad513..42ac1acc09eb 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt @@ -55,7 +55,6 @@ object ArgumentPageProvider : SettingsPageProvider { entryList.add( createEntry(owner, EntryEnum.STRING_PARAM) // Set attributes - .setIsAllowSearch(true) .setIsSearchDataDynamic(true) .setSearchDataFn { ArgumentPageModel.genStringParamSearchData() } .setUiLayoutFn { @@ -67,7 +66,6 @@ object ArgumentPageProvider : SettingsPageProvider { entryList.add( createEntry(owner, EntryEnum.INT_PARAM) // Set attributes - .setIsAllowSearch(true) .setIsSearchDataDynamic(true) .setSearchDataFn { ArgumentPageModel.genIntParamSearchData() } .setUiLayoutFn { @@ -90,8 +88,6 @@ object ArgumentPageProvider : SettingsPageProvider { owner = createSettingsPage(arguments), displayName = "${name}_$stringParam", ) - // Set attributes - .setIsAllowSearch(false) .setSearchDataFn { ArgumentPageModel.genInjectSearchData() } .setUiLayoutFn { // Set ui rendering diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt index 160e77b061eb..7f21a4dd4e7c 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ChartPage.kt @@ -134,7 +134,6 @@ object ChartPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt index c903cfd96ce3..9f24ea9a2b6d 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt @@ -20,6 +20,7 @@ import android.os.Bundle import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.common.EntrySearchData import com.android.settingslib.spa.framework.common.SettingsEntry import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPage @@ -42,7 +43,7 @@ object FooterPageProvider : SettingsPageProvider { val entryList = mutableListOf<SettingsEntry>() entryList.add( SettingsEntryBuilder.create( "Some Preference", owner) - .setIsAllowSearch(true) + .setSearchDataFn { EntrySearchData(title = "Some Preference") } .setUiLayoutFn { Preference(remember { object : PreferenceModel { @@ -58,7 +59,6 @@ object FooterPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt index e10cf3aa0702..ddf66aae1c22 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt @@ -72,7 +72,6 @@ object IllustrationPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/LoadingBarPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/LoadingBarPage.kt index c354930dc4a8..4332a81e088c 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/LoadingBarPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/LoadingBarPage.kt @@ -46,7 +46,6 @@ object LoadingBarPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt index 1f76557764a7..20d90dd0ee7a 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ProgressBarPage.kt @@ -48,7 +48,6 @@ object ProgressBarPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt index cb58a95e01a6..c0d0abc5786e 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt @@ -40,7 +40,6 @@ object SettingsPagerPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt index 73b34a50a520..a62ec7b0925c 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt @@ -48,7 +48,6 @@ object SliderPageProvider : SettingsPageProvider { val entryList = mutableListOf<SettingsEntry>() entryList.add( SettingsEntryBuilder.create("Simple Slider", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SliderPreference(object : SliderPreferenceModel { override val title = "Simple Slider" @@ -58,7 +57,6 @@ object SliderPageProvider : SettingsPageProvider { ) entryList.add( SettingsEntryBuilder.create("Slider with icon", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SliderPreference(object : SliderPreferenceModel { override val title = "Slider with icon" @@ -72,7 +70,6 @@ object SliderPageProvider : SettingsPageProvider { ) entryList.add( SettingsEntryBuilder.create("Slider with changeable icon", owner) - .setIsAllowSearch(true) .setUiLayoutFn { val initValue = 0 var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) } @@ -93,7 +90,6 @@ object SliderPageProvider : SettingsPageProvider { ) entryList.add( SettingsEntryBuilder.create("Slider with steps", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SliderPreference(object : SliderPreferenceModel { override val title = "Slider with steps" @@ -109,7 +105,6 @@ object SliderPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt index f38a8d4193b3..67e35dc37b50 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt @@ -44,14 +44,12 @@ object MainSwitchPreferencePageProvider : SettingsPageProvider { val entryList = mutableListOf<SettingsEntry>() entryList.add( SettingsEntryBuilder.create( "MainSwitchPreference", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleMainSwitchPreference() }.build() ) entryList.add( SettingsEntryBuilder.create( "MainSwitchPreference not changeable", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleNotChangeableMainSwitchPreference() }.build() @@ -62,7 +60,6 @@ object MainSwitchPreferencePageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt index 61925a7b7164..eddede752d06 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt @@ -43,7 +43,6 @@ object PreferenceMainPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = owner) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt index ff89f2b25e7d..0c9a0430bc1e 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt @@ -87,7 +87,6 @@ object PreferencePageProvider : SettingsPageProvider { val entryList = mutableListOf<SettingsEntry>() entryList.add( createEntry(EntryEnum.SIMPLE_PREFERENCE) - .setIsAllowSearch(true) .setMacro { spaLogger.message(TAG, "create macro for ${EntryEnum.SIMPLE_PREFERENCE}") SimplePreferenceMacro(title = SIMPLE_PREFERENCE_TITLE) @@ -96,7 +95,6 @@ object PreferencePageProvider : SettingsPageProvider { ) entryList.add( createEntry(EntryEnum.SUMMARY_PREFERENCE) - .setIsAllowSearch(true) .setMacro { spaLogger.message(TAG, "create macro for ${EntryEnum.SUMMARY_PREFERENCE}") SimplePreferenceMacro( @@ -110,7 +108,6 @@ object PreferencePageProvider : SettingsPageProvider { entryList.add(singleLineSummaryEntry()) entryList.add( createEntry(EntryEnum.DISABLED_PREFERENCE) - .setIsAllowSearch(true) .setHasMutableStatus(true) .setMacro { spaLogger.message(TAG, "create macro for ${EntryEnum.DISABLED_PREFERENCE}") @@ -126,7 +123,6 @@ object PreferencePageProvider : SettingsPageProvider { ) entryList.add( createEntry(EntryEnum.ASYNC_SUMMARY_PREFERENCE) - .setIsAllowSearch(true) .setHasMutableStatus(true) .setSearchDataFn { EntrySearchData(title = ASYNC_PREFERENCE_TITLE) @@ -165,7 +161,6 @@ object PreferencePageProvider : SettingsPageProvider { ) entryList.add( createEntry(EntryEnum.MANUAL_UPDATE_PREFERENCE) - .setIsAllowSearch(true) .setUiLayoutFn { val model = PreferencePageModel.create() val manualUpdaterSummary = remember { model.getManualUpdaterSummary() } @@ -179,7 +174,8 @@ object PreferencePageProvider : SettingsPageProvider { } } ) - }.setSliceDataFn { sliceUri, args -> + } + .setSliceDataFn { sliceUri, args -> val createSliceImpl = { v: Int -> createDemoActionSlice( sliceUri = sliceUri, @@ -204,7 +200,6 @@ object PreferencePageProvider : SettingsPageProvider { ) entryList.add( createEntry(EntryEnum.AUTO_UPDATE_PREFERENCE) - .setIsAllowSearch(true) .setUiLayoutFn { val model = PreferencePageModel.create() val autoUpdaterSummary = remember { model.getAutoUpdaterSummary() } @@ -251,7 +246,6 @@ object PreferencePageProvider : SettingsPageProvider { } private fun singleLineSummaryEntry() = createEntry(EntryEnum.SINGLE_LINE_SUMMARY_PREFERENCE) - .setIsAllowSearch(true) .setUiLayoutFn { Preference( model = object : PreferenceModel { @@ -267,7 +261,6 @@ object PreferencePageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = owner) - .setIsAllowSearch(true) .setMacro { spaLogger.message(TAG, "create macro for INJECT entry") SimplePreferenceMacro( diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt index dab04fdf740a..067911cf4b9d 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt @@ -49,35 +49,30 @@ object SwitchPreferencePageProvider : SettingsPageProvider { val entryList = mutableListOf<SettingsEntry>() entryList.add( SettingsEntryBuilder.create( "SwitchPreference", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleSwitchPreference() }.build() ) entryList.add( SettingsEntryBuilder.create( "SwitchPreference with summary", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleSwitchPreferenceWithSummary() }.build() ) entryList.add( SettingsEntryBuilder.create( "SwitchPreference with async summary", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleSwitchPreferenceWithAsyncSummary() }.build() ) entryList.add( SettingsEntryBuilder.create( "SwitchPreference not changeable", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleNotChangeableSwitchPreference() }.build() ) entryList.add( SettingsEntryBuilder.create( "SwitchPreference with icon", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleSwitchPreferenceWithIcon() }.build() @@ -88,7 +83,6 @@ object SwitchPreferencePageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt index 22da99c23fc8..33e5e8d6d31a 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt @@ -46,28 +46,24 @@ object TwoTargetSwitchPreferencePageProvider : SettingsPageProvider { val entryList = mutableListOf<SettingsEntry>() entryList.add( SettingsEntryBuilder.create( "TwoTargetSwitchPreference", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleTwoTargetSwitchPreference() }.build() ) entryList.add( SettingsEntryBuilder.create( "TwoTargetSwitchPreference with summary", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleTwoTargetSwitchPreferenceWithSummary() }.build() ) entryList.add( SettingsEntryBuilder.create( "TwoTargetSwitchPreference with async summary", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleTwoTargetSwitchPreferenceWithAsyncSummary() }.build() ) entryList.add( SettingsEntryBuilder.create( "TwoTargetSwitchPreference not changeable", owner) - .setIsAllowSearch(true) .setUiLayoutFn { SampleNotChangeableTwoTargetSwitchPreference() }.build() @@ -78,7 +74,6 @@ object TwoTargetSwitchPreferencePageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt index d87cbe82d9d8..cb58abf60180 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt @@ -39,7 +39,6 @@ object CategoryPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt index ec2f436c9e98..ba769d206fd5 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt @@ -40,7 +40,6 @@ object SpinnerPageProvider : SettingsPageProvider { fun buildInjectEntry(): SettingsEntryBuilder { return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name)) - .setIsAllowSearch(true) .setUiLayoutFn { Preference(object : PreferenceModel { override val title = TITLE diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt index bb1cd6e44867..76f6611a6690 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/Contexts.kt @@ -16,6 +16,7 @@ package com.android.settingslib.spaprivileged.framework.common +import android.app.ActivityManager import android.app.AlarmManager import android.app.AppOpsManager import android.app.admin.DevicePolicyManager @@ -28,6 +29,9 @@ import android.os.UserHandle import android.os.UserManager import android.permission.PermissionControllerManager +/** The [ActivityManager] instance. */ +val Context.activityManager get() = getSystemService(ActivityManager::class.java)!! + /** The [AlarmManager] instance. */ val Context.alarmManager get() = getSystemService(AlarmManager::class.java)!! diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 2e0155b86e7f..caaa88d6aea8 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1105,6 +1105,8 @@ <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string> <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited --> <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging is paused</string> + <!-- [CHAR_LIMIT=80] Label for battery charging future pause --> + <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging to <xliff:g id="dock_defender_threshold">%2$s</xliff:g></string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index 132a631e25cc..8b68a09bbf65 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -66,7 +66,7 @@ public class BatteryStatus { public BatteryStatus(Intent batteryChangedIntent) { status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); - level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); + level = getBatteryLevel(batteryChangedIntent); health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); @@ -188,7 +188,7 @@ public class BatteryStatus { */ public static boolean isCharged(Intent batteryChangedIntent) { int status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); - int level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); + int level = getBatteryLevel(batteryChangedIntent); return isCharged(status, level); } @@ -204,4 +204,13 @@ public class BatteryStatus { public static boolean isCharged(int status, int level) { return status == BATTERY_STATUS_FULL || level >= 100; } + + /** Gets the battery level from the intent. */ + public static int getBatteryLevel(Intent batteryChangedIntent) { + final int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); + return scale == 0 + ? -1 /*invalid battery level*/ + : Math.round((level / (float) scale) * 100f); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 336cdd3f259f..291f6a39105b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -317,7 +317,9 @@ public class UtilsTest { @Test public void getBatteryStatus_statusIsFull_returnFullString() { - final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100); + final Intent intent = new Intent() + .putExtra(BatteryManager.EXTRA_LEVEL, 100) + .putExtra(BatteryManager.EXTRA_SCALE, 100); final Resources resources = mContext.getResources(); assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo( @@ -326,7 +328,9 @@ public class UtilsTest { @Test public void getBatteryStatus_statusIsFullAndUseCompactStatus_returnFullyChargedString() { - final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100); + final Intent intent = new Intent() + .putExtra(BatteryManager.EXTRA_LEVEL, 100) + .putExtra(BatteryManager.EXTRA_SCALE, 100); final Resources resources = mContext.getResources(); assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo( diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 01c080990cfd..680a0a17dca8 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -771,6 +771,80 @@ <!-- Permissions required for CTS test - CtsAppFgsTestCases --> <uses-permission android:name="android.permission.USE_EXACT_ALARM" /> + <!-- Permissions required for CTS test - CtsAppFgsTestCases --> + <uses-permission android:name="android.permission.health.READ_ACTIVE_CALORIES_BURNED" /> + <uses-permission android:name="android.permission.health.READ_BASAL_BODY_TEMPERATURE" /> + <uses-permission android:name="android.permission.health.READ_BASAL_METABOLIC_RATE" /> + <uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE" /> + <uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE" /> + <uses-permission android:name="android.permission.health.READ_BODY_FAT" /> + <uses-permission android:name="android.permission.health.READ_BODY_TEMPERATURE" /> + <uses-permission android:name="android.permission.health.READ_BODY_WATER_MASS" /> + <uses-permission android:name="android.permission.health.READ_BONE_MASS" /> + <uses-permission android:name="android.permission.health.READ_CERVICAL_MUCUS" /> + <uses-permission android:name="android.permission.health.READ_DISTANCE" /> + <uses-permission android:name="android.permission.health.READ_ELEVATION_GAINED" /> + <uses-permission android:name="android.permission.health.READ_EXERCISE" /> + <uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED" /> + <uses-permission android:name="android.permission.health.READ_HEART_RATE" /> + <uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY" /> + <uses-permission android:name="android.permission.health.READ_HEIGHT" /> + <uses-permission android:name="android.permission.health.READ_HIP_CIRCUMFERENCE" /> + <uses-permission android:name="android.permission.health.READ_HYDRATION" /> + <uses-permission android:name="android.permission.health.READ_LEAN_BODY_MASS" /> + <uses-permission android:name="android.permission.health.READ_MENSTRUATION" /> + <uses-permission android:name="android.permission.health.READ_NUTRITION" /> + <uses-permission android:name="android.permission.health.READ_OVULATION_TEST" /> + <uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION" /> + <uses-permission android:name="android.permission.health.READ_POWER" /> + <uses-permission android:name="android.permission.health.READ_RESPIRATORY_RATE" /> + <uses-permission android:name="android.permission.health.READ_RESTING_HEART_RATE" /> + <uses-permission android:name="android.permission.health.READ_SEXUAL_ACTIVITY" /> + <uses-permission android:name="android.permission.health.READ_SLEEP" /> + <uses-permission android:name="android.permission.health.READ_SPEED" /> + <uses-permission android:name="android.permission.health.READ_STEPS" /> + <uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" /> + <uses-permission android:name="android.permission.health.READ_VO2_MAX" /> + <uses-permission android:name="android.permission.health.READ_WAIST_CIRCUMFERENCE" /> + <uses-permission android:name="android.permission.health.READ_WEIGHT" /> + <uses-permission android:name="android.permission.health.READ_WHEELCHAIR_PUSHES" /> + <uses-permission android:name="android.permission.health.WRITE_ACTIVE_CALORIES_BURNED" /> + <uses-permission android:name="android.permission.health.WRITE_BASAL_BODY_TEMPERATURE" /> + <uses-permission android:name="android.permission.health.WRITE_BASAL_METABOLIC_RATE" /> + <uses-permission android:name="android.permission.health.WRITE_BLOOD_GLUCOSE" /> + <uses-permission android:name="android.permission.health.WRITE_BLOOD_PRESSURE" /> + <uses-permission android:name="android.permission.health.WRITE_BODY_FAT" /> + <uses-permission android:name="android.permission.health.WRITE_BODY_TEMPERATURE" /> + <uses-permission android:name="android.permission.health.WRITE_BODY_WATER_MASS" /> + <uses-permission android:name="android.permission.health.WRITE_BONE_MASS" /> + <uses-permission android:name="android.permission.health.WRITE_CERVICAL_MUCUS" /> + <uses-permission android:name="android.permission.health.WRITE_DISTANCE" /> + <uses-permission android:name="android.permission.health.WRITE_ELEVATION_GAINED" /> + <uses-permission android:name="android.permission.health.WRITE_EXERCISE" /> + <uses-permission android:name="android.permission.health.WRITE_FLOORS_CLIMBED" /> + <uses-permission android:name="android.permission.health.WRITE_HEART_RATE" /> + <uses-permission android:name="android.permission.health.WRITE_HEART_RATE_VARIABILITY" /> + <uses-permission android:name="android.permission.health.WRITE_HEIGHT" /> + <uses-permission android:name="android.permission.health.WRITE_HIP_CIRCUMFERENCE" /> + <uses-permission android:name="android.permission.health.WRITE_HYDRATION" /> + <uses-permission android:name="android.permission.health.WRITE_LEAN_BODY_MASS" /> + <uses-permission android:name="android.permission.health.WRITE_MENSTRUATION" /> + <uses-permission android:name="android.permission.health.WRITE_NUTRITION" /> + <uses-permission android:name="android.permission.health.WRITE_OVULATION_TEST" /> + <uses-permission android:name="android.permission.health.WRITE_OXYGEN_SATURATION" /> + <uses-permission android:name="android.permission.health.WRITE_POWER" /> + <uses-permission android:name="android.permission.health.WRITE_RESPIRATORY_RATE" /> + <uses-permission android:name="android.permission.health.WRITE_RESTING_HEART_RATE" /> + <uses-permission android:name="android.permission.health.WRITE_SEXUAL_ACTIVITY" /> + <uses-permission android:name="android.permission.health.WRITE_SLEEP" /> + <uses-permission android:name="android.permission.health.WRITE_SPEED" /> + <uses-permission android:name="android.permission.health.WRITE_STEPS" /> + <uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED" /> + <uses-permission android:name="android.permission.health.WRITE_VO2_MAX" /> + <uses-permission android:name="android.permission.health.WRITE_WAIST_CIRCUMFERENCE" /> + <uses-permission android:name="android.permission.health.WRITE_WEIGHT" /> + <uses-permission android:name="android.permission.health.WRITE_WHEELCHAIR_PUSHES" /> + <!-- Permission required for CTS test - ApplicationExemptionsTests --> <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS" /> diff --git a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt index e611e8bf0068..979e1a08b7d4 100644 --- a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt +++ b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt @@ -38,12 +38,18 @@ import platform.test.screenshot.ScreenshotTestRule import platform.test.screenshot.getEmulatedDevicePathConfig /** A rule for Compose screenshot diff tests. */ -class ComposeScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule { +class ComposeScreenshotTestRule( + emulationSpec: DeviceEmulationSpec, + assetPathRelativeToBuildRoot: String +) : TestRule { private val colorsRule = MaterialYouColorsRule() private val deviceEmulationRule = DeviceEmulationRule(emulationSpec) private val screenshotRule = ScreenshotTestRule( - SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec)) + SystemUIGoldenImagePathManager( + getEmulatedDevicePathConfig(emulationSpec), + assetPathRelativeToBuildRoot + ) ) private val composeRule = createAndroidComposeRule<ScreenshotActivity>() private val delegateRule = diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml index 2b7bdc2dc4cb..c772c9649cc7 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml @@ -27,7 +27,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - androidprv:layout_maxWidth="@dimen/keyguard_security_width" + androidprv:layout_maxWidth="@dimen/biometric_auth_pattern_view_max_size" android:layout_gravity="center_horizontal|bottom" android:clipChildren="false" android:clipToPadding="false"> diff --git a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml b/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml deleted file mode 100644 index a3c37e420f29..000000000000 --- a/packages/SystemUI/res-keyguard/values-sw540dp-port/dimens.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/assets/res/any/dimens.xml -** -** Copyright 2013, 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. -*/ ---> -<resources> - <!-- Height of the sliding KeyguardSecurityContainer - (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">550dp</dimen> -</resources> diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml index 1dc61c501beb..b7a1bb46c317 100644 --- a/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-sw720dp/dimens.xml @@ -17,10 +17,5 @@ */ --> <resources> - - <!-- Height of the sliding KeyguardSecurityContainer - (includes 2x keyguard_security_view_top_margin) --> - <dimen name="keyguard_security_height">470dp</dimen> - <dimen name="widget_big_font_size">100dp</dimen> </resources> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 3861d983b309..e6593b16b731 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -29,9 +29,6 @@ (includes 2x keyguard_security_view_top_margin) --> <dimen name="keyguard_security_height">420dp</dimen> - <!-- Max Height of the sliding KeyguardSecurityContainer - (includes 2x keyguard_security_view_top_margin) --> - <!-- pin/password field max height --> <dimen name="keyguard_password_height">80dp</dimen> diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml index 6e0e38b95ee5..88f138f9b093 100644 --- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml +++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml @@ -71,8 +71,8 @@ <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" android:layout_gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_width="@dimen/biometric_auth_pattern_view_size" + android:layout_height="@dimen/biometric_auth_pattern_view_size"/> <TextView android:id="@+id/error" diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml index 891c6af4b667..81ca37189ac4 100644 --- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml @@ -67,8 +67,8 @@ <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" android:layout_gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_width="@dimen/biometric_auth_pattern_view_size" + android:layout_height="@dimen/biometric_auth_pattern_view_size"/> <TextView android:id="@+id/error" diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 49ef330dcc52..fff25448b2e4 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -40,6 +40,10 @@ <dimen name="biometric_dialog_button_negative_max_width">140dp</dimen> <dimen name="biometric_dialog_button_positive_max_width">116dp</dimen> + <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size --> + <dimen name="biometric_auth_pattern_view_size">248dp</dimen> + <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen> + <dimen name="global_actions_power_dialog_item_height">130dp</dimen> <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen> diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml index aefd9981d02e..a0e721e571d8 100644 --- a/packages/SystemUI/res/values-land/styles.xml +++ b/packages/SystemUI/res/values-land/styles.xml @@ -29,11 +29,11 @@ <style name="AuthCredentialPatternContainerStyle"> <item name="android:gravity">center</item> - <item name="android:maxHeight">320dp</item> - <item name="android:maxWidth">320dp</item> - <item name="android:minHeight">200dp</item> - <item name="android:minWidth">200dp</item> - <item name="android:paddingHorizontal">60dp</item> + <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item> + <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item> + <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item> + <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item> + <item name="android:paddingHorizontal">32dp</item> <item name="android:paddingVertical">20dp</item> </style> diff --git a/packages/SystemUI/res/values-sw360dp/dimens.xml b/packages/SystemUI/res/values-sw360dp/dimens.xml index 65ca70bac0dc..03365b3d8c4f 100644 --- a/packages/SystemUI/res/values-sw360dp/dimens.xml +++ b/packages/SystemUI/res/values-sw360dp/dimens.xml @@ -25,5 +25,8 @@ <!-- Home Controls --> <dimen name="global_actions_side_margin">12dp</dimen> + + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">298dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw392dp-land/dimens.xml b/packages/SystemUI/res/values-sw392dp-land/dimens.xml new file mode 100644 index 000000000000..1e26a699f85a --- /dev/null +++ b/packages/SystemUI/res/values-sw392dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size --> + <dimen name="biometric_auth_pattern_view_size">248dp</dimen> + <dimen name="biometric_auth_pattern_view_max_size">248dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml index 78279ca4f520..96af3c13f32e 100644 --- a/packages/SystemUI/res/values-sw392dp/dimens.xml +++ b/packages/SystemUI/res/values-sw392dp/dimens.xml @@ -24,5 +24,8 @@ <!-- Home Controls --> <dimen name="global_actions_side_margin">16dp</dimen> + + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">298dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw410dp-land/dimens.xml b/packages/SystemUI/res/values-sw410dp-land/dimens.xml new file mode 100644 index 000000000000..c4d9b9b92f57 --- /dev/null +++ b/packages/SystemUI/res/values-sw410dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size --> + <dimen name="biometric_auth_pattern_view_size">248dp</dimen> + <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml index 7da47e5089be..ff6e005a94c7 100644 --- a/packages/SystemUI/res/values-sw410dp/dimens.xml +++ b/packages/SystemUI/res/values-sw410dp/dimens.xml @@ -27,4 +27,6 @@ <dimen name="global_actions_grid_item_side_margin">12dp</dimen> <dimen name="global_actions_grid_item_height">72dp</dimen> + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">348dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw600dp-land/styles.xml b/packages/SystemUI/res/values-sw600dp-land/styles.xml index 8148d3dfaf7d..5ca2b43e250a 100644 --- a/packages/SystemUI/res/values-sw600dp-land/styles.xml +++ b/packages/SystemUI/res/values-sw600dp-land/styles.xml @@ -16,16 +16,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="AuthCredentialPatternContainerStyle"> - <item name="android:gravity">center</item> - <item name="android:maxHeight">420dp</item> - <item name="android:maxWidth">420dp</item> - <item name="android:minHeight">200dp</item> - <item name="android:minWidth">200dp</item> - <item name="android:paddingHorizontal">120dp</item> - <item name="android:paddingVertical">40dp</item> - </style> - <style name="TextAppearance.AuthNonBioCredential.Title"> <item name="android:fontFamily">google-sans</item> <item name="android:layout_marginTop">16dp</item> diff --git a/packages/SystemUI/res/values-sw600dp-port/styles.xml b/packages/SystemUI/res/values-sw600dp-port/styles.xml index 771de08cb360..41d931d8f119 100644 --- a/packages/SystemUI/res/values-sw600dp-port/styles.xml +++ b/packages/SystemUI/res/values-sw600dp-port/styles.xml @@ -24,16 +24,6 @@ <item name="android:layout_gravity">top</item> </style> - <style name="AuthCredentialPatternContainerStyle"> - <item name="android:gravity">center</item> - <item name="android:maxHeight">420dp</item> - <item name="android:maxWidth">420dp</item> - <item name="android:minHeight">200dp</item> - <item name="android:minWidth">200dp</item> - <item name="android:paddingHorizontal">180dp</item> - <item name="android:paddingVertical">80dp</item> - </style> - <style name="TextAppearance.AuthNonBioCredential.Title"> <item name="android:fontFamily">google-sans</item> <item name="android:layout_marginTop">24dp</item> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 599bf30a5135..9bc0dde49b4c 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -92,4 +92,6 @@ <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> <dimen name="lockscreen_shade_keyguard_transition_distance">@dimen/lockscreen_shade_media_transition_distance</dimen> + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">348dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp-land/styles.xml b/packages/SystemUI/res/values-sw720dp-land/styles.xml index f9ed67d89de7..d9406d3aac56 100644 --- a/packages/SystemUI/res/values-sw720dp-land/styles.xml +++ b/packages/SystemUI/res/values-sw720dp-land/styles.xml @@ -16,16 +16,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="AuthCredentialPatternContainerStyle"> - <item name="android:gravity">center</item> - <item name="android:maxHeight">420dp</item> - <item name="android:maxWidth">420dp</item> - <item name="android:minHeight">200dp</item> - <item name="android:minWidth">200dp</item> - <item name="android:paddingHorizontal">120dp</item> - <item name="android:paddingVertical">40dp</item> - </style> - <style name="TextAppearance.AuthNonBioCredential.Title"> <item name="android:fontFamily">google-sans</item> <item name="android:layout_marginTop">16dp</item> diff --git a/packages/SystemUI/res/values-sw720dp-port/styles.xml b/packages/SystemUI/res/values-sw720dp-port/styles.xml index 78d299c483e6..41d931d8f119 100644 --- a/packages/SystemUI/res/values-sw720dp-port/styles.xml +++ b/packages/SystemUI/res/values-sw720dp-port/styles.xml @@ -24,16 +24,6 @@ <item name="android:layout_gravity">top</item> </style> - <style name="AuthCredentialPatternContainerStyle"> - <item name="android:gravity">center</item> - <item name="android:maxHeight">420dp</item> - <item name="android:maxWidth">420dp</item> - <item name="android:minHeight">200dp</item> - <item name="android:minWidth">200dp</item> - <item name="android:paddingHorizontal">240dp</item> - <item name="android:paddingVertical">120dp</item> - </style> - <style name="TextAppearance.AuthNonBioCredential.Title"> <item name="android:fontFamily">google-sans</item> <item name="android:layout_marginTop">24dp</item> diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index 07050171470a..927059aa7e40 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -22,5 +22,8 @@ <dimen name="controls_padding_horizontal">75dp</dimen> <dimen name="large_screen_shade_header_height">56dp</dimen> + + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">348dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw800dp/dimens.xml b/packages/SystemUI/res/values-sw800dp/dimens.xml new file mode 100644 index 000000000000..0d82217456e4 --- /dev/null +++ b/packages/SystemUI/res/values-sw800dp/dimens.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">348dp</dimen> +</resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9f29c5bd0bc2..569b661cc522 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -959,6 +959,10 @@ <!-- Biometric Auth Credential values --> <dimen name="biometric_auth_icon_size">48dp</dimen> + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> + <dimen name="biometric_auth_pattern_view_size">348dp</dimen> + <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen> + <!-- Starting text size in sp of batteryLevel for wireless charging animation --> <item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen"> 0 diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 0c91ced7ab4d..aafa47fb08fb 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -251,11 +251,12 @@ <style name="AuthCredentialPatternContainerStyle"> <item name="android:gravity">center</item> - <item name="android:maxHeight">420dp</item> - <item name="android:maxWidth">420dp</item> - <item name="android:minHeight">200dp</item> - <item name="android:minWidth">200dp</item> - <item name="android:padding">20dp</item> + <item name="android:maxHeight">@dimen/biometric_auth_pattern_view_max_size</item> + <item name="android:maxWidth">@dimen/biometric_auth_pattern_view_max_size</item> + <item name="android:minHeight">@dimen/biometric_auth_pattern_view_size</item> + <item name="android:minWidth">@dimen/biometric_auth_pattern_view_size</item> + <item name="android:paddingHorizontal">32dp</item> + <item name="android:paddingVertical">20dp</item> </style> <style name="AuthCredentialPinPasswordContainerStyle"> diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt index 49cc48321d77..e032bb9b7e30 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt @@ -34,13 +34,19 @@ import platform.test.screenshot.* /** * A rule that allows to run a screenshot diff test on a view that is hosted in another activity. */ -class ExternalViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule { +class ExternalViewScreenshotTestRule( + emulationSpec: DeviceEmulationSpec, + assetPathRelativeToBuildRoot: String +) : TestRule { private val colorsRule = MaterialYouColorsRule() private val deviceEmulationRule = DeviceEmulationRule(emulationSpec) private val screenshotRule = ScreenshotTestRule( - SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec)) + SystemUIGoldenImagePathManager( + getEmulatedDevicePathConfig(emulationSpec), + assetPathRelativeToBuildRoot + ) ) private val delegateRule = RuleChain.outerRule(colorsRule).around(deviceEmulationRule).around(screenshotRule) diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt index fafc7744f439..72d8c5a09852 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt @@ -23,11 +23,11 @@ import platform.test.screenshot.PathConfig /** A [GoldenImagePathManager] that should be used for all SystemUI screenshot tests. */ class SystemUIGoldenImagePathManager( pathConfig: PathConfig, - override val assetsPathRelativeToRepo: String = "tests/screenshot/assets" + assetsPathRelativeToBuildRoot: String ) : GoldenImagePathManager( appContext = InstrumentationRegistry.getInstrumentation().context, - assetsPathRelativeToRepo = assetsPathRelativeToRepo, + assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot, deviceLocalPath = InstrumentationRegistry.getInstrumentation() .targetContext diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt index 36ac1ff9ad30..6d0cc5ee4feb 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt @@ -44,17 +44,16 @@ class ViewScreenshotTestRule( emulationSpec: DeviceEmulationSpec, private val matcher: BitmapMatcher = UnitTestBitmapMatcher, pathConfig: PathConfig = getEmulatedDevicePathConfig(emulationSpec), - assetsPathRelativeToRepo: String = "" + assetsPathRelativeToBuildRoot: String ) : TestRule { private val colorsRule = MaterialYouColorsRule() private val deviceEmulationRule = DeviceEmulationRule(emulationSpec) private val screenshotRule = ScreenshotTestRule( - if (assetsPathRelativeToRepo.isBlank()) { - SystemUIGoldenImagePathManager(pathConfig) - } else { - SystemUIGoldenImagePathManager(pathConfig, assetsPathRelativeToRepo) - } + SystemUIGoldenImagePathManager( + getEmulatedDevicePathConfig(emulationSpec), + assetsPathRelativeToBuildRoot + ) ) private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java) private val delegateRule = diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt index 819768544b0c..e6283b86283b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt @@ -26,7 +26,6 @@ data class KeyguardFingerprintListenModel( val credentialAttempted: Boolean, val deviceInteractive: Boolean, val dreaming: Boolean, - val encryptedOrLockdown: Boolean, val fingerprintDisabled: Boolean, val fingerprintLockedOut: Boolean, val goingToSleep: Boolean, @@ -37,6 +36,7 @@ data class KeyguardFingerprintListenModel( val primaryUser: Boolean, val shouldListenSfpsState: Boolean, val shouldListenForFingerprintAssistant: Boolean, + val strongerAuthRequired: Boolean, val switchingUser: Boolean, val udfps: Boolean, val userDoesNotHaveTrust: Boolean diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 01be33e1e156..4d0a273a2189 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -363,16 +363,18 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard final boolean sfpsEnabled = getResources().getBoolean( R.bool.config_show_sidefps_hint_on_bouncer); final boolean fpsDetectionRunning = mUpdateMonitor.isFingerprintDetectionRunning(); - final boolean needsStrongAuth = mUpdateMonitor.userNeedsStrongAuth(); + final boolean isUnlockingWithFpAllowed = + mUpdateMonitor.isUnlockingWithFingerprintAllowed(); - boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning && !needsStrongAuth; + boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning + && isUnlockingWithFpAllowed; if (DEBUG) { Log.d(TAG, "sideFpsToShow=" + toShow + ", " + "mBouncerVisible=" + mBouncerVisible + ", " + "configEnabled=" + sfpsEnabled + ", " + "fpsDetectionRunning=" + fpsDetectionRunning + ", " - + "needsStrongAuth=" + needsStrongAuth); + + "isUnlockingWithFpAllowed=" + isUnlockingWithFpAllowed); } if (toShow) { mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 39ade347dd9b..993d80f49182 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -27,6 +27,8 @@ import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_P import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED; import static android.hardware.biometrics.BiometricConstants.LockoutMode; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; +import static android.hardware.biometrics.BiometricSourceType.FACE; +import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; @@ -228,7 +230,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Biometric authentication: Cancelling and waiting for the relevant biometric service to * send us the confirmation that cancellation has happened. */ - private static final int BIOMETRIC_STATE_CANCELLING = 2; + @VisibleForTesting + protected static final int BIOMETRIC_STATE_CANCELLING = 2; + + /** + * Biometric state: During cancelling we got another request to start listening, so when we + * receive the cancellation done signal, we should start listening again. + */ + @VisibleForTesting + protected static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3; /** * Action indicating keyguard *can* start biometric authentiation. @@ -243,12 +253,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ private static final int BIOMETRIC_ACTION_UPDATE = 2; - /** - * Biometric state: During cancelling we got another request to start listening, so when we - * receive the cancellation done signal, we should start listening again. - */ - private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3; - @VisibleForTesting public static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1; public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2; @@ -356,7 +360,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private KeyguardBypassController mKeyguardBypassController; private List<SubscriptionInfo> mSubscriptionInfo; - private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + @VisibleForTesting + protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; private int mFaceRunningState = BIOMETRIC_STATE_STOPPED; private boolean mIsDreaming; private boolean mLogoutEnabled; @@ -790,7 +795,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab new BiometricAuthenticated(true, isStrongBiometric)); // Update/refresh trust state only if user can skip bouncer if (getUserCanSkipBouncer(userId)) { - mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT); + mTrustManager.unlockedByBiometricForUser(userId, FINGERPRINT); } // Don't send cancel if authentication succeeds mFingerprintCancelSignal = null; @@ -800,7 +805,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT, + cb.onBiometricAuthenticated(userId, FINGERPRINT, isStrongBiometric); } } @@ -833,7 +838,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); + cb.onBiometricAuthFailed(FINGERPRINT); } } if (isUdfpsSupported()) { @@ -858,7 +863,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT, acquireInfo); + cb.onBiometricAcquired(FINGERPRINT, acquireInfo); } } } @@ -892,7 +897,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT); + cb.onBiometricHelp(msgId, helpString, FINGERPRINT); } } } @@ -944,7 +949,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { lockedOutStateChanged = !mFingerprintLockedOutPermanent; mFingerprintLockedOutPermanent = true; - mLogger.d("Fingerprint locked out - requiring strong auth"); + mLogger.d("Fingerprint permanently locked out - requiring stronger auth"); mLockPatternUtils.requireStrongAuth( STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser()); } @@ -953,6 +958,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { lockedOutStateChanged |= !mFingerprintLockedOut; mFingerprintLockedOut = true; + mLogger.d("Fingerprint temporarily locked out - requiring stronger auth"); if (isUdfpsEnrolled()) { updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } @@ -963,12 +969,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT); + cb.onBiometricError(msgId, errString, FINGERPRINT); } } if (lockedOutStateChanged) { - notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT); + notifyLockedOutStateChanged(FINGERPRINT); } } @@ -996,7 +1002,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (changed) { - notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT); + notifyLockedOutStateChanged(FINGERPRINT); } } @@ -1019,7 +1025,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(), - BiometricSourceType.FINGERPRINT); + FINGERPRINT); } } } @@ -1032,7 +1038,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab new BiometricAuthenticated(true, isStrongBiometric)); // Update/refresh trust state only if user can skip bouncer if (getUserCanSkipBouncer(userId)) { - mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE); + mTrustManager.unlockedByBiometricForUser(userId, FACE); } // Don't send cancel if authentication succeeds mFaceCancelSignal = null; @@ -1043,7 +1049,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onBiometricAuthenticated(userId, - BiometricSourceType.FACE, + FACE, isStrongBiometric); } } @@ -1065,7 +1071,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricAuthFailed(BiometricSourceType.FACE); + cb.onBiometricAuthFailed(FACE); } } handleFaceHelp(BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, @@ -1078,7 +1084,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricAcquired(BiometricSourceType.FACE, acquireInfo); + cb.onBiometricAcquired(FACE, acquireInfo); } } } @@ -1113,7 +1119,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE); + cb.onBiometricHelp(msgId, helpString, FACE); } } } @@ -1181,12 +1187,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onBiometricError(msgId, errString, - BiometricSourceType.FACE); + FACE); } } if (lockedOutStateChanged) { - notifyLockedOutStateChanged(BiometricSourceType.FACE); + notifyLockedOutStateChanged(FACE); } } @@ -1200,7 +1206,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET), getBiometricLockoutDelay()); if (changed) { - notifyLockedOutStateChanged(BiometricSourceType.FACE); + notifyLockedOutStateChanged(FACE); } } @@ -1223,7 +1229,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onBiometricRunningStateChanged(isFaceDetectionRunning(), - BiometricSourceType.FACE); + FACE); } } } @@ -1364,7 +1370,39 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) { - return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric); + // StrongAuthTracker#isUnlockingWithBiometricAllowed includes + // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent; + // however the strong auth tracker does not include the temporary lockout + // mFingerprintLockedOut. + return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric) + && !mFingerprintLockedOut; + } + + private boolean isUnlockingWithFaceAllowed() { + return mStrongAuthTracker.isUnlockingWithBiometricAllowed(false); + } + + /** + * Whether fingerprint is allowed ot be used for unlocking based on the strongAuthTracker + * and temporary lockout state (tracked by FingerprintManager via error codes). + */ + public boolean isUnlockingWithFingerprintAllowed() { + return isUnlockingWithBiometricAllowed(true); + } + + /** + * Whether the given biometric is allowed based on strongAuth & lockout states. + */ + public boolean isUnlockingWithBiometricAllowed( + @NonNull BiometricSourceType biometricSourceType) { + switch (biometricSourceType) { + case FINGERPRINT: + return isUnlockingWithFingerprintAllowed(); + case FACE: + return isUnlockingWithFaceAllowed(); + default: + return false; + } } public boolean isUserInLockdown(int userId) { @@ -1386,11 +1424,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return isEncrypted || isLockDown; } - public boolean userNeedsStrongAuth() { - return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser()) - != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; - } - private boolean containsFlag(int haystack, int needle) { return (haystack & needle) != 0; } @@ -1560,12 +1593,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; - private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback - = (sensorId, userId, isStrongBiometric) -> { - // Trigger the fingerprint success path so the bouncer can be shown - handleFingerprintAuthenticated(userId, isStrongBiometric); - }; - /** * Propagates a pointer down event to keyguard. */ @@ -2636,27 +2663,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser && biometricEnabledForUser; - - final boolean shouldListenBouncerState = !(mFingerprintLockedOut - && mPrimaryBouncerIsOrWillBeShowing && mCredentialAttempted); - - final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user); + final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed(); + final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled(); + final boolean shouldListenBouncerState = + !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing; final boolean shouldListenUdfpsState = !isUdfps || (!userCanSkipBouncer - && !isEncryptedOrLockdownForUser + && !strongerAuthRequired && userDoesNotHaveTrust); boolean shouldListenSideFpsState = true; - if (isSfpsSupported() && isSfpsEnrolled()) { + if (isSideFps) { shouldListenSideFpsState = mSfpsRequireScreenOnToAuthPrefEnabled ? isDeviceInteractive() : true; } boolean shouldListen = shouldListenKeyguardState && shouldListenUserState - && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut() + && shouldListenBouncerState && shouldListenUdfpsState && shouldListenSideFpsState; - maybeLogListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), @@ -2668,7 +2693,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mCredentialAttempted, mDeviceInteractive, mIsDreaming, - isEncryptedOrLockdownForUser, fingerprintDisabledForUser, mFingerprintLockedOut, mGoingToSleep, @@ -2679,6 +2703,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mIsPrimaryUser, shouldListenSideFpsState, shouldListenForFingerprintAssistant, + strongerAuthRequired, mSwitchingUser, isUdfps, userDoesNotHaveTrust)); @@ -2706,10 +2731,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean isEncryptedOrTimedOut = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT) || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT); - - // TODO: always disallow when fp is already locked out? - final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent; - + final boolean fpLockedOut = isFingerprintLockedOut(); final boolean canBypass = mKeyguardBypassController != null && mKeyguardBypassController.canBypass(); // There's no reason to ask the HAL for authentication when the user can dismiss the @@ -2831,15 +2853,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Waiting for restart via handleFingerprintError(). return; } - mLogger.v("startListeningForFingerprint()"); if (unlockPossible) { mFingerprintCancelSignal = new CancellationSignal(); - if (isEncryptedOrLockdown(userId)) { - mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback, + if (!isUnlockingWithFingerprintAllowed()) { + mLogger.v("startListeningForFingerprint - detect"); + mFpm.detectFingerprint( + mFingerprintCancelSignal, + (sensorId, user, isStrongBiometric) -> { + mLogger.d("fingerprint detected"); + // Trigger the fingerprint success path so the bouncer can be shown + handleFingerprintAuthenticated(user, isStrongBiometric); + }, userId); } else { + mLogger.v("startListeningForFingerprint - authenticate"); mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal, mFingerprintAuthenticationCallback, null /* handler */, FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */); @@ -3056,11 +3085,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + // Immediately stop previous biometric listening states. + // Resetting lockout states updates the biometric listening states. if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) { + stopListeningForFace(FACE_AUTH_UPDATED_USER_SWITCHING); handleFaceLockoutReset(mFaceManager.getLockoutModeForUser( mFaceSensorProperties.get(0).sensorId, userId)); } if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) { + stopListeningForFingerprint(); handleFingerprintLockoutReset(mFpm.getLockoutModeForUser( mFingerprintSensorProperties.get(0).sensorId, userId)); } @@ -3467,7 +3500,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @AnyThread public void setSwitchingUser(boolean switching) { mSwitchingUser = switching; - // Since this comes in on a binder thread, we need to post if first + // Since this comes in on a binder thread, we need to post it first mHandler.post(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_USER_SWITCHING)); } @@ -3559,8 +3592,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Assert.isMainThread(); mUserFingerprintAuthenticated.clear(); mUserFaceAuthenticated.clear(); - mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser); - mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser); + mTrustManager.clearAllBiometricRecognized(FINGERPRINT, unlockedUser); + mTrustManager.clearAllBiometricRecognized(FACE, unlockedUser); mLogger.d("clearBiometricRecognized"); for (int i = 0; i < mCallbacks.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index fc5f4470cc3c..6ac54feeb935 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -116,9 +116,9 @@ class AuthRippleController @Inject constructor( notificationShadeWindowController.setForcePluginOpen(false, this) } - fun showUnlockRipple(biometricSourceType: BiometricSourceType?) { + fun showUnlockRipple(biometricSourceType: BiometricSourceType) { if (!keyguardStateController.isShowing || - keyguardUpdateMonitor.userNeedsStrongAuth()) { + !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(biometricSourceType)) { return } @@ -246,7 +246,7 @@ class AuthRippleController @Inject constructor( object : KeyguardUpdateMonitorCallback() { override fun onBiometricAuthenticated( userId: Int, - biometricSourceType: BiometricSourceType?, + biometricSourceType: BiometricSourceType, isStrongBiometric: Boolean ) { if (biometricSourceType == BiometricSourceType.FINGERPRINT) { @@ -255,14 +255,14 @@ class AuthRippleController @Inject constructor( showUnlockRipple(biometricSourceType) } - override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) { + override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) { if (biometricSourceType == BiometricSourceType.FINGERPRINT) { mView.retractDwellRipple() } } override fun onBiometricAcquired( - biometricSourceType: BiometricSourceType?, + biometricSourceType: BiometricSourceType, acquireInfo: Int ) { if (biometricSourceType == BiometricSourceType.FINGERPRINT && diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index bbaf47dfa0f9..aa6c619d9969 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -391,7 +391,7 @@ object Flags { // 1600 - accessibility @JvmField val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS = - unreleasedFlag(1600, "a11y_floating_menu_fling_spring_animations") + unreleasedFlag(1600, "a11y_floating_menu_fling_spring_animations", teamfood = true) // 1700 - clipboard @JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index 84a80744185e..2cf5fb98d07e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.content.res.ColorStateList +import android.hardware.biometrics.BiometricSourceType import android.os.Handler import android.os.Trace import android.os.UserHandle @@ -71,7 +72,7 @@ constructor( KeyguardUpdateMonitor.getCurrentUser() ) && !needsFullscreenBouncer() && - !keyguardUpdateMonitor.userNeedsStrongAuth() && + keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) && !keyguardBypassController.bypassEnabled /** Runnable to show the primary bouncer. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 8609e4af13f6..10d31ea2d277 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -972,13 +972,8 @@ public class ScreenshotController { if (imageData.uri != null) { if (!imageData.owner.equals(Process.myUserHandle())) { - // TODO: Handle non-primary user ownership (e.g. Work Profile) - // This image is owned by another user. Special treatment will be - // required in the UI (badging) as well as sending intents which can - // correctly forward those URIs on to be read (actions). - - Log.d(TAG, "*** Screenshot saved to a non-primary user (" - + imageData.owner + ") as " + imageData.uri); + Log.d(TAG, "Screenshot saved to user " + imageData.owner + " as " + + imageData.uri); } mScreenshotHandler.post(() -> { if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { @@ -1059,6 +1054,11 @@ public class ScreenshotController { R.string.screenshot_failed_to_save_text); } else { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName); + if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY) + && mUserManager.isManagedProfile(imageData.owner.getIdentifier())) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0, + mPackageName); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index a3d9965e9397..7fbdeca3963a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -2072,13 +2072,6 @@ public final class NotificationPanelViewController implements Dumpable { mInitialTouchX = x; initVelocityTracker(); trackMovement(event); - float qsExpansionFraction = computeQsExpansionFraction(); - // Intercept the touch if QS is between fully collapsed and fully expanded state - if (qsExpansionFraction > 0.0 && qsExpansionFraction < 1.0) { - mShadeLog.logMotionEvent(event, - "onQsIntercept: down action, QS partially expanded/collapsed"); - return true; - } if (mKeyguardShowing && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { // Dragging down on the lockscreen statusbar should prohibit other interactions @@ -2331,13 +2324,6 @@ public final class NotificationPanelViewController implements Dumpable { if (!isFullyCollapsed()) { handleQsDown(event); } - // defer touches on QQS to shade while shade is collapsing. Added margin for error - // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS. - if (computeQsExpansionFraction() <= 0.01 && getExpandedFraction() < 1.0) { - mShadeLog.logMotionEvent(event, - "handleQsTouch: QQS touched while shade collapsing"); - mQsTracking = false; - } if (!mQsExpandImmediate && mQsTracking) { onQsTouch(event); if (!mConflictingQsExpansionGesture && !mSplitShadeEnabled) { @@ -2578,6 +2564,7 @@ public final class NotificationPanelViewController implements Dumpable { // Reset scroll position and apply that position to the expanded height. float height = mQsExpansionHeight; setQsExpansionHeight(height); + updateExpandedHeightToMaxHeight(); mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); // When expanding QS, let's authenticate the user if possible, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index aa0757e1d572..000fe140882c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -240,8 +240,8 @@ public class KeyguardBouncer { && !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( KeyguardUpdateMonitor.getCurrentUser()) && !needsFullscreenBouncer() - && !mKeyguardUpdateMonitor.isFaceLockedOut() - && !mKeyguardUpdateMonitor.userNeedsStrongAuth() + && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + BiometricSourceType.FACE) && !mKeyguardBypassController.getBypassEnabled()) { mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); } else { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt index 88396628017b..afd582a3b822 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt @@ -63,7 +63,6 @@ private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel( credentialAttempted = false, deviceInteractive = false, dreaming = false, - encryptedOrLockdown = false, fingerprintDisabled = false, fingerprintLockedOut = false, goingToSleep = false, @@ -74,6 +73,7 @@ private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel( primaryUser = false, shouldListenSfpsState = false, shouldListenForFingerprintAssistant = false, + strongerAuthRequired = false, switchingUser = false, udfps = false, userDoesNotHaveTrust = false diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 4d58b09f1076..e39b9b58158f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -379,9 +379,9 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @Test - public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() { + public void onBouncerVisibilityChanged_unlockingWithFingerprintNotAllowed_sideFpsHintHidden() { setupConditionsToEnableSideFpsHint(); - setNeedsStrongAuth(true); + setUnlockingWithFingerprintAllowed(false); reset(mSideFpsController); mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); @@ -574,7 +574,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { attachView(); setSideFpsHintEnabledFromResources(true); setFingerprintDetectionRunning(true); - setNeedsStrongAuth(false); + setUnlockingWithFingerprintAllowed(true); } private void attachView() { @@ -593,9 +593,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { enabled); } - private void setNeedsStrongAuth(boolean needed) { - when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed); - mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0); + private void setUnlockingWithFingerprintAllowed(boolean allowed) { + when(mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()).thenReturn(allowed); } private void setupGetSecurityView() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 7231b3427a71..63e160331e6c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -28,6 +28,7 @@ import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; +import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING; import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser; @@ -281,7 +282,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { componentInfo, FaceSensorProperties.TYPE_UNKNOWN, false /* supportsFaceDetection */, true /* supportsSelfIllumination */, false /* resetLockoutRequiresChallenge */)); - when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of( @@ -594,30 +594,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testFingerprintDoesNotAuth_whenEncrypted() { - testFingerprintWhenStrongAuth( - STRONG_AUTH_REQUIRED_AFTER_BOOT); - } - - @Test - public void testFingerprintDoesNotAuth_whenDpmLocked() { - testFingerprintWhenStrongAuth( - KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW); - } - - @Test - public void testFingerprintDoesNotAuth_whenUserLockdown() { - testFingerprintWhenStrongAuth( - KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - } - - private void testFingerprintWhenStrongAuth(int strongAuth) { + public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() { // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) // will trigger updateBiometricListeningState(); clearInvocations(mFingerprintManager); mKeyguardUpdateMonitor.resetBiometricListeningState(); - when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); mTestableLooper.processAllMessages(); @@ -928,10 +911,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE; final boolean fpLocked = fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE; - when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser))) - .thenReturn(fingerprintLockoutMode); - when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser))) - .thenReturn(faceLockoutMode); mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); mTestableLooper.processAllMessages(); @@ -940,7 +919,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean()); verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); +// resetFaceManager(); +// resetFingerprintManager(); + when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser))) + .thenReturn(fingerprintLockoutMode); + when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser))) + .thenReturn(faceLockoutMode); final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal); final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal); mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel; @@ -951,14 +936,22 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.handleUserSwitchComplete(newUser); mTestableLooper.processAllMessages(); - verify(faceCancel, faceLocked ? times(1) : never()).cancel(); - verify(fpCancel, fpLocked ? times(1) : never()).cancel(); - verify(callback, faceLocked ? times(1) : never()).onBiometricRunningStateChanged( + // THEN face and fingerprint listening are always cancelled immediately + verify(faceCancel).cancel(); + verify(callback).onBiometricRunningStateChanged( eq(false), eq(BiometricSourceType.FACE)); - verify(callback, fpLocked ? times(1) : never()).onBiometricRunningStateChanged( + verify(fpCancel).cancel(); + verify(callback).onBiometricRunningStateChanged( eq(false), eq(BiometricSourceType.FINGERPRINT)); + + // THEN locked out states are updated assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLocked); assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLocked); + + // Fingerprint should be restarted once its cancelled bc on lockout, the device + // can still detectFingerprint (and if it's not locked out, fingerprint can listen) + assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState) + .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING); } @Test @@ -1144,9 +1137,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // GIVEN status bar state is on the keyguard mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); - // WHEN user hasn't authenticated since last boot - when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser())) - .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT); + // WHEN user hasn't authenticated since last boot, cannot unlock with FP + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); // THEN we shouldn't listen for udfps assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false); @@ -1258,8 +1250,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); // WHEN device in lock down - when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn( - KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); // THEN we shouldn't listen for udfps assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 0b528a5c8d1e..eb8c823ffe1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -37,7 +37,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.leak.RotationUtils -import javax.inject.Provider +import com.android.systemui.util.mockito.any import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -46,15 +46,16 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.eq import org.mockito.Mock -import org.mockito.Mockito.any +import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.MockitoSession import org.mockito.quality.Strictness +import javax.inject.Provider @SmallTest @RunWith(AndroidTestingRunner::class) @@ -118,12 +119,13 @@ class AuthRippleControllerTest : SysuiTestCase() { @Test fun testFingerprintTrigger_KeyguardShowing_Ripple() { - // GIVEN fp exists, keyguard is showing, user doesn't need strong auth + // GIVEN fp exists, keyguard is showing, unlocking with fp allowed val fpsLocation = Point(5, 5) `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) // WHEN fingerprint authenticated val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) @@ -140,11 +142,12 @@ class AuthRippleControllerTest : SysuiTestCase() { @Test fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() { - // GIVEN fp exists & user doesn't need strong auth + // GIVEN fp exists & unlocking with fp allowed val fpsLocation = Point(5, 5) `when`(authController.udfpsLocation).thenReturn(fpsLocation) controller.onViewAttached() - `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) // WHEN keyguard is NOT showing & fingerprint authenticated `when`(keyguardStateController.isShowing).thenReturn(false) @@ -160,15 +163,16 @@ class AuthRippleControllerTest : SysuiTestCase() { } @Test - fun testFingerprintTrigger_StrongAuthRequired_NoRipple() { + fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() { // GIVEN fp exists & keyguard is showing val fpsLocation = Point(5, 5) `when`(authController.udfpsLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - // WHEN user needs strong auth & fingerprint authenticated - `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(true) + // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FINGERPRINT))).thenReturn(false) val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) verify(keyguardUpdateMonitor).registerCallback(captor.capture()) captor.value.onBiometricAuthenticated( @@ -182,13 +186,14 @@ class AuthRippleControllerTest : SysuiTestCase() { @Test fun testFaceTriggerBypassEnabled_Ripple() { - // GIVEN face auth sensor exists, keyguard is showing & strong auth isn't required + // GIVEN face auth sensor exists, keyguard is showing & unlocking with face is allowed val faceLocation = Point(5, 5) `when`(authController.faceSensorLocation).thenReturn(faceLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + BiometricSourceType.FACE)).thenReturn(true) // WHEN bypass is enabled & face authenticated `when`(bypassController.canBypass()).thenReturn(true) @@ -275,6 +280,8 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardStateController.isShowing).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + BiometricSourceType.FINGERPRINT)).thenReturn(true) `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) controller.showUnlockRipple(BiometricSourceType.FINGERPRINT) @@ -295,6 +302,8 @@ class AuthRippleControllerTest : SysuiTestCase() { `when`(keyguardStateController.isShowing).thenReturn(true) `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) `when`(authController.isUdfpsFingerDown).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( + eq(BiometricSourceType.FACE))).thenReturn(true) controller.showUnlockRipple(BiometricSourceType.FACE) assertTrue("reveal didn't start on keyguardFadingAway", diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index d3b541899635..df7ee432e79e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -39,6 +39,7 @@ import static org.mockito.Mockito.when; import android.content.res.ColorStateList; import android.graphics.Color; +import android.hardware.biometrics.BiometricSourceType; import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -398,6 +399,8 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testShow_delaysIfFaceAuthIsRunning() { + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) + .thenReturn(true); when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true); mBouncer.show(true /* reset */); @@ -410,9 +413,10 @@ public class KeyguardBouncerTest extends SysuiTestCase { } @Test - public void testShow_doesNotDelaysIfFaceAuthIsLockedOut() { + public void testShow_doesNotDelaysIfFaceAuthIsNotAllowed() { when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true); - when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true); + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) + .thenReturn(false); mBouncer.show(true /* reset */); verify(mHandler, never()).postDelayed(any(), anyLong()); diff --git a/services/OWNERS b/services/OWNERS index 495c0737e599..eace9060277d 100644 --- a/services/OWNERS +++ b/services/OWNERS @@ -3,7 +3,7 @@ per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION # art-team@ manages the system server profile per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com -per-file java/com/android/server/* = toddke@google.com,patb@google.com +per-file java/com/android/server/* = patb@google.com #{LAST_RESORT_SUGGESTION} per-file tests/servicestests/src/com/android/server/systemconfig/* = patb@google.com per-file proguard.flags = jdduke@google.com diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 87d166815323..630cd35029bd 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -860,7 +860,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid()); } return IntPair.of( - getClientStateLocked(userState), + combineUserStateAndProxyState(getClientStateLocked(userState), + mProxyManager.getStateLocked()), client.mLastSentRelevantEventTypes); } else { userState.mUserClients.register(callback, client); @@ -872,7 +873,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + " and userId:" + mCurrentUserId); } return IntPair.of( - (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0, + (resolvedUserId == mCurrentUserId) ? combineUserStateAndProxyState( + getClientStateLocked(userState), mProxyManager.getStateLocked()) + : 0, client.mLastSentRelevantEventTypes); } } @@ -1003,6 +1006,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub notifyAccessibilityServicesDelayedLocked(event, false); notifyAccessibilityServicesDelayedLocked(event, true); mUiAutomationManager.sendAccessibilityEventLocked(event); + mProxyManager.sendAccessibilityEvent(event); } private void sendAccessibilityEventToInputFilter(AccessibilityEvent event) { @@ -1143,9 +1147,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } List<AccessibilityServiceConnection> services = getUserStateLocked(resolvedUserId).mBoundServices; - int numServices = services.size(); + int numServices = services.size() + mProxyManager.getNumProxys(); interfacesToInterrupt = new ArrayList<>(numServices); - for (int i = 0; i < numServices; i++) { + for (int i = 0; i < services.size(); i++) { AccessibilityServiceConnection service = services.get(i); IBinder a11yServiceBinder = service.mService; IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface; @@ -1153,6 +1157,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub interfacesToInterrupt.add(a11yServiceInterface); } } + mProxyManager.addServiceInterfaces(interfacesToInterrupt); } for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) { try { @@ -1941,6 +1946,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mUiAutomationManager.getServiceInfo(), client) ? mUiAutomationManager.getRelevantEventTypes() : 0; + relevantEventTypes |= mProxyManager.getRelevantEventTypes(); return relevantEventTypes; } @@ -2178,21 +2184,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub updateAccessibilityEnabledSettingLocked(userState); } - void scheduleUpdateClientsIfNeeded(AccessibilityUserState userState) { - synchronized (mLock) { - scheduleUpdateClientsIfNeededLocked(userState); - } + private int combineUserStateAndProxyState(int userState, int proxyState) { + return userState | proxyState; } void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) { final int clientState = getClientStateLocked(userState); - if (userState.getLastSentClientStateLocked() != clientState + final int proxyState = mProxyManager.getStateLocked(); + if ((userState.getLastSentClientStateLocked() != clientState + || mProxyManager.getLastSentStateLocked() != proxyState) && (mGlobalClients.getRegisteredCallbackCount() > 0 - || userState.mUserClients.getRegisteredCallbackCount() > 0)) { + || userState.mUserClients.getRegisteredCallbackCount() > 0)) { userState.setLastSentClientStateLocked(clientState); + mProxyManager.setLastStateLocked(proxyState); + // Send both the user and proxy state to the app for now. + // TODO(b/250929565): Send proxy state to proxy clients mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendStateToAllClients, - this, clientState, userState.mUserId)); + this, combineUserStateAndProxyState(clientState, proxyState), + userState.mUserId)); } } @@ -2434,7 +2444,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // binding we do an update pass after each bind event, so we run this // code and register the callback if needed. - boolean observingWindows = mUiAutomationManager.canRetrieveInteractiveWindowsLocked(); + boolean observingWindows = mUiAutomationManager.canRetrieveInteractiveWindowsLocked() + || mProxyManager.canRetrieveInteractiveWindowsLocked(); List<AccessibilityServiceConnection> boundServices = userState.mBoundServices; final int boundServiceCount = boundServices.size(); for (int i = 0; !observingWindows && (i < boundServiceCount); i++) { @@ -3657,7 +3668,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + "proxy-ed"); } - mProxyManager.registerProxy(client, displayId); + mProxyManager.registerProxy(client, displayId, mContext, + sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(), + mWindowManagerService, mA11yWindowManager); return true; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index e5e1d027dd11..ce269f5eca59 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -696,7 +696,7 @@ public class AccessibilitySecurityPolicy { final ResolveInfo resolveInfo = service.getServiceInfo().getResolveInfo(); if (resolveInfo == null) { - // For InteractionBridge and UiAutomation + // For InteractionBridge, UiAutomation, and Proxy. return true; } diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java index 247f320adecd..d7f9c12b7885 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java @@ -16,8 +16,12 @@ package com.android.server.accessibility; +import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_CLASS_NAME; +import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_PACKAGE_NAME; + import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityTrace; +import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.MagnificationConfig; import android.annotation.NonNull; import android.content.ComponentName; @@ -31,6 +35,7 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallback; +import android.os.RemoteException; import android.view.KeyEvent; import android.view.accessibility.AccessibilityDisplayProxy; import android.view.accessibility.AccessibilityNodeInfo; @@ -53,10 +58,6 @@ import java.util.Set; * TODO(241429275): Initialize this when a proxy is registered. */ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection { - // Names used to populate ComponentName and ResolveInfo - private static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage"; - private static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass"; - private int mDisplayId; private List<AccessibilityServiceInfo> mInstalledAndEnabledServices; @@ -76,6 +77,16 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon } /** + * Called when the proxy is registered. + */ + void initializeServiceInterface(IAccessibilityServiceClient serviceInterface) + throws RemoteException { + mServiceInterface = serviceInterface; + mService = serviceInterface.asBinder(); + mServiceInterface.init(this, mId, this.mOverlayWindowTokens.get(mDisplayId)); + } + + /** * Keeps mAccessibilityServiceInfo in sync with the proxy's list of AccessibilityServiceInfos. * * <p>This also sets the properties that are assumed to be populated by installed packages. @@ -89,7 +100,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon synchronized (mLock) { mInstalledAndEnabledServices = infos; final AccessibilityServiceInfo proxyInfo = mAccessibilityServiceInfo; - // Reset values. + // Reset values. mAccessibilityServiceInfo is not completely reset since it is final proxyInfo.flags = 0; proxyInfo.eventTypes = 0; proxyInfo.notificationTimeout = 0; diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java index a2ce61063aaf..2184878b4c36 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java @@ -14,9 +14,21 @@ * limitations under the License. */ package com.android.server.accessibility; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; +import android.content.ComponentName; +import android.content.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.SparseArray; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; -import java.util.HashSet; +import com.android.server.wm.WindowManagerInternal; + +import java.util.List; /** * Manages proxy connections. @@ -27,32 +39,185 @@ import java.util.HashSet; * TODO(241117292): Remove or cut down during simultaneous user refactoring. */ public class ProxyManager { + // Names used to populate ComponentName and ResolveInfo in connection.mA11yServiceInfo and in + // the infos of connection.setInstalledAndEnabledServices + static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage"; + static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass"; + private final Object mLock; - private final HashSet<Integer> mDisplayIds = new HashSet<>(); + + // Used to determine if we should notify AccessibilityManager clients of updates. + // TODO(254545943): Separate this so each display id has its own state. Currently there is no + // way to identify from AccessibilityManager which proxy state should be returned. + private int mLastState = -1; + + private SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections = + new SparseArray<>(); ProxyManager(Object lock) { mLock = lock; } /** - * TODO: Create the proxy service connection. + * Creates the service connection. */ - public void registerProxy(IAccessibilityServiceClient client, int displayId) { - mDisplayIds.add(displayId); + public void registerProxy(IAccessibilityServiceClient client, int displayId, + Context context, + int id, Handler mainHandler, + AccessibilitySecurityPolicy securityPolicy, + AbstractAccessibilityServiceConnection.SystemSupport systemSupport, + AccessibilityTrace trace, + WindowManagerInternal windowManagerInternal, + AccessibilityWindowManager awm) throws RemoteException { + + // Set a default AccessibilityServiceInfo that is used before the proxy's info is + // populated. A proxy has the touch exploration and window capabilities. + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION + | AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); + final String componentClassDisplayName = PROXY_COMPONENT_CLASS_NAME + displayId; + info.setComponentName(new ComponentName(PROXY_COMPONENT_PACKAGE_NAME, + componentClassDisplayName)); + ProxyAccessibilityServiceConnection connection = + new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info, + id, mainHandler, mLock, securityPolicy, systemSupport, trace, + windowManagerInternal, + awm, displayId); + + mProxyA11yServiceConnections.put(displayId, connection); + + // If the client dies, make sure to remove the connection. + IBinder.DeathRecipient deathRecipient = + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + client.asBinder().unlinkToDeath(this, 0); + clearConnection(displayId); + } + }; + client.asBinder().linkToDeath(deathRecipient, 0); + // Notify apps that the service state has changed. + // A11yManager#A11yServicesStateChangeListener + connection.mSystemSupport.onClientChangeLocked(true); + + connection.initializeServiceInterface(client); } /** - * TODO: Unregister the proxy service connection based on display id. + * Unregister the proxy based on display id. */ public boolean unregisterProxy(int displayId) { - mDisplayIds.remove(displayId); - return true; + return clearConnection(displayId); + } + + private boolean clearConnection(int displayId) { + if (mProxyA11yServiceConnections.contains(displayId)) { + mProxyA11yServiceConnections.remove(displayId); + return true; + } + return false; } /** * Checks if a display id is being proxy-ed. */ public boolean isProxyed(int displayId) { - return mDisplayIds.contains(displayId); + return mProxyA11yServiceConnections.contains(displayId); + } + + /** + * Sends AccessibilityEvents to all proxies. + * {@link android.view.accessibility.AccessibilityDisplayProxy} will filter based on display. + * TODO(b/250929565): Filtering should happen in the system, not in the proxy. + */ + public void sendAccessibilityEvent(AccessibilityEvent event) { + for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { + ProxyAccessibilityServiceConnection proxy = + mProxyA11yServiceConnections.valueAt(i); + proxy.notifyAccessibilityEvent(event); + } + } + + /** + * Returns {@code true} if any proxy can retrieve windows. + * TODO(b/250929565): Retrieve per connection/user state. + */ + public boolean canRetrieveInteractiveWindowsLocked() { + boolean observingWindows = false; + for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { + final ProxyAccessibilityServiceConnection proxy = + mProxyA11yServiceConnections.valueAt(i); + if (proxy.mRetrieveInteractiveWindows) { + observingWindows = true; + break; + } + } + return observingWindows; + } + + /** + * If there is at least one proxy, accessibility is enabled. + */ + public int getStateLocked() { + int clientState = 0; + final boolean a11yEnabled = mProxyA11yServiceConnections.size() > 0; + if (a11yEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; + } + return clientState; + // TODO(b/254545943): When A11yManager is separated, include support for other properties + // like isTouchExplorationEnabled. + } + + /** + * Gets the last state. + */ + public int getLastSentStateLocked() { + return mLastState; + } + + /** + * Sets the last state. + */ + public void setLastStateLocked(int proxyState) { + mLastState = proxyState; + } + + /** + * Returns the relevant event types of every proxy. + * TODO(254545943): When A11yManager is separated, return based on the A11yManager display. + */ + public int getRelevantEventTypes() { + int relevantEventTypes = 0; + for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { + ProxyAccessibilityServiceConnection proxy = + mProxyA11yServiceConnections.valueAt(i); + relevantEventTypes |= proxy.getRelevantEventTypes(); + } + return relevantEventTypes; + } + + /** + * Gets the number of current proxy connections. + * @return + */ + public int getNumProxys() { + return mProxyA11yServiceConnections.size(); + } + + /** + * Adds the service interfaces to a list. + * @param interfaces + */ + public void addServiceInterfaces(List<IAccessibilityServiceClient> interfaces) { + for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { + final ProxyAccessibilityServiceConnection proxy = + mProxyA11yServiceConnections.valueAt(i); + final IBinder proxyBinder = proxy.mService; + final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface; + if ((proxyBinder != null) && (proxyInterface != null)) { + interfaces.add(proxyInterface); + } + } } -} +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 544dd4e6dcff..68880bd09a40 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -73,6 +73,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.Executors; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @hide @@ -104,6 +105,9 @@ public class BinaryTransparencyService extends SystemService { @VisibleForTesting static final String BUNDLE_CONTENT_DIGEST = "content-digest"; + static final String APEX_PRELOAD_LOCATION = "/system/apex/"; + static final String APEX_PRELOAD_LOCATION_ERROR = "could-not-be-determined"; + // used for indicating any type of error during MBA measurement static final int MBA_STATUS_ERROR = 0; // used for indicating factory condition preloads @@ -1110,18 +1114,22 @@ public class BinaryTransparencyService extends SystemService { } } - // TODO(b/259349011): Need to be more robust against package name mismatch in the filename + @NonNull private String getOriginalApexPreinstalledLocation(String packageName, String currentInstalledLocation) { - if (currentInstalledLocation.contains("/decompressed/")) { - String resultPath = "system/apex" + packageName + ".capex"; - File f = new File(resultPath); - if (f.exists()) { - return resultPath; + // get a listing of all apex files in /system/apex/ + Set<String> originalApexs = Stream.of(new File(APEX_PRELOAD_LOCATION).listFiles()) + .filter(f -> !f.isDirectory()) + .map(File::getName) + .collect(Collectors.toSet()); + + for (String originalApex : originalApexs) { + if (originalApex.startsWith(packageName)) { + return APEX_PRELOAD_LOCATION + originalApex; } - return "/system/apex/" + packageName + ".next.capex"; } - return "/system/apex" + packageName + "apex"; + + return APEX_PRELOAD_LOCATION_ERROR; } /** diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java index 104d10de93c8..540ed4cdb330 100644 --- a/services/core/java/com/android/server/DockObserver.java +++ b/services/core/java/com/android/server/DockObserver.java @@ -302,6 +302,7 @@ final class DockObserver extends SystemService { getContext(), soundUri); if (sfx != null) { sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.preferBuiltinDevice(true); sfx.play(); } } diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 02c6ca20d34e..27ee627d1b48 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -91,7 +91,7 @@ public final class DropBoxManagerService extends SystemService { private static final int DEFAULT_MAX_FILES_LOWRAM = 300; private static final int DEFAULT_QUOTA_KB = 10 * 1024; private static final int DEFAULT_QUOTA_PERCENT = 10; - private static final int DEFAULT_RESERVE_PERCENT = 10; + private static final int DEFAULT_RESERVE_PERCENT = 0; private static final int QUOTA_RESCAN_MILLIS = 5000; private static final boolean PROFILE_DUMP = false; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 30d4b8b7bd4a..ca86021cd629 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -52,8 +52,8 @@ import android.telephony.Annotation; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SrvccState; import android.telephony.BarringInfo; +import android.telephony.CallAttributes; import android.telephony.CallQuality; -import android.telephony.CallState; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.CellSignalStrength; @@ -82,7 +82,6 @@ import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.telephony.emergency.EmergencyNumber; -import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; import android.text.TextUtils; import android.util.ArrayMap; @@ -350,9 +349,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private CallQuality[] mCallQuality; - private ArrayList<List<CallState>> mCallStateLists; + private CallAttributes[] mCallAttributes; - // network type of the call associated with the mCallStateLists and mCallQuality + // network type of the call associated with the mCallAttributes and mCallQuality private int[] mCallNetworkType; private int[] mSrvccState; @@ -688,6 +687,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallPreciseDisconnectCause = copyOf(mCallPreciseDisconnectCause, mNumPhones); mCallQuality = copyOf(mCallQuality, mNumPhones); mCallNetworkType = copyOf(mCallNetworkType, mNumPhones); + mCallAttributes = copyOf(mCallAttributes, mNumPhones); mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones); mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones); mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones); @@ -707,7 +707,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mLinkCapacityEstimateLists, mNumPhones); cutListToSize(mCarrierPrivilegeStates, mNumPhones); cutListToSize(mCarrierServiceStates, mNumPhones); - cutListToSize(mCallStateLists, mNumPhones); return; } @@ -731,7 +730,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallDisconnectCause[i] = DisconnectCause.NOT_VALID; mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID; mCallQuality[i] = createCallQuality(); - mCallStateLists.add(i, new ArrayList<>()); + mCallAttributes[i] = new CallAttributes(createPreciseCallState(), + TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality()); mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN; mPreciseCallState[i] = createPreciseCallState(); mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; @@ -799,7 +799,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallPreciseDisconnectCause = new int[numPhones]; mCallQuality = new CallQuality[numPhones]; mCallNetworkType = new int[numPhones]; - mCallStateLists = new ArrayList<>(); + mCallAttributes = new CallAttributes[numPhones]; mPreciseDataConnectionStates = new ArrayList<>(); mCellInfo = new ArrayList<>(numPhones); mImsReasonInfo = new ArrayList<>(); @@ -837,7 +837,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallDisconnectCause[i] = DisconnectCause.NOT_VALID; mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID; mCallQuality[i] = createCallQuality(); - mCallStateLists.add(i, new ArrayList<>()); + mCallAttributes[i] = new CallAttributes(createPreciseCallState(), + TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality()); mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN; mPreciseCallState[i] = createPreciseCallState(); mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; @@ -1335,7 +1336,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) { try { - r.callback.onCallStatesChanged(mCallStateLists.get(r.phoneId)); + r.callback.onCallAttributesChanged(mCallAttributes[r.phoneId]); } catch (RemoteException ex) { remove(r.binder); } @@ -2170,30 +2171,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - /** - * Send a notification to registrants that the precise call state has changed. - * - * @param phoneId the phoneId carrying the data connection - * @param subId the subscriptionId for the data connection - * @param callStates Array of PreciseCallState of foreground, background & ringing calls. - * @param imsCallIds Array of IMS call session ID{@link ImsCallSession#getCallId()} for - * ringing, foreground & background calls. - * @param imsServiceTypes Array of IMS call service type for ringing, foreground & - * background calls. - * @param imsCallTypes Array of IMS call type for ringing, foreground & background calls. - */ - public void notifyPreciseCallState(int phoneId, int subId, - @Annotation.PreciseCallStates int[] callStates, String[] imsCallIds, - @Annotation.ImsCallServiceType int[] imsServiceTypes, - @Annotation.ImsCallType int[] imsCallTypes) { + public void notifyPreciseCallState(int phoneId, int subId, int ringingCallState, + int foregroundCallState, int backgroundCallState) { if (!checkNotifyPermission("notifyPreciseCallState()")) { return; } - - int ringingCallState = callStates[CallState.CALL_CLASSIFICATION_RINGING]; - int foregroundCallState = callStates[CallState.CALL_CLASSIFICATION_FOREGROUND]; - int backgroundCallState = callStates[CallState.CALL_CLASSIFICATION_BACKGROUND]; - synchronized (mRecords) { if (validatePhoneId(phoneId)) { mRingingCallState[phoneId] = ringingCallState; @@ -2204,11 +2186,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { backgroundCallState, DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID); - boolean notifyCallState = true; + boolean notifyCallAttributes = true; if (mCallQuality == null) { log("notifyPreciseCallState: mCallQuality is null, " + "skipping call attributes"); - notifyCallState = false; + notifyCallAttributes = false; } else { // If the precise call state is no longer active, reset the call network type // and call quality. @@ -2217,54 +2199,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN; mCallQuality[phoneId] = createCallQuality(); } - mCallStateLists.get(phoneId).clear(); - if (foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_IDLE) { - CallQuality callQuality = mCallQuality[phoneId]; - mCallStateLists.get(phoneId).add( - new CallState.Builder( - callStates[CallState.CALL_CLASSIFICATION_FOREGROUND]) - .setNetworkType(mCallNetworkType[phoneId]) - .setCallQuality(callQuality) - .setCallClassification( - CallState.CALL_CLASSIFICATION_FOREGROUND) - .setImsCallSessionId(imsCallIds[ - CallState.CALL_CLASSIFICATION_FOREGROUND]) - .setImsCallServiceType(imsServiceTypes[ - CallState.CALL_CLASSIFICATION_FOREGROUND]) - .setImsCallType(imsCallTypes[ - CallState.CALL_CLASSIFICATION_FOREGROUND]).build()); - - } - if (backgroundCallState != PreciseCallState.PRECISE_CALL_STATE_IDLE) { - mCallStateLists.get(phoneId).add( - new CallState.Builder( - callStates[CallState.CALL_CLASSIFICATION_BACKGROUND]) - .setNetworkType(mCallNetworkType[phoneId]) - .setCallQuality(createCallQuality()) - .setCallClassification( - CallState.CALL_CLASSIFICATION_BACKGROUND) - .setImsCallSessionId(imsCallIds[ - CallState.CALL_CLASSIFICATION_BACKGROUND]) - .setImsCallServiceType(imsServiceTypes[ - CallState.CALL_CLASSIFICATION_BACKGROUND]) - .setImsCallType(imsCallTypes[ - CallState.CALL_CLASSIFICATION_BACKGROUND]).build()); - } - if (ringingCallState != PreciseCallState.PRECISE_CALL_STATE_IDLE) { - mCallStateLists.get(phoneId).add( - new CallState.Builder( - callStates[CallState.CALL_CLASSIFICATION_RINGING]) - .setNetworkType(mCallNetworkType[phoneId]) - .setCallQuality(createCallQuality()) - .setCallClassification( - CallState.CALL_CLASSIFICATION_RINGING) - .setImsCallSessionId(imsCallIds[ - CallState.CALL_CLASSIFICATION_RINGING]) - .setImsCallServiceType(imsServiceTypes[ - CallState.CALL_CLASSIFICATION_RINGING]) - .setImsCallType(imsCallTypes[ - CallState.CALL_CLASSIFICATION_RINGING]).build()); - } + mCallAttributes[phoneId] = new CallAttributes(mPreciseCallState[phoneId], + mCallNetworkType[phoneId], mCallQuality[phoneId]); } for (Record r : mRecords) { @@ -2277,11 +2213,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (notifyCallState && r.matchTelephonyCallbackEvent( + if (notifyCallAttributes && r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r, subId, phoneId)) { try { - r.callback.onCallStatesChanged(mCallStateLists.get(phoneId)); + r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2579,29 +2515,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // merge CallQuality with PreciseCallState and network type mCallQuality[phoneId] = callQuality; mCallNetworkType[phoneId] = callNetworkType; - if (mCallStateLists.get(phoneId).size() > 0 - && mCallStateLists.get(phoneId).get(0).getCallState() - == PreciseCallState.PRECISE_CALL_STATE_ACTIVE) { - CallState prev = mCallStateLists.get(phoneId).remove(0); - mCallStateLists.get(phoneId).add( - 0, new CallState.Builder(prev.getCallState()) - .setNetworkType(callNetworkType) - .setCallQuality(callQuality) - .setCallClassification(prev.getCallClassification()) - .setImsCallSessionId(prev.getImsCallSessionId()) - .setImsCallServiceType(prev.getImsCallServiceType()) - .setImsCallType(prev.getImsCallType()).build()); - } else { - log("There is no active call to report CallQaulity"); - return; - } + mCallAttributes[phoneId] = new CallAttributes(mPreciseCallState[phoneId], + callNetworkType, callQuality); for (Record r : mRecords) { if (r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r, subId, phoneId)) { try { - r.callback.onCallStatesChanged(mCallStateLists.get(phoneId)); + r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -3069,6 +2991,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mSrvccState=" + mSrvccState[i]); pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause[i]); pw.println("mCallQuality=" + mCallQuality[i]); + pw.println("mCallAttributes=" + mCallAttributes[i]); pw.println("mCallNetworkType=" + mCallNetworkType[i]); pw.println("mPreciseDataConnectionStates=" + mPreciseDataConnectionStates.get(i)); pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]); diff --git a/services/core/java/com/android/server/app/TEST_MAPPING b/services/core/java/com/android/server/app/TEST_MAPPING index 0ba4d8c35523..feb2b4fc6850 100644 --- a/services/core/java/com/android/server/app/TEST_MAPPING +++ b/services/core/java/com/android/server/app/TEST_MAPPING @@ -18,6 +18,23 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "name": "FrameworksCoreGameManagerTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.app" + } + ], + "file_patterns": [ + "(/|^)GameManagerService.java", "(/|^)GameManagerSettings.java" + ] } ] }
\ No newline at end of file diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 1c57151ed7bd..229393def27f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -421,13 +421,6 @@ public class FingerprintService extends SystemService { return -1; } - if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { - // If this happens, something in KeyguardUpdateMonitor is wrong. This should only - // ever be invoked when the user is encrypted or lockdown. - Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown"); - return -1; - } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index b882c47b71d0..e16ca0bbe1e1 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -551,6 +551,15 @@ public class CameraServiceProxy extends SystemService lensFacing, ignoreResizableAndSdkCheck); } + /** + * Placeholder method to fetch the system state for autoframing. + * TODO: b/260617354 + */ + @Override + public int getAutoframingOverride(String packageName) { + return CaptureRequest.CONTROL_AUTOFRAMING_OFF; + } + @Override public void pingForUserUpdate() { if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 2669d217120f..8d247f6a89e3 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -140,9 +140,7 @@ import com.android.server.location.provider.StationaryThrottlingLocationProvider import com.android.server.location.provider.proxy.ProxyLocationProvider; import com.android.server.location.settings.LocationSettings; import com.android.server.location.settings.LocationUserSettings; -import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.LegacyPermissionManagerInternal; -import com.android.server.utils.Slogf; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -310,10 +308,6 @@ public class LocationManagerService extends ILocationManager.Stub implements permissionManagerInternal.setLocationExtraPackagesProvider( userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationExtraPackageNames)); - - // TODO(b/241604546): properly handle this callback - LocalServices.getService(UserManagerInternal.class).addUserVisibilityListener( - (u, v) -> Slogf.i(TAG, "onUserVisibilityChanged(): %d -> %b", u, v)); } @Nullable @@ -1702,7 +1696,7 @@ public class LocationManagerService extends ILocationManager.Stub implements private final Context mContext; - private final UserInfoHelper mUserInfoHelper; + private final SystemUserInfoHelper mUserInfoHelper; private final LocationSettings mLocationSettings; private final AlarmHelper mAlarmHelper; private final SystemAppOpsHelper mAppOpsHelper; @@ -1725,7 +1719,7 @@ public class LocationManagerService extends ILocationManager.Stub implements @GuardedBy("this") private boolean mSystemReady; - SystemInjector(Context context, UserInfoHelper userInfoHelper) { + SystemInjector(Context context, SystemUserInfoHelper userInfoHelper) { mContext = context; mUserInfoHelper = userInfoHelper; @@ -1745,6 +1739,7 @@ public class LocationManagerService extends ILocationManager.Stub implements } synchronized void onSystemReady() { + mUserInfoHelper.onSystemReady(); mAppOpsHelper.onSystemReady(); mLocationPermissionsHelper.onSystemReady(); mSettingsHelper.onSystemReady(); diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java index 45436e753191..cb952edd0b71 100644 --- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java @@ -110,6 +110,11 @@ public class LocationEventLog extends LocalEventLog<Object> { addLog(new UserSwitchedEvent(userIdFrom, userIdTo)); } + /** Logs a user visibility changed event. */ + public void logUserVisibilityChanged(int userId, boolean visible) { + addLog(new UserVisibilityChangedEvent(userId, visible)); + } + /** Logs a location enabled/disabled event. */ public void logLocationEnabled(int userId, boolean enabled) { addLog(new LocationEnabledEvent(userId, enabled)); @@ -475,6 +480,22 @@ public class LocationEventLog extends LocalEventLog<Object> { } } + private static final class UserVisibilityChangedEvent { + + private final int mUserId; + private final boolean mVisible; + + UserVisibilityChangedEvent(int userId, boolean visible) { + mUserId = userId; + mVisible = visible; + } + + @Override + public String toString() { + return "[u" + mUserId + "] " + (mVisible ? "visible" : "invisible"); + } + } + private static final class LocationEnabledEvent { private final int mUserId; diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 0f5e3d46fd22..d3ceddd56827 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -387,7 +387,7 @@ public class GeofenceManager extends if (!mSettingsHelper.isLocationEnabled(identity.getUserId())) { return false; } - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { + if (!mUserInfoHelper.isVisibleUserId(identity.getUserId())) { return false; } if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), @@ -534,7 +534,10 @@ public class GeofenceManager extends } void onUserChanged(int userId, int change) { - if (change == UserListener.CURRENT_USER_CHANGED) { + // current user changes affect whether system server location requests are allowed to access + // location, and visibility changes affect whether any given user may access location. + if (change == UserListener.CURRENT_USER_CHANGED + || change == UserListener.USER_VISIBILITY_CHANGED) { updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } } diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index 349b94bc137b..567d8ac5e5df 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -317,7 +317,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter identity.getUserId())) { return false; } - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { + if (!mUserInfoHelper.isVisibleUserId(identity.getUserId())) { return false; } if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), @@ -394,7 +394,10 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter } private void onUserChanged(int userId, int change) { - if (change == UserListener.CURRENT_USER_CHANGED) { + // current user changes affect whether system server location requests are allowed to access + // location, and visibility changes affect whether any given user may access location. + if (change == UserListener.CURRENT_USER_CHANGED + || change == UserListener.USER_VISIBILITY_CHANGED) { updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } } diff --git a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java index ed1e65457b24..40dd979c9692 100644 --- a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java @@ -33,9 +33,11 @@ import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import java.io.FileDescriptor; import java.util.Arrays; +import java.util.Objects; /** * Provides accessors and listeners for all user info. @@ -50,11 +52,21 @@ public class SystemUserInfoHelper extends UserInfoHelper { @Nullable private IActivityManager mActivityManager; @GuardedBy("this") @Nullable private UserManager mUserManager; + @GuardedBy("this") + @Nullable private UserManagerInternal mUserManagerInternal; public SystemUserInfoHelper(Context context) { mContext = context; } + /** The function should be called when PHASE_SYSTEM_SERVICES_READY. */ + public synchronized void onSystemReady() { + mUserManagerInternal = + Objects.requireNonNull(LocalServices.getService(UserManagerInternal.class)); + mUserManagerInternal.addUserVisibilityListener( + (userId, visible) -> dispatchOnVisibleUserChanged(userId, visible)); + } + @Nullable protected final ActivityManagerInternal getActivityManagerInternal() { synchronized (this) { @@ -136,6 +148,24 @@ public class SystemUserInfoHelper extends UserInfoHelper { } @Override + public boolean isVisibleUserId(@UserIdInt int userId) { + synchronized (this) { + // if you're hitting this precondition then you are invoking this before the system is + // ready + Preconditions.checkState(mUserManagerInternal != null); + } + + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (this) { + return mUserManagerInternal.isUserVisible(userId); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override protected int[] getProfileIds(@UserIdInt int userId) { UserManager userManager = getUserManager(); diff --git a/services/core/java/com/android/server/location/injector/UserInfoHelper.java b/services/core/java/com/android/server/location/injector/UserInfoHelper.java index c835370a6f86..2b9db1c8a557 100644 --- a/services/core/java/com/android/server/location/injector/UserInfoHelper.java +++ b/services/core/java/com/android/server/location/injector/UserInfoHelper.java @@ -22,6 +22,7 @@ import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG; import static com.android.server.location.injector.UserInfoHelper.UserListener.CURRENT_USER_CHANGED; import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_STARTED; import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_STOPPED; +import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_VISIBILITY_CHANGED; import android.annotation.IntDef; import android.annotation.UserIdInt; @@ -47,8 +48,9 @@ public abstract class UserInfoHelper { int CURRENT_USER_CHANGED = 1; int USER_STARTED = 2; int USER_STOPPED = 3; + int USER_VISIBILITY_CHANGED = 4; - @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED}) + @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED, USER_VISIBILITY_CHANGED}) @Retention(RetentionPolicy.SOURCE) @interface UserChange {} @@ -121,6 +123,18 @@ public abstract class UserInfoHelper { } } + protected final void dispatchOnVisibleUserChanged(@UserIdInt int userId, boolean visible) { + if (D) { + Log.d(TAG, "visibility of u" + userId + " changed to " + + (visible ? "visible" : "invisible")); + } + EVENT_LOG.logUserVisibilityChanged(userId, visible); + + for (UserListener listener : mListeners) { + listener.onUserChanged(userId, USER_VISIBILITY_CHANGED); + } + } + /** * Returns an array of running user ids. This will include all running users, and will also * include any profiles of the running users. The caller must never mutate the returned @@ -129,8 +143,8 @@ public abstract class UserInfoHelper { public abstract int[] getRunningUserIds(); /** - * Returns true if the given user id is either the current user or a profile of the current - * user. + * Returns {@code true} if the given user id is either the current user or a profile of the + * current user. */ public abstract boolean isCurrentUserId(@UserIdInt int userId); @@ -140,6 +154,13 @@ public abstract class UserInfoHelper { */ public abstract @UserIdInt int getCurrentUserId(); + /** + * Returns {@code true} if the user is visible. + * + * <p>The visibility of a user is defined by {@link android.os.UserManager#isUserVisible()}. + */ + public abstract boolean isVisibleUserId(@UserIdInt int userId); + protected abstract int[] getProfileIds(@UserIdInt int userId); /** diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 338a99567b67..7063cb80d514 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -661,6 +661,8 @@ public class LocationProviderManager extends if (!GPS_PROVIDER.equals(mName)) { Log.e(TAG, "adas gnss bypass request received in non-gps provider"); adasGnssBypass = false; + } else if (!mUserHelper.isCurrentUserId(getIdentity().getUserId())) { + adasGnssBypass = false; } else if (!mLocationSettings.getUserSettings( getIdentity().getUserId()).isAdasGnssLocationEnabled()) { adasGnssBypass = false; @@ -1712,6 +1714,8 @@ public class LocationProviderManager extends if (!GPS_PROVIDER.equals(mName)) { Log.e(TAG, "adas gnss bypass request received in non-gps provider"); adasGnssBypass = false; + } else if (!mUserHelper.isCurrentUserId(identity.getUserId())) { + adasGnssBypass = false; } else if (!mLocationSettings.getUserSettings( identity.getUserId()).isAdasGnssLocationEnabled()) { adasGnssBypass = false; @@ -2193,7 +2197,7 @@ public class LocationProviderManager extends if (!isEnabled(identity.getUserId())) { return false; } - if (!mUserHelper.isCurrentUserId(identity.getUserId())) { + if (!mUserHelper.isVisibleUserId(identity.getUserId())) { return false; } } @@ -2322,6 +2326,10 @@ public class LocationProviderManager extends switch (change) { case UserListener.CURRENT_USER_CHANGED: + // current user changes affect whether system server location requests are + // allowed to access location, and visibility changes affect whether any given + // user may access location. + case UserListener.USER_VISIBILITY_CHANGED: updateRegistrations( registration -> registration.getIdentity().getUserId() == userId); break; diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java index a94a4e2a70be..ced547c35899 100644 --- a/services/core/java/com/android/server/pm/InstallArgs.java +++ b/services/core/java/com/android/server/pm/InstallArgs.java @@ -59,6 +59,7 @@ final class InstallArgs { final boolean mForceQueryableOverride; final int mDataLoaderType; final int mPackageSource; + final boolean mKeepApplicationEnabledSetting; // The list of instruction sets supported by this app. This is currently // only used during the rmdex() phase to clean up resources. We can get rid of this @@ -72,7 +73,8 @@ final class InstallArgs { List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, String traceMethod, int traceCookie, SigningDetails signingDetails, int installReason, int installScenario, - boolean forceQueryableOverride, int dataLoaderType, int packageSource) { + boolean forceQueryableOverride, int dataLoaderType, int packageSource, + boolean keepApplicationEnabledSetting) { mOriginInfo = originInfo; mMoveInfo = moveInfo; mInstallFlags = installFlags; @@ -93,6 +95,7 @@ final class InstallArgs { mForceQueryableOverride = forceQueryableOverride; mDataLoaderType = dataLoaderType; mPackageSource = packageSource; + mKeepApplicationEnabledSetting = keepApplicationEnabledSetting; } /** @@ -104,7 +107,7 @@ final class InstallArgs { null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0, SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT, false, DataLoaderType.NONE, - PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED); + PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, false); mCodeFile = (codePath != null) ? new File(codePath) : null; } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index b02d1a8c0353..78e419078bde 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; +import static android.content.pm.PackageManager.INSTALL_FAILED_DEPRECATED_SDK_VERSION; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP; @@ -136,6 +137,7 @@ import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.provider.DeviceConfig; import android.stats.storage.StorageEnums; import android.system.ErrnoException; import android.system.Os; @@ -1017,6 +1019,28 @@ final class InstallPackageHelper { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + // If the minimum installable SDK version enforcement is enabled, block the install + // of apps using a lower target SDK version than required. This helps improve security + // and privacy as malware can target older SDK versions to avoid enforcement of new API + // behavior. + if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + "MinInstallableTargetSdk__install_block_enabled", + false)) { + int minInstallableTargetSdk = + DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + "MinInstallableTargetSdk__min_installable_target_sdk", + 0); + if (parsedPackage.getTargetSdkVersion() < minInstallableTargetSdk) { + Slog.w(TAG, "App " + parsedPackage.getPackageName() + + " targets deprecated sdk version"); + throw new PrepareFailure(INSTALL_FAILED_DEPRECATED_SDK_VERSION, + "App package must target at least version " + + minInstallableTargetSdk); + } + } else { + Slog.i(TAG, "Minimum installable target sdk enforcement not enabled"); + } + // Instant apps have several additional install-time checks. if (instantApp) { if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) { @@ -2020,7 +2044,8 @@ final class InstallPackageHelper { Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName); } // Enable system package for requested users - if (installedForUsers != null) { + if (installedForUsers != null + && !installRequest.isKeepApplicationEnabledSetting()) { for (int origUserId : installedForUsers) { if (userId == UserHandle.USER_ALL || userId == origUserId) { ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, @@ -2070,16 +2095,22 @@ final class InstallPackageHelper { if (userId != UserHandle.USER_ALL) { // It's implied that when a user requests installation, they want the app to - // be installed and enabled. + // be installed and enabled. The caller, however, can explicitly specify to + // keep the existing enabled state. ps.setInstalled(true, userId); - ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName); + if (!installRequest.isKeepApplicationEnabledSetting()) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, + installerPackageName); + } } else if (allUsers != null) { // The caller explicitly specified INSTALL_ALL_USERS flag. // Thus, updating the settings to install the app for all users. for (int currentUserId : allUsers) { ps.setInstalled(true, currentUserId); - ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, - installerPackageName); + if (!installRequest.isKeepApplicationEnabledSetting()) { + ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId, + installerPackageName); + } } } diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 71571dc4f306..5974a9cbab67 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -128,7 +128,8 @@ final class InstallRequest { params.mAutoRevokePermissionsMode, params.mTraceMethod, params.mTraceCookie, params.mSigningDetails, params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride, - params.mDataLoaderType, params.mPackageSource); + params.mDataLoaderType, params.mPackageSource, + params.mKeepApplicationEnabledSetting); mPackageMetrics = new PackageMetrics(this); mIsInstallInherit = params.mIsInherit; mSessionId = params.mSessionId; @@ -498,6 +499,10 @@ final class InstallRequest { return mScanResult.mChangedAbiCodePath; } + public boolean isKeepApplicationEnabledSetting() { + return mInstallArgs == null ? false : mInstallArgs.mKeepApplicationEnabledSetting; + } + public boolean isForceQueryableOverride() { return mInstallArgs != null && mInstallArgs.mForceQueryableOverride; } diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index 69ced1b39585..2b6398af348e 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -98,6 +98,7 @@ class InstallingSession { final boolean mIsInherit; final int mSessionId; final int mRequireUserAction; + final boolean mKeepApplicationEnabledSetting; // For move install InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer, @@ -130,6 +131,7 @@ class InstallingSession { mIsInherit = false; mSessionId = -1; mRequireUserAction = USER_ACTION_UNSPECIFIED; + mKeepApplicationEnabledSetting = false; } InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer, @@ -163,6 +165,7 @@ class InstallingSession { mIsInherit = sessionParams.mode == MODE_INHERIT_EXISTING; mSessionId = sessionId; mRequireUserAction = sessionParams.requireUserAction; + mKeepApplicationEnabledSetting = sessionParams.keepApplicationEnabledSetting; } @Override diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 2ee12bf97823..3983acfe9d2d 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -269,6 +269,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String ATTR_SIGNATURE = "signature"; private static final String ATTR_CHECKSUM_KIND = "checksumKind"; private static final String ATTR_CHECKSUM_VALUE = "checksumValue"; + private static final String ATTR_KEEP_APPLICATION_ENABLED_SETTING = + "keepApplicationEnabledSetting"; private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT; @@ -1098,6 +1100,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.requireUserAction = params.requireUserAction; info.installerUid = mInstallerUid; info.packageSource = params.packageSource; + info.keepApplicationEnabledSetting = params.keepApplicationEnabledSetting; } return info; } @@ -4310,6 +4313,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mPreapprovalRequested.set(true); } + @Override + public boolean isKeepApplicationEnabledSetting() { + return params.keepApplicationEnabledSetting; + } + void setSessionReady() { synchronized (mLock) { // Do not allow destroyed/failed session to change state @@ -4691,6 +4699,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason); + writeBooleanAttribute(out, ATTR_KEEP_APPLICATION_ENABLED_SETTING, + params.keepApplicationEnabledSetting); final boolean isDataLoader = params.dataLoaderParams != null; writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader); @@ -4852,6 +4862,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); params.installReason = in.getAttributeInt(null, ATTR_INSTALL_REASON); params.packageSource = in.getAttributeInt(null, ATTR_PACKAGE_SOURCE); + params.keepApplicationEnabledSetting = in.getAttributeBoolean(null, + ATTR_KEEP_APPLICATION_ENABLED_SETTING, false); if (in.getAttributeBoolean(null, ATTR_IS_DATALOADER, false)) { params.dataLoaderParams = new DataLoaderParams( diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index cc1306dbc2b3..e1efc612224c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -3234,6 +3234,9 @@ class PackageManagerShellCommand extends ShellCommand { case "--skip-verification": sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; break; + case "--skip-enable": + sessionParams.setKeepApplicationEnabledSetting(); + break; default: throw new IllegalArgumentException("Unknown option " + opt); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 88e12fa9a604..23a6b67269e8 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3783,10 +3783,10 @@ public class UserManagerService extends IUserManager.Stub { private UserInfo getEarliestCreatedFullUser() { final List<UserInfo> users = getUsersInternal(true, true, true); UserInfo earliestUser = users.get(0); - long earliestCreationTime = earliestUser.creationTime; + long earliestCreationTime = Long.MAX_VALUE; for (int i = 0; i < users.size(); i++) { final UserInfo info = users.get(i); - if (info.isFull() && info.isAdmin() && info.creationTime > 0 + if (info.isFull() && info.isAdmin() && info.creationTime >= 0 && info.creationTime < earliestCreationTime) { earliestCreationTime = info.creationTime; earliestUser = info; diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index f8c1c9269ff3..10cd5d1a0669 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -91,7 +91,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub deviceActivityMonitor.addListener(new DeviceActivityMonitor.Listener() { @Override public void onFlightComplete() { - timeZoneDetectorStrategy.enableTelephonyTimeZoneFallback(); + timeZoneDetectorStrategy.enableTelephonyTimeZoneFallback("onFlightComplete()"); } }); @@ -402,9 +402,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub * Sends a signal to enable telephony fallback. Provided for command-line access for use * during tests. This is not exposed as a binder API. */ - void enableTelephonyFallback() { + void enableTelephonyFallback(@NonNull String reason) { enforceManageTimeZoneDetectorPermission(); - mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback(); + mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback(reason); } /** diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java index 69274dba7825..ab68e834d337 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java @@ -189,7 +189,7 @@ class TimeZoneDetectorShellCommand extends ShellCommand { } private int runEnableTelephonyFallback() { - mInterface.enableTelephonyFallback(); + mInterface.enableTelephonyFallback("Command line"); return 0; } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 5768a6bfa0dc..37e67c921634 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -35,13 +35,13 @@ import android.util.IndentingPrintWriter; * <p>Devices can have zero, one or two automatic time zone detection algorithms available at any * point in time. * - * <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm + * <p>The two automatic detection algorithms supported are "telephony" and "location". Algorithm * availability and use depends on several factors: * <ul> * <li>Telephony is only available on devices with a telephony stack. - * <li>Geolocation is also optional and configured at image creation time. When enabled on a - * device, its availability depends on the current user's settings, so switching between users can - * change the automatic algorithm used by the device.</li> + * <li>Location is also optional and configured at image creation time. When enabled on a device, + * its availability depends on the current user's settings, so switching between users can change + * the automatic detection algorithm used by the device.</li> * </ul> * * <p>If there are no automatic time zone detections algorithms available then the user can usually @@ -56,14 +56,14 @@ import android.util.IndentingPrintWriter; * slotIndexes must have an empty suggestion submitted in order to "withdraw" their previous * suggestion otherwise it will remain in use. * - * <p>Geolocation detection is dependent on the current user and their settings. The device retains - * at most one geolocation suggestion. Generally, use of a device's location is dependent on the - * user's "location toggle", but even when that is enabled the user may choose to enable / disable - * the use of geolocation for device time zone detection. If the current user changes to one that - * does not have geolocation detection enabled, or the user turns off geolocation detection, then - * the strategy discards the latest geolocation suggestion. Devices that lose a location fix must - * have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it - * will remain in use. + * <p>Location-based detection is dependent on the current user and their settings. The device + * retains at most one geolocation suggestion. Generally, use of a device's location is dependent on + * the user's "location toggle", but even when that is enabled the user may choose to enable / + * disable the use of location for device time zone detection. If the current user changes to one + * that does not have location-based detection enabled, or the user turns off the location-based + * detection, then the strategy will be sent an event that clears the latest suggestion. Devices + * that lose their location fix must have an empty suggestion submitted in order to "withdraw" their + * previous suggestion otherwise it will remain in use. * * <p>The strategy uses only one algorithm at a time and does not attempt consensus even when * more than one is available on a device. This "use only one" behavior is deliberate as different @@ -72,25 +72,27 @@ import android.util.IndentingPrintWriter; * users enter areas without the necessary signals. Ultimately, with no perfect algorithm available, * the user is left to choose which algorithm works best for their circumstances. * - * <p>When geolocation detection is supported and enabled, in certain circumstances, such as during - * international travel, it makes sense to prioritize speed of detection via telephony (when - * available) Vs waiting for the geolocation algorithm to reach certainty. Geolocation detection can - * sometimes be slow to get a location fix and can require network connectivity (which cannot be - * assumed when users are travelling) for server-assisted location detection or time zone lookup. - * Therefore, as a restricted form of prioritization between geolocation and telephony algorithms, - * the strategy provides "telephony fallback" behavior, which can be set to "supported" via device - * config. Fallback mode is toggled on at runtime via {@link #enableTelephonyTimeZoneFallback()} in - * response to signals outside of the scope of this class. Telephony fallback allows the use of - * telephony suggestions to help with faster detection but only until geolocation detection - * provides a concrete, "certain" suggestion. After geolocation has made the first certain - * suggestion, telephony fallback is disabled until the next call to {@link - * #enableTelephonyTimeZoneFallback()}. + * <p>When the location detection algorithm is supported and enabled, in certain circumstances, such + * as during international travel, it makes sense to prioritize speed of detection via telephony + * (when available) Vs waiting for the location-based detection algorithm to reach certainty. + * Location-based detection can sometimes be slow to get a location fix and can require network + * connectivity (which cannot be assumed when users are travelling) for server-assisted location + * detection or time zone lookup. Therefore, as a restricted form of prioritization between location + * and telephony algorithms, the strategy provides "telephony fallback mode" behavior, which can be + * set to "supported" via device config. Fallback mode is entered at runtime in response to signals + * from outside of the strategy, e.g. from a call to {@link + * #enableTelephonyTimeZoneFallback(String)}, or from information in the latest {@link + * LocationAlgorithmEvent}. For telephony fallback mode to actually use a telephony suggestion, the + * location algorithm <em>must</em> report it is uncertain. Telephony fallback allows the use of + * telephony suggestions to help with faster detection but only until the location algorithm + * provides a concrete, "certain" suggestion. After the location algorithm has made a certain + * suggestion, telephony fallback mode is disabled. * * <p>Threading: * * <p>Implementations of this class must be thread-safe as calls calls like {@link * #generateMetricsState()} and {@link #dump(IndentingPrintWriter, String[])} may be called on - * differents thread concurrently with other operations. + * different threads concurrently with other operations. * * @hide */ @@ -181,11 +183,11 @@ public interface TimeZoneDetectorStrategy extends Dumpable { void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion); /** - * Tells the strategy that it can fall back to telephony detection while geolocation detection - * remains uncertain. {@link #handleLocationAlgorithmEvent(LocationAlgorithmEvent)} can - * disable it again. See {@link TimeZoneDetectorStrategy} for details. + * Tells the strategy that it can fall back to telephony detection while the location detection + * algorithm remains uncertain. {@link #handleLocationAlgorithmEvent(LocationAlgorithmEvent)} + * can disable it again. See {@link TimeZoneDetectorStrategy} for details. */ - void enableTelephonyTimeZoneFallback(); + void enableTelephonyTimeZoneFallback(@NonNull String reason); /** Generates a state snapshot for metrics. */ @NonNull @@ -194,6 +196,6 @@ public interface TimeZoneDetectorStrategy extends Dumpable { /** Returns {@code true} if the device supports telephony time zone detection. */ boolean isTelephonyTimeZoneDetectionSupported(); - /** Returns {@code true} if the device supports geolocation time zone detection. */ + /** Returns {@code true} if the device supports location-based time zone detection. */ boolean isGeoTimeZoneDetectionSupported(); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index fa811efcfec0..e0e3565e1b0b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -63,12 +63,8 @@ import java.util.Objects; public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy { /** - * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device configuration / settings - * / system properties. It can be faked for testing. - * - * <p>Note: Because the settings / system properties-derived values can currently be modified - * independently and from different threads (and processes!), their use is prone to race - * conditions. + * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that + * available from {@link #mServiceConfigAccessor}. It can be faked for testing. */ @VisibleForTesting public interface Environment { @@ -234,7 +230,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * allows). * * <p>This field is only actually used when telephony time zone fallback is supported, but the - * value is maintained even when it isn't supported as it can be turned on at any time via + * value is maintained even when it isn't supported as support can be turned on at any time via * server flags. The elapsed realtime when the mode last changed is used to help ordering * between fallback mode switches and suggestions. * @@ -421,10 +417,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat notifyStateChangeListenersAsynchronously(); } - // Update the mTelephonyTimeZoneFallbackEnabled state if needed: a certain suggestion - // will usually disable telephony fallback mode if it is currently enabled. - // TODO(b/236624675)Some provider status codes can be used to enable telephony fallback. - disableTelephonyFallbackIfNeeded(); + // Manage telephony fallback state. + if (event.getAlgorithmStatus().couldEnableTelephonyFallback()) { + // An event may trigger entry into telephony fallback mode if the status + // indicates the location algorithm cannot work and is likely to stay not working. + enableTelephonyTimeZoneFallback("handleLocationAlgorithmEvent(), event=" + event); + } else { + // A certain suggestion will exit telephony fallback mode. + disableTelephonyFallbackIfNeeded(); + } // Now perform auto time zone detection. The new event may be used to modify the time zone // setting. @@ -497,38 +498,41 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } @Override - public synchronized void enableTelephonyTimeZoneFallback() { - // Only do any work if fallback is currently not enabled. + public synchronized void enableTelephonyTimeZoneFallback(@NonNull String reason) { + // Only do any work to enter fallback mode if fallback is currently not already enabled. if (!mTelephonyTimeZoneFallbackEnabled.getValue()) { ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal; final boolean fallbackEnabled = true; mTelephonyTimeZoneFallbackEnabled = new TimestampedValue<>( mEnvironment.elapsedRealtimeMillis(), fallbackEnabled); - String logMsg = "enableTelephonyTimeZoneFallbackMode: " - + " currentUserConfig=" + currentUserConfig - + ", mTelephonyTimeZoneFallbackEnabled=" - + mTelephonyTimeZoneFallbackEnabled; + String logMsg = "enableTelephonyTimeZoneFallback: " + + " reason=" + reason + + ", currentUserConfig=" + currentUserConfig + + ", mTelephonyTimeZoneFallbackEnabled=" + mTelephonyTimeZoneFallbackEnabled; logTimeZoneDebugInfo(logMsg); // mTelephonyTimeZoneFallbackEnabled and mLatestLocationAlgorithmEvent interact. - // If the latest event contains a "certain" geolocation suggestion, then the telephony - // fallback value needs to be considered after changing it. + // If the latest location algorithm event contains a "certain" geolocation suggestion, + // then the telephony fallback mode needs to be (re)considered after changing it. + // // With the way that the mTelephonyTimeZoneFallbackEnabled time is currently chosen // above, and the fact that geolocation suggestions should never have a time in the - // future, the following call will be a no-op, and telephony fallback will remain - // enabled. This comment / call is left as a reminder that it is possible for there to - // be a current, "certain" geolocation suggestion when this signal arrives and it is - // intentional that fallback stays enabled in this case. The choice to do this - // is mostly for symmetry WRT the case where fallback is enabled and an old "certain" - // geolocation is received; that would also leave telephony fallback enabled. - // This choice means that telephony fallback will remain enabled until a new "certain" - // geolocation suggestion is received. If, instead, the next geolocation is "uncertain", - // then telephony fallback will occur. + // future, the following call will usually be a no-op, and telephony fallback mode will + // remain enabled. This comment / call is left as a reminder that it is possible in some + // cases for there to be a current, "certain" geolocation suggestion when an attempt is + // made to enable telephony fallback mode and it is intentional that fallback mode stays + // enabled in this case. The choice to do this is mostly for symmetry WRT the case where + // fallback is enabled and then an old "certain" geolocation suggestion is received; + // that would also leave telephony fallback mode enabled. + // + // This choice means that telephony fallback mode remains enabled if there is an + // existing "certain" suggestion until a new "certain" geolocation suggestion is + // received. If, instead, the next geolocation suggestion is "uncertain", then telephony + // fallback, i.e. the use of a telephony suggestion, will actually occur. disableTelephonyFallbackIfNeeded(); if (currentUserConfig.isTelephonyFallbackSupported()) { - String reason = "enableTelephonyTimeZoneFallbackMode"; doAutoTimeZoneDetection(currentUserConfig, reason); } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 3bb02382b57c..14d6d7bdfb7d 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; @@ -226,9 +227,8 @@ class BackNavigationController { mBackAnimationInProgress = true; // We don't have an application callback, let's find the destination of the back gesture - Task finalTask = currentTask; - prevActivity = currentTask.getActivity( - (r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity()); + // The search logic should align with ActivityClientController#finishActivity + prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); // TODO Dialog window does not need to attach on activity, check // window.mAttrs.type != TYPE_BASE_APPLICATION if ((window.getParent().getChildCount() > 1 @@ -244,12 +244,14 @@ class BackNavigationController { } else if (currentTask.returnsToHomeRootTask()) { // Our Task should bring back to home removedWindowContainer = currentTask; + prevTask = currentTask.getDisplayArea().getRootHomeTask(); backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer - prevTask = currentTask.mRootWindowContainer.getTaskBelow(currentTask); + prevTask = currentTask.mRootWindowContainer.getTask(Task::showToCurrentUser, + currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); removedWindowContainer = currentTask; // If it reaches the top activity, we will check the below task from parent. // If it's null or multi-window, fallback the type to TYPE_CALLBACK. @@ -423,6 +425,11 @@ class BackNavigationController { void reset(@NonNull WindowContainer close, @NonNull WindowContainer open) { clearBackAnimateTarget(null); + if (close == null || open == null) { + Slog.e(TAG, "reset animation with null target close: " + + close + " open: " + open); + return; + } if (close.asActivityRecord() != null && open.asActivityRecord() != null && (close.asActivityRecord().getTask() == open.asActivityRecord().getTask())) { mSwitchType = ACTIVITY_SWITCH; diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 374da1c8e7e3..d3b9e10a436a 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -22,10 +22,11 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.PackageManager; +import android.credentials.ClearCredentialStateRequest; import android.credentials.CreateCredentialRequest; import android.credentials.GetCredentialOption; import android.credentials.GetCredentialRequest; -import android.credentials.IClearCredentialSessionCallback; +import android.credentials.IClearCredentialStateCallback; import android.credentials.ICreateCredentialCallback; import android.credentials.ICredentialManager; import android.credentials.IGetCredentialCallback; @@ -34,6 +35,7 @@ import android.os.CancellationSignal; import android.os.ICancellationSignal; import android.os.UserHandle; import android.provider.Settings; +import android.service.credentials.BeginCreateCredentialRequest; import android.service.credentials.GetCredentialsRequest; import android.text.TextUtils; import android.util.Log; @@ -198,7 +200,7 @@ public final class CredentialManagerService extends // Iterate over all provider sessions and invoke the request providerSessions.forEach(providerCreateSession -> { providerCreateSession.getRemoteCredentialService().onCreateCredential( - (android.service.credentials.CreateCredentialRequest) + (BeginCreateCredentialRequest) providerCreateSession.getProviderRequest(), /*callback=*/providerCreateSession); }); @@ -206,8 +208,8 @@ public final class CredentialManagerService extends } @Override - public ICancellationSignal clearCredentialSession( - IClearCredentialSessionCallback callback, String callingPackage) { + public ICancellationSignal clearCredentialState(ClearCredentialStateRequest request, + IClearCredentialStateCallback callback, String callingPackage) { // TODO: implement. Log.i(TAG, "clearCredentialSession"); ICancellationSignal cancelTransport = CancellationSignal.createTransport(); diff --git a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java index 4cdc4570ba90..d0bc0744f8a5 100644 --- a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java +++ b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java @@ -22,7 +22,7 @@ import android.credentials.CreateCredentialResponse; import android.credentials.Credential; import android.credentials.ui.ProviderPendingIntentResponse; import android.service.credentials.CredentialProviderService; -import android.service.credentials.CredentialsDisplayContent; +import android.service.credentials.CredentialsResponseContent; /** * Helper class for setting up pending intent, and extracting objects from it. @@ -37,14 +37,15 @@ public class PendingIntentResultHandler { return pendingIntentResponse.getResultCode() == Activity.RESULT_OK; } - /** Extracts the {@link CredentialsDisplayContent} object added to the result data. */ - public static CredentialsDisplayContent extractCredentialsDisplayContent(Intent resultData) { + /** Extracts the {@link CredentialsResponseContent} object added to the result data. */ + public static CredentialsResponseContent extractResponseContent(Intent resultData) { if (resultData == null) { return null; } return resultData.getParcelableExtra( - CredentialProviderService.EXTRA_GET_CREDENTIALS_DISPLAY_CONTENT, - CredentialsDisplayContent.class); + CredentialProviderService + .EXTRA_GET_CREDENTIALS_CONTENT_RESULT, + CredentialsResponseContent.class); } /** Extracts the {@link CreateCredentialResponse} object added to the result data. */ @@ -53,7 +54,7 @@ public class PendingIntentResultHandler { return null; } return resultData.getParcelableExtra( - CredentialProviderService.EXTRA_CREATE_CREDENTIAL_RESPONSE, + CredentialProviderService.EXTRA_CREATE_CREDENTIAL_RESULT, CreateCredentialResponse.class); } @@ -63,7 +64,7 @@ public class PendingIntentResultHandler { return null; } return resultData.getParcelableExtra( - CredentialProviderService.EXTRA_GET_CREDENTIAL, + CredentialProviderService.EXTRA_CREDENTIAL_RESULT, Credential.class); } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 6bb8c60ec06f..332a75ea566b 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -26,11 +26,12 @@ import android.credentials.ui.CreateCredentialProviderData; import android.credentials.ui.Entry; import android.credentials.ui.ProviderPendingIntentResponse; import android.os.Bundle; +import android.service.credentials.BeginCreateCredentialRequest; +import android.service.credentials.BeginCreateCredentialResponse; import android.service.credentials.CreateCredentialRequest; -import android.service.credentials.CreateCredentialResponse; +import android.service.credentials.CreateEntry; import android.service.credentials.CredentialProviderInfo; import android.service.credentials.CredentialProviderService; -import android.service.credentials.SaveEntry; import android.util.Log; import android.util.Slog; @@ -44,14 +45,14 @@ import java.util.Map; * Will likely split this into remote response state and UI state. */ public final class ProviderCreateSession extends ProviderSession< - CreateCredentialRequest, CreateCredentialResponse> { + BeginCreateCredentialRequest, BeginCreateCredentialResponse> { private static final String TAG = "ProviderCreateSession"; // Key to be used as an entry key for a save entry private static final String SAVE_ENTRY_KEY = "save_entry_key"; @NonNull - private final Map<String, SaveEntry> mUiSaveEntries = new HashMap<>(); + private final Map<String, CreateEntry> mUiSaveEntries = new HashMap<>(); /** The complete request to be used in the second round. */ private final CreateCredentialRequest mCompleteRequest; @@ -62,13 +63,19 @@ public final class ProviderCreateSession extends ProviderSession< CredentialProviderInfo providerInfo, CreateRequestSession createRequestSession, RemoteCredentialService remoteCredentialService) { - CreateCredentialRequest providerRequest = + CreateCredentialRequest providerCreateRequest = createProviderRequest(providerInfo.getCapabilities(), createRequestSession.mClientRequest, createRequestSession.mClientCallingPackage); - if (providerRequest != null) { + if (providerCreateRequest != null) { + // TODO : Replace with proper splitting of request + BeginCreateCredentialRequest providerBeginCreateRequest = + new BeginCreateCredentialRequest( + providerCreateRequest.getCallingPackage(), + providerCreateRequest.getType(), + new Bundle()); return new ProviderCreateSession(context, providerInfo, createRequestSession, userId, - remoteCredentialService, providerRequest); + remoteCredentialService, providerBeginCreateRequest, providerCreateRequest); } Log.i(TAG, "Unable to create provider session"); return null; @@ -87,36 +94,28 @@ public final class ProviderCreateSession extends ProviderSession< return null; } - private static CreateCredentialRequest getFirstRoundRequest(CreateCredentialRequest request) { - // TODO: Replace with first round bundle from request when ready - return new CreateCredentialRequest( - request.getCallingPackage(), - request.getType(), - new Bundle()); - } - private ProviderCreateSession( @NonNull Context context, @NonNull CredentialProviderInfo info, @NonNull ProviderInternalCallback callbacks, @UserIdInt int userId, @NonNull RemoteCredentialService remoteCredentialService, - @NonNull CreateCredentialRequest request) { - super(context, info, getFirstRoundRequest(request), callbacks, userId, + @NonNull BeginCreateCredentialRequest beginCreateRequest, + @NonNull CreateCredentialRequest completeCreateRequest) { + super(context, info, beginCreateRequest, callbacks, userId, remoteCredentialService); - // TODO : Replace with proper splitting of request - mCompleteRequest = request; + mCompleteRequest = completeCreateRequest; setStatus(Status.PENDING); } /** Returns the save entry maintained in state by this provider session. */ - public SaveEntry getUiSaveEntry(String entryId) { + public CreateEntry getUiSaveEntry(String entryId) { return mUiSaveEntries.get(entryId); } @Override public void onProviderResponseSuccess( - @Nullable CreateCredentialResponse response) { + @Nullable BeginCreateCredentialResponse response) { Log.i(TAG, "in onProviderResponseSuccess"); onUpdateResponse(response); } @@ -138,7 +137,7 @@ public final class ProviderCreateSession extends ProviderSession< } } - private void onUpdateResponse(CreateCredentialResponse response) { + private void onUpdateResponse(BeginCreateCredentialResponse response) { Log.i(TAG, "updateResponse with save entries"); mProviderResponse = response; updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED); @@ -152,15 +151,15 @@ public final class ProviderCreateSession extends ProviderSession< Log.i(TAG, "In prepareUiData not in uiInvokingStatus"); return null; } - final CreateCredentialResponse response = getProviderResponse(); + final BeginCreateCredentialResponse response = getProviderResponse(); if (response == null) { Log.i(TAG, "In prepareUiData response null"); throw new IllegalStateException("Response must be in completion mode"); } - if (response.getSaveEntries() != null) { + if (response.getCreateEntries() != null) { Log.i(TAG, "In prepareUiData save entries not null"); return prepareUiProviderData( - prepareUiSaveEntries(response.getSaveEntries()), + prepareUiSaveEntries(response.getCreateEntries()), null, /*isDefaultProvider=*/false); } @@ -192,24 +191,25 @@ public final class ProviderCreateSession extends ProviderSession< } } - private List<Entry> prepareUiSaveEntries(@NonNull List<SaveEntry> saveEntries) { + private List<Entry> prepareUiSaveEntries(@NonNull List<CreateEntry> saveEntries) { Log.i(TAG, "in populateUiSaveEntries"); List<Entry> uiSaveEntries = new ArrayList<>(); // Populate the save entries - for (SaveEntry saveEntry : saveEntries) { + for (CreateEntry createEntry : saveEntries) { String entryId = generateEntryId(); - mUiSaveEntries.put(entryId, saveEntry); + mUiSaveEntries.put(entryId, createEntry); Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId); - uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, saveEntry.getSlice(), - saveEntry.getPendingIntent(), setUpFillInIntent(saveEntry.getPendingIntent()))); + uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, createEntry.getSlice(), + createEntry.getPendingIntent(), setUpFillInIntent( + createEntry.getPendingIntent()))); } return uiSaveEntries; } private Intent setUpFillInIntent(PendingIntent pendingIntent) { Intent intent = pendingIntent.getIntent(); - intent.putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST_PARAMS, + intent.putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST, mCompleteRequest); return intent; } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index d63cdebe0e1b..6cd011b7a686 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -29,7 +29,7 @@ import android.credentials.ui.ProviderPendingIntentResponse; import android.service.credentials.Action; import android.service.credentials.CredentialEntry; import android.service.credentials.CredentialProviderInfo; -import android.service.credentials.CredentialsDisplayContent; +import android.service.credentials.CredentialsResponseContent; import android.service.credentials.GetCredentialsRequest; import android.service.credentials.GetCredentialsResponse; import android.util.Log; @@ -211,20 +211,20 @@ public final class ProviderGetSession extends ProviderSession<GetCredentialsRequ prepareUiAuthenticationAction(mProviderResponse.getAuthenticationAction()), /*remoteEntry=*/null); } - if (mProviderResponse.getCredentialsDisplayContent() != null) { - Log.i(TAG, "In prepareUiData displayContent not null"); + if (mProviderResponse.getCredentialsResponseContent() != null) { + Log.i(TAG, "In prepareUiData credentialsResponseContent not null"); return prepareUiProviderData(prepareUiActionEntries( - mProviderResponse.getCredentialsDisplayContent().getActions()), - prepareUiCredentialEntries(mProviderResponse.getCredentialsDisplayContent() + mProviderResponse.getCredentialsResponseContent().getActions()), + prepareUiCredentialEntries(mProviderResponse.getCredentialsResponseContent() .getCredentialEntries()), /*authenticationAction=*/null, prepareUiRemoteEntry(mProviderResponse - .getCredentialsDisplayContent().getRemoteCredentialEntry())); + .getCredentialsResponseContent().getRemoteCredentialEntry())); } return null; } - private Entry prepareUiRemoteEntry(Action remoteCredentialEntry) { + private Entry prepareUiRemoteEntry(CredentialEntry remoteCredentialEntry) { if (remoteCredentialEntry == null) { return null; } @@ -316,11 +316,11 @@ public final class ProviderGetSession extends ProviderSession<GetCredentialsRequ @Nullable ProviderPendingIntentResponse providerPendingIntentResponse) { if (providerPendingIntentResponse != null) { if (PendingIntentResultHandler.isSuccessfulResponse(providerPendingIntentResponse)) { - CredentialsDisplayContent content = PendingIntentResultHandler - .extractCredentialsDisplayContent(providerPendingIntentResponse + CredentialsResponseContent content = PendingIntentResultHandler + .extractResponseContent(providerPendingIntentResponse .getResultData()); if (content != null) { - onUpdateResponse(GetCredentialsResponse.createWithDisplayContent(content)); + onUpdateResponse(GetCredentialsResponse.createWithResponseContent(content)); return; } } @@ -342,7 +342,7 @@ public final class ProviderGetSession extends ProviderSession<GetCredentialsRequ if (response.getAuthenticationAction() != null) { Log.i(TAG , "updateResponse with authentication entry"); updateStatusAndInvokeCallback(Status.REQUIRES_AUTHENTICATION); - } else if (response.getCredentialsDisplayContent() != null) { + } else if (response.getCredentialsResponseContent() != null) { Log.i(TAG , "updateResponse with credentialEntries"); // TODO validate response updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED); diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index 4a07f0a4e305..ac360bd4fc9e 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -23,7 +23,7 @@ import android.content.Context; import android.credentials.Credential; import android.credentials.ui.ProviderData; import android.credentials.ui.ProviderPendingIntentResponse; -import android.service.credentials.Action; +import android.service.credentials.CredentialEntry; import android.service.credentials.CredentialProviderException; import android.service.credentials.CredentialProviderInfo; import android.util.Pair; @@ -50,7 +50,7 @@ public abstract class ProviderSession<T, R> @Nullable protected Credential mFinalCredentialResponse; @NonNull protected final T mProviderRequest; @Nullable protected R mProviderResponse; - @Nullable protected Pair<String, Action> mUiRemoteEntry; + @Nullable protected Pair<String, CredentialEntry> mUiRemoteEntry; /** * Returns true if the given status reflects that the provider state is ready to be shown diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java index c2464b5d235e..e385bcb32201 100644 --- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java +++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java @@ -24,14 +24,14 @@ import android.content.Intent; import android.os.Handler; import android.os.ICancellationSignal; import android.os.RemoteException; -import android.service.credentials.CreateCredentialRequest; -import android.service.credentials.CreateCredentialResponse; +import android.service.credentials.BeginCreateCredentialRequest; +import android.service.credentials.BeginCreateCredentialResponse; import android.service.credentials.CredentialProviderException; import android.service.credentials.CredentialProviderException.CredentialProviderError; import android.service.credentials.CredentialProviderService; import android.service.credentials.GetCredentialsRequest; import android.service.credentials.GetCredentialsResponse; -import android.service.credentials.ICreateCredentialCallback; +import android.service.credentials.IBeginCreateCredentialCallback; import android.service.credentials.ICredentialProviderService; import android.service.credentials.IGetCredentialsCallback; import android.text.format.DateUtils; @@ -146,27 +146,27 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr handleExecutionResponse(result, error, cancellationSink, callback))); } - /** Main entry point to be called for executing a createCredential call on the remote + /** Main entry point to be called for executing a beginCreateCredential call on the remote * provider service. * @param request the request to be sent to the provider * @param callback the callback to be used to send back the provider response to the * {@link ProviderCreateSession} class that maintains provider state */ - public void onCreateCredential(@NonNull CreateCredentialRequest request, - ProviderCallbacks<CreateCredentialResponse> callback) { + public void onCreateCredential(@NonNull BeginCreateCredentialRequest request, + ProviderCallbacks<BeginCreateCredentialResponse> callback) { Log.i(TAG, "In onCreateCredential in RemoteCredentialService"); AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); - AtomicReference<CompletableFuture<CreateCredentialResponse>> futureRef = + AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef = new AtomicReference<>(); - CompletableFuture<CreateCredentialResponse> connectThenExecute = postAsync(service -> { - CompletableFuture<CreateCredentialResponse> createCredentialFuture = + CompletableFuture<BeginCreateCredentialResponse> connectThenExecute = postAsync(service -> { + CompletableFuture<BeginCreateCredentialResponse> createCredentialFuture = new CompletableFuture<>(); - ICancellationSignal cancellationSignal = service.onCreateCredential( - request, new ICreateCredentialCallback.Stub() { + ICancellationSignal cancellationSignal = service.onBeginCreateCredential( + request, new IBeginCreateCredentialCallback.Stub() { @Override - public void onSuccess(CreateCredentialResponse response) { - Log.i(TAG, "In onSuccess onCreateCredential " + public void onSuccess(BeginCreateCredentialResponse response) { + Log.i(TAG, "In onSuccess onBeginCreateCredential " + "in RemoteCredentialService"); createCredentialFuture.complete(response); } @@ -179,7 +179,7 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr createCredentialFuture.completeExceptionally( new CredentialProviderException(errorCode, errorMsg)); }}); - CompletableFuture<CreateCredentialResponse> future = futureRef.get(); + CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get(); if (future != null && future.isCancelled()) { dispatchCancellationSignal(cancellationSignal); } else { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java index ac23d4e427c4..014ef3d7ef63 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java @@ -34,14 +34,16 @@ public class FakeUserInfoHelper extends UserInfoHelper { public static final int DEFAULT_USERID = 0; private final IntArray mRunningUserIds; + private final IntArray mVisibleUserIds; private final SparseArray<IntArray> mProfiles; private int mCurrentUserId; public FakeUserInfoHelper() { mCurrentUserId = DEFAULT_USERID; - mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID}); + mRunningUserIds = IntArray.wrap(new int[] {DEFAULT_USERID}); mProfiles = new SparseArray<>(); + mVisibleUserIds = IntArray.wrap(new int[] {DEFAULT_USERID}); } public void startUser(int userId) { @@ -65,6 +67,7 @@ public class FakeUserInfoHelper extends UserInfoHelper { mRunningUserIds.remove(idx); } + setUserInvisibleInternal(userId); dispatchOnUserStopped(userId); } @@ -82,16 +85,39 @@ public class FakeUserInfoHelper extends UserInfoHelper { // ensure all profiles are started if they didn't exist before... for (int userId : currentProfileUserIds) { startUserInternal(userId, false); + setUserVisibleInternal(userId, true); } if (oldUserId != mCurrentUserId) { dispatchOnCurrentUserChanged(oldUserId, mCurrentUserId); + setUserVisibleInternal(mCurrentUserId, true); } } - @Override - public int[] getRunningUserIds() { - return mRunningUserIds.toArray(); + private void setUserVisibleInternal(int userId, boolean alwaysDispatch) { + int idx = mVisibleUserIds.indexOf(userId); + if (idx < 0) { + mVisibleUserIds.add(userId); + } else if (!alwaysDispatch) { + return; + } + dispatchOnVisibleUserChanged(userId, true); + } + + private void setUserInvisibleInternal(int userId) { + int idx = mVisibleUserIds.indexOf(userId); + if (idx >= 0) { + mVisibleUserIds.remove(userId); + } + dispatchOnVisibleUserChanged(userId, false); + } + + public void setUserVisible(int userId, boolean visible) { + if (visible) { + setUserVisibleInternal(userId, true); + } else { + setUserInvisibleInternal(userId); + } } @Override @@ -100,11 +126,21 @@ public class FakeUserInfoHelper extends UserInfoHelper { } @Override + public int[] getRunningUserIds() { + return mRunningUserIds.toArray(); + } + + @Override public int getCurrentUserId() { return mCurrentUserId; } @Override + public boolean isVisibleUserId(int userId) { + return mVisibleUserIds.indexOf(userId) >= 0; + } + + @Override protected int[] getProfileIds(int userId) { IntArray profiles = mProfiles.get(userId); if (profiles != null) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemUserInfoHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemUserInfoHelperTest.java index 490b2e87976a..d9aa23244ce2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemUserInfoHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemUserInfoHelperTest.java @@ -15,6 +15,7 @@ */ package com.android.server.location.injector; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -31,6 +32,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.location.injector.UserInfoHelper.UserListener; +import com.android.server.pm.UserManagerInternal; import org.junit.After; import org.junit.Before; @@ -45,13 +47,14 @@ public class SystemUserInfoHelperTest { private static final int USER1_ID = 1; private static final int USER1_MANAGED_ID = 11; - private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID}; + private static final int[] USER1_PROFILES = new int[] {USER1_ID, USER1_MANAGED_ID}; private static final int USER2_ID = 2; private static final int USER2_MANAGED_ID = 12; - private static final int[] USER2_PROFILES = new int[]{USER2_ID, USER2_MANAGED_ID}; + private static final int[] USER2_PROFILES = new int[] {USER2_ID, USER2_MANAGED_ID}; @Mock private Context mContext; @Mock private UserManager mUserManager; + @Mock private UserManagerInternal mUserManagerInternal; private SystemUserInfoHelper mHelper; @@ -63,12 +66,15 @@ public class SystemUserInfoHelperTest { doReturn(USER1_PROFILES).when(mUserManager).getEnabledProfileIds(USER1_ID); doReturn(USER2_PROFILES).when(mUserManager).getEnabledProfileIds(USER2_ID); + LocalServices.addService(UserManagerInternal.class, mUserManagerInternal); + mHelper = new SystemUserInfoHelper(mContext); } @After public void tearDown() { LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(UserManagerInternal.class); } @Test @@ -77,11 +83,11 @@ public class SystemUserInfoHelperTest { mHelper.addListener(listener); mHelper.dispatchOnCurrentUserChanged(USER1_ID, USER2_ID); - verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED); - verify(listener, times(1)).onUserChanged(USER1_MANAGED_ID, + verify(listener).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED); + verify(listener).onUserChanged(USER1_MANAGED_ID, UserListener.CURRENT_USER_CHANGED); - verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED); - verify(listener, times(1)).onUserChanged(USER2_MANAGED_ID, + verify(listener).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED); + verify(listener).onUserChanged(USER2_MANAGED_ID, UserListener.CURRENT_USER_CHANGED); mHelper.dispatchOnCurrentUserChanged(USER2_ID, USER1_ID); @@ -94,6 +100,25 @@ public class SystemUserInfoHelperTest { } @Test + public void testListener_UserVisibilityChanged() { + mHelper.onSystemReady(); + verify(mUserManagerInternal).addUserVisibilityListener(any()); + + UserListener listener = mock(UserListener.class); + mHelper.addListener(listener); + + mHelper.dispatchOnVisibleUserChanged(USER1_ID, false); + mHelper.dispatchOnVisibleUserChanged(USER2_ID, true); + verify(listener).onUserChanged(USER1_ID, UserListener.USER_VISIBILITY_CHANGED); + verify(listener).onUserChanged(USER2_ID, UserListener.USER_VISIBILITY_CHANGED); + + mHelper.dispatchOnVisibleUserChanged(USER2_ID, false); + mHelper.dispatchOnVisibleUserChanged(USER1_ID, true); + verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_VISIBILITY_CHANGED); + verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_VISIBILITY_CHANGED); + } + + @Test public void testListener_StartUser() { UserListener listener = mock(UserListener.class); mHelper.addListener(listener); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 20e4e8011327..aa28ad489027 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -175,7 +175,9 @@ public class LocationProviderManagerTest { doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString()); mInjector = new TestInjector(mContext); + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, true); mInjector.getUserInfoHelper().startUser(OTHER_USER); + mInjector.getUserInfoHelper().setUserVisible(OTHER_USER, true); mPassive = new PassiveLocationProviderManager(mContext, mInjector); mPassive.startManager(null); @@ -331,6 +333,20 @@ public class LocationProviderManagerTest { } @Test + public void testGetLastLocation_InvisibleUser() { + Location loc = createLocation(NAME, mRandom); + mProvider.setProviderLocation(loc); + + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, false); + assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY, + PERMISSION_FINE)).isNull(); + + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, true); + assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY, + PERMISSION_FINE)).isEqualTo(loc); + } + + @Test public void testGetLastLocation_Bypass() { mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( new PackageTagsList.Builder().add( @@ -569,6 +585,25 @@ public class LocationProviderManagerTest { } @Test + public void testRegisterListener_InvisibleUser() throws Exception { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = new LocationRequest.Builder(0) + .setWorkSource(WORK_SOURCE) + .build(); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, false); + mProvider.setProviderLocation(createLocationResult(NAME, mRandom)); + verify(listener, never()).onLocationChanged(any(List.class), + nullable(IRemoteCallback.class)); + + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, true); + LocationResult loc = createLocationResult(NAME, mRandom); + mProvider.setProviderLocation(loc); + verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class)); + } + + @Test public void testRegisterListener_ExpiringAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); LocationRequest request = new LocationRequest.Builder(0) @@ -799,6 +834,17 @@ public class LocationProviderManagerTest { } @Test + public void testGetCurrentLocation_InvisibleUser() throws Exception { + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, false); + + ILocationCallback listener = createMockGetCurrentLocationListener(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); + + verify(listener).onLocation(isNull()); + } + + @Test public void testFlush() throws Exception { ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( @@ -1008,6 +1054,21 @@ public class LocationProviderManagerTest { } @Test + public void testProviderRequest_InvisibleUser() { + ILocationListener listener = createMockLocationListener(); + LocationRequest request = new LocationRequest.Builder(5) + .setWorkSource(WORK_SOURCE) + .build(); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); + + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, false); + assertThat(mProvider.getRequest().isActive()).isFalse(); + + mInjector.getUserInfoHelper().setUserVisible(CURRENT_USER, true); + assertThat(mProvider.getRequest().isActive()).isTrue(); + } + + @Test public void testProviderRequest_IgnoreLocationSettings() { mInjector.getSettingsHelper().setIgnoreSettingsAllowlist( new PackageTagsList.Builder().add( diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java index d477cb6f356c..799a7fe3a3db 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java @@ -18,6 +18,7 @@ package com.android.server.tare; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -108,7 +109,7 @@ public class AgentTrendCalculatorTest { @Before public void setUp() { final InternalResourceService irs = mock(InternalResourceService.class); - when(irs.isVip(anyInt(), anyString())).thenReturn(false); + when(irs.isVip(anyInt(), anyString(), anyLong())).thenReturn(false); mEconomicPolicy = new MockEconomicPolicy(irs); } 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 ddfa05cf5a2e..c46ebf20c3ee 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java @@ -402,6 +402,6 @@ public class ScribeTest { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode())); pkgInfo.applicationInfo = applicationInfo; - mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(pkgInfo)); + mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(getContext(), pkgInfo)); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 0f09252b8ca1..52a550b1bcf2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -58,7 +58,6 @@ import android.view.Display; import android.view.DisplayAdjustments; import android.view.DisplayInfo; import android.view.WindowManager; -import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityWindowAttributes; import androidx.test.InstrumentationRegistry; @@ -106,8 +105,6 @@ public class AccessibilityManagerServiceTest { LABEL, DESCRIPTION, TEST_PENDING_INTENT); - private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION = - new AccessibilityAction(ACTION_ID, LABEL); private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1; @@ -282,10 +279,12 @@ public class AccessibilityManagerServiceTest { @Test public void testRegisterProxy() throws Exception { mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY); - verify(mProxyManager).registerProxy(mMockServiceClient, TEST_DISPLAY); + verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY), + eq(mTestableContext), anyInt(), any(), eq(mMockSecurityPolicy), + eq(mA11yms), eq(mA11yms.getTraceManager()), + eq(mMockWindowManagerService), eq(mMockA11yWindowManager)); } - @SmallTest @Test public void testRegisterProxyWithoutPermission() throws Exception { @@ -296,7 +295,8 @@ public class AccessibilityManagerServiceTest { Assert.fail(); } catch (SecurityException expected) { } - verify(mProxyManager, never()).registerProxy(mMockServiceClient, TEST_DISPLAY); + verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(), + any(), any(), any(), any()); } @SmallTest @@ -307,7 +307,8 @@ public class AccessibilityManagerServiceTest { Assert.fail(); } catch (IllegalArgumentException expected) { } - verify(mProxyManager, never()).registerProxy(mMockServiceClient, Display.DEFAULT_DISPLAY); + verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(), + any(), any(), any(), any()); } @SmallTest @@ -318,7 +319,30 @@ public class AccessibilityManagerServiceTest { Assert.fail(); } catch (IllegalArgumentException expected) { } - verify(mProxyManager, never()).registerProxy(mMockServiceClient, Display.INVALID_DISPLAY); + verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(), + any(), any(), any(), any()); + } + + @SmallTest + @Test + public void testUnRegisterProxyWithPermission() throws Exception { + mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY); + mA11yms.unregisterProxyForDisplay(TEST_DISPLAY); + + verify(mProxyManager).unregisterProxy(TEST_DISPLAY); + } + + @SmallTest + @Test + public void testUnRegisterProxyWithoutPermission() throws Exception { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + try { + mA11yms.unregisterProxyForDisplay(TEST_DISPLAY); + Assert.fail(); + } catch (SecurityException expected) { + } + verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY); } @SmallTest @@ -417,6 +441,8 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() { + when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); + final AccessibilityUserState userState = mA11yms.mUserStates.get( mA11yms.getCurrentUserIdLocked()); userState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME); @@ -432,6 +458,8 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() { + when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); + setupAccessibilityServiceConnection(0); when(mMockSecurityPolicy.canControlMagnification(any())).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index ccc43f29b2d1..a5d7a109eb83 100644 --- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -144,6 +144,7 @@ public final class DisplayBrightnessStrategySelectorTest { DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( DisplayManagerInternal.DisplayPowerRequest.class); displayPowerRequest.screenBrightnessOverride = Float.NaN; + when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, Display.STATE_ON), mInvalidBrightnessStrategy); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index bcdc65c19330..1e72369ac3a6 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -111,7 +111,7 @@ public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { } @Override - public void enableTelephonyTimeZoneFallback() { + public void enableTelephonyTimeZoneFallback(String reason) { throw new UnsupportedOperationException(); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 74efdb5d6d98..1c014d19d776 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -29,6 +29,9 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYP import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS; +import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_UNKNOWN; +import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW; @@ -65,6 +68,7 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality; import android.os.HandlerThread; +import android.service.timezone.TimeZoneProviderStatus; import com.android.server.SystemTimeZone.TimeZoneConfidence; import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion; @@ -1148,7 +1152,7 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testTelephonyFallback() { + public void testTelephonyFallback_enableTelephonyTimeZoneFallbackCalled() { ConfigurationInternal config = new ConfigurationInternal.Builder( CONFIG_AUTO_ENABLED_GEO_ENABLED) .setTelephonyFallbackSupported(true) @@ -1178,19 +1182,21 @@ public class TimeZoneDetectorStrategyImplTest { // Receiving an "uncertain" geolocation suggestion should have no effect. { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneNotChanged() .verifyTelephonyFallbackIsEnabled(true); } // Receiving a "certain" geolocation suggestion should disable telephony fallback mode. { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createCertainLocationAlgorithmEvent("Europe/London"); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneChangedAndReset(locationAlgorithmEvent) .verifyTelephonyFallbackIsEnabled(false); } @@ -1214,17 +1220,19 @@ public class TimeZoneDetectorStrategyImplTest { // Geolocation suggestions should continue to be used as normal (previous telephony // suggestions are not used, even when the geolocation suggestion is uncertain). { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent certainLocationAlgorithmEvent = createCertainLocationAlgorithmEvent("Europe/Rome"); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent) .verifyTimeZoneChangedAndReset(certainLocationAlgorithmEvent) .verifyTelephonyFallbackIsEnabled(false); + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent uncertainLocationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent) .verifyTimeZoneNotChanged() .verifyTelephonyFallbackIsEnabled(false); @@ -1246,19 +1254,21 @@ public class TimeZoneDetectorStrategyImplTest { // Make the geolocation algorithm uncertain. { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneChangedAndReset(lastTelephonySuggestion) .verifyTelephonyFallbackIsEnabled(true); } // Make the geolocation algorithm certain, disabling telephony fallback. { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createCertainLocationAlgorithmEvent("Europe/Lisbon"); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneChangedAndReset(locationAlgorithmEvent) .verifyTelephonyFallbackIsEnabled(false); @@ -1267,9 +1277,10 @@ public class TimeZoneDetectorStrategyImplTest { // Demonstrate what happens when geolocation is uncertain when telephony fallback is // enabled. { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneNotChanged() .verifyTelephonyFallbackIsEnabled(false) .simulateEnableTelephonyFallback() @@ -1279,6 +1290,132 @@ public class TimeZoneDetectorStrategyImplTest { } @Test + public void testTelephonyFallback_locationAlgorithmEventSuggestsFallback() { + ConfigurationInternal config = new ConfigurationInternal.Builder( + CONFIG_AUTO_ENABLED_GEO_ENABLED) + .setTelephonyFallbackSupported(true) + .build(); + + Script script = new Script() + .initializeClock(ARBITRARY_ELAPSED_REALTIME_MILLIS) + .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) + .simulateConfigurationInternalChange(config) + .resetConfigurationTracking(); + + // Confirm initial state is as expected. + script.verifyTelephonyFallbackIsEnabled(true) + .verifyTimeZoneNotChanged(); + + // Although geolocation detection is enabled, telephony fallback should be used initially + // and until a suitable "certain" geolocation suggestion is received. + { + TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion( + SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, + "Europe/Paris"); + script.simulateIncrementClock() + .simulateTelephonyTimeZoneSuggestion(telephonySuggestion) + .verifyTimeZoneChangedAndReset(telephonySuggestion) + .verifyTelephonyFallbackIsEnabled(true); + } + + // Receiving an "uncertain" geolocation suggestion without a status should have no effect. + { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) + .verifyTimeZoneNotChanged() + .verifyTelephonyFallbackIsEnabled(true); + } + + // Receiving a "certain" geolocation suggestion should disable telephony fallback mode. + { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + LocationAlgorithmEvent locationAlgorithmEvent = + createCertainLocationAlgorithmEvent("Europe/London"); + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) + .verifyTimeZoneChangedAndReset(locationAlgorithmEvent) + .verifyTelephonyFallbackIsEnabled(false); + } + + // Used to record the last telephony suggestion received, which will be used when fallback + // takes place. + TelephonyTimeZoneSuggestion lastTelephonySuggestion; + + // Telephony suggestions should now be ignored and geolocation detection is "in control". + { + TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion( + SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, + "Europe/Berlin"); + script.simulateIncrementClock() + .simulateTelephonyTimeZoneSuggestion(telephonySuggestion) + .verifyTimeZoneNotChanged() + .verifyTelephonyFallbackIsEnabled(false); + lastTelephonySuggestion = telephonySuggestion; + } + + // Geolocation suggestions should continue to be used as normal (previous telephony + // suggestions are not used, even when the geolocation suggestion is uncertain). + { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + LocationAlgorithmEvent certainLocationAlgorithmEvent = + createCertainLocationAlgorithmEvent("Europe/Rome"); + script.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent) + .verifyTimeZoneChangedAndReset(certainLocationAlgorithmEvent) + .verifyTelephonyFallbackIsEnabled(false); + + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + LocationAlgorithmEvent uncertainLocationAlgorithmEvent = + createUncertainLocationAlgorithmEvent(); + script.simulateLocationAlgorithmEvent(uncertainLocationAlgorithmEvent) + .verifyTimeZoneNotChanged() + .verifyTelephonyFallbackIsEnabled(false); + + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + LocationAlgorithmEvent certainLocationAlgorithmEvent2 = + createCertainLocationAlgorithmEvent("Europe/Rome"); + script.simulateLocationAlgorithmEvent(certainLocationAlgorithmEvent2) + // No change needed, device will already be set to Europe/Rome. + .verifyTimeZoneNotChanged() + .verifyTelephonyFallbackIsEnabled(false); + } + + // Enable telephony fallback via a LocationAlgorithmEvent containing an "uncertain" + // suggestion. + { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + TimeZoneProviderStatus primaryProviderReportedStatus = + new TimeZoneProviderStatus.Builder() + .setLocationDetectionDependencyStatus( + DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS) + .setConnectivityDependencyStatus(DEPENDENCY_STATUS_UNKNOWN) + .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_UNKNOWN) + .build(); + LocationAlgorithmEvent uncertainEventBlockedBySettings = + createUncertainLocationAlgorithmEvent(primaryProviderReportedStatus); + script.simulateLocationAlgorithmEvent(uncertainEventBlockedBySettings) + .verifyTimeZoneChangedAndReset(lastTelephonySuggestion) + .verifyTelephonyFallbackIsEnabled(true); + } + + // Make the geolocation algorithm certain, disabling telephony fallback. + { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); + LocationAlgorithmEvent locationAlgorithmEvent = + createCertainLocationAlgorithmEvent("Europe/Lisbon"); + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) + .verifyTimeZoneChangedAndReset(locationAlgorithmEvent) + .verifyTelephonyFallbackIsEnabled(false); + } + } + + @Test public void testTelephonyFallback_noTelephonySuggestionToFallBackTo() { ConfigurationInternal config = new ConfigurationInternal.Builder( CONFIG_AUTO_ENABLED_GEO_ENABLED) @@ -1297,9 +1434,10 @@ public class TimeZoneDetectorStrategyImplTest { // Receiving an "uncertain" geolocation suggestion should have no effect. { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneNotChanged() .verifyTelephonyFallbackIsEnabled(true); } @@ -1307,9 +1445,10 @@ public class TimeZoneDetectorStrategyImplTest { // Make an uncertain geolocation suggestion, there is no telephony suggestion to fall back // to { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent locationAlgorithmEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(locationAlgorithmEvent) + script.simulateLocationAlgorithmEvent(locationAlgorithmEvent) .verifyTimeZoneNotChanged() .verifyTelephonyFallbackIsEnabled(true); } @@ -1319,16 +1458,18 @@ public class TimeZoneDetectorStrategyImplTest { // Geolocation suggestions should continue to be used as normal (previous telephony // suggestions are not used, even when the geolocation suggestion is uncertain). { + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent certainEvent = createCertainLocationAlgorithmEvent("Europe/Rome"); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(certainEvent) + script.simulateLocationAlgorithmEvent(certainEvent) .verifyTimeZoneChangedAndReset(certainEvent) .verifyTelephonyFallbackIsEnabled(false); + // Increment the clock before creating the event: the clock's value is used by the event + script.simulateIncrementClock(); LocationAlgorithmEvent uncertainEvent = createUncertainLocationAlgorithmEvent(); - script.simulateIncrementClock() - .simulateLocationAlgorithmEvent(uncertainEvent) + script.simulateLocationAlgorithmEvent(uncertainEvent) .verifyTimeZoneNotChanged() .verifyTelephonyFallbackIsEnabled(false); @@ -1549,9 +1690,16 @@ public class TimeZoneDetectorStrategyImplTest { } private LocationAlgorithmEvent createUncertainLocationAlgorithmEvent() { + TimeZoneProviderStatus primaryProviderReportedStatus = null; + return createUncertainLocationAlgorithmEvent(primaryProviderReportedStatus); + } + + private LocationAlgorithmEvent createUncertainLocationAlgorithmEvent( + TimeZoneProviderStatus primaryProviderReportedStatus) { GeolocationTimeZoneSuggestion suggestion = createUncertainGeolocationSuggestion(); LocationTimeZoneAlgorithmStatus algorithmStatus = new LocationTimeZoneAlgorithmStatus( - DETECTION_ALGORITHM_STATUS_RUNNING, PROVIDER_STATUS_IS_UNCERTAIN, null, + DETECTION_ALGORITHM_STATUS_RUNNING, + PROVIDER_STATUS_IS_UNCERTAIN, primaryProviderReportedStatus, PROVIDER_STATUS_NOT_PRESENT, null); LocationAlgorithmEvent event = new LocationAlgorithmEvent(algorithmStatus, suggestion); event.addDebugInfo("Test uncertain event"); @@ -1744,11 +1892,12 @@ public class TimeZoneDetectorStrategyImplTest { } /** - * Simulates the time zone detection strategty receiving a signal that allows it to do + * Simulates the time zone detection strategy receiving a signal that allows it to do * telephony fallback. */ Script simulateEnableTelephonyFallback() { - mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback(); + mTimeZoneDetectorStrategy.enableTelephonyTimeZoneFallback( + "simulateEnableTelephonyFallback()"); return this; } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index f3f56e0709d8..dc3515dec2f5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -114,6 +114,20 @@ public class BackNavigationControllerTests extends WindowTestsBase { } @Test + public void backTypeBackToHomeDifferentUser() { + Task taskA = createTask(mDefaultDisplay); + ActivityRecord recordA = createActivityRecord(taskA); + Mockito.doNothing().when(recordA).reparentSurfaceControl(any(), any()); + doReturn(false).when(taskA).showToCurrentUser(); + + withSystemCallback(createTopTaskWithActivity()); + BackNavigationInfo backNavigationInfo = startBackNavigation(); + assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); + assertThat(typeToString(backNavigationInfo.getType())) + .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); + } + + @Test public void backTypeCrossActivityWhenBackToPreviousActivity() { CrossActivityTestCase testCase = createTopTaskWithTwoActivities(); IOnBackInvokedCallback callback = withSystemCallback(testCase.task); diff --git a/services/usb/Android.bp b/services/usb/Android.bp index 3b50fa43536c..52cfe25d9f26 100644 --- a/services/usb/Android.bp +++ b/services/usb/Android.bp @@ -29,7 +29,7 @@ java_library_static { "android.hardware.usb-V1.1-java", "android.hardware.usb-V1.2-java", "android.hardware.usb-V1.3-java", - "android.hardware.usb-V1-java", + "android.hardware.usb-V2-java", "android.hardware.usb.gadget-V1.0-java", "android.hardware.usb.gadget-V1.1-java", "android.hardware.usb.gadget-V1.2-java", diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index f8df6c6ea8df..4bb9de532360 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -73,6 +73,7 @@ import android.service.ServiceProtoEnums; import android.service.usb.UsbPortInfoProto; import android.service.usb.UsbPortManagerProto; import android.util.ArrayMap; +import android.util.IntArray; import android.util.Log; import android.util.Slog; @@ -87,6 +88,7 @@ import com.android.server.usb.hal.port.RawPortInfo; import com.android.server.usb.hal.port.UsbPortHal; import com.android.server.usb.hal.port.UsbPortHalInstance; +import java.util.Arrays; import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.Objects; @@ -754,6 +756,31 @@ public class UsbPortManager { } } + /** + * Sets Compliance Warnings for simulated USB port objects. + */ + public void simulateComplianceWarnings(String portId, String complianceWarningsString, + IndentingPrintWriter pw) { + synchronized (mLock) { + final RawPortInfo portInfo = mSimulatedPorts.get(portId); + if (portInfo == null) { + pw.println("Simulated port not found"); + return; + } + + IntArray complianceWarnings = new IntArray(); + for (String s : complianceWarningsString.split("[, ]")) { + if (s.length() > 0) { + complianceWarnings.add(Integer.parseInt(s)); + } + } + pw.println("Simulating Compliance Warnings: portId=" + portId + + " Warnings=" + complianceWarningsString); + portInfo.complianceWarnings = complianceWarnings.toArray(); + updatePortsLocked(pw, null); + } + } + public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { synchronized (mLock) { final RawPortInfo portInfo = mSimulatedPorts.get(portId); @@ -842,7 +869,10 @@ public class UsbPortManager { portInfo.contaminantDetectionStatus, portInfo.usbDataStatus, portInfo.powerTransferLimited, - portInfo.powerBrickConnectionStatus, pw); + portInfo.powerBrickConnectionStatus, + portInfo.supportsComplianceWarnings, + portInfo.complianceWarnings, + pw); } } else { for (RawPortInfo currentPortInfo : newPortInfo) { @@ -857,7 +887,10 @@ public class UsbPortManager { currentPortInfo.contaminantDetectionStatus, currentPortInfo.usbDataStatus, currentPortInfo.powerTransferLimited, - currentPortInfo.powerBrickConnectionStatus, pw); + currentPortInfo.powerBrickConnectionStatus, + currentPortInfo.supportsComplianceWarnings, + currentPortInfo.complianceWarnings, + pw); } } @@ -880,6 +913,9 @@ public class UsbPortManager { handlePortRemovedLocked(portInfo, pw); break; } + if (portInfo.mComplianceWarningChange == portInfo.COMPLIANCE_WARNING_CHANGED) { + handlePortComplianceWarningLocked(portInfo, pw); + } } } @@ -896,6 +932,8 @@ public class UsbPortManager { int usbDataStatus, boolean powerTransferLimited, int powerBrickConnectionStatus, + boolean supportsComplianceWarnings, + @NonNull int[] complianceWarnings, IndentingPrintWriter pw) { // Only allow mode switch capability for dual role ports. // Validate that the current mode matches the supported modes we expect. @@ -949,13 +987,15 @@ public class UsbPortManager { portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId, supportedModes, supportedContaminantProtectionModes, supportsEnableContaminantPresenceProtection, - supportsEnableContaminantPresenceDetection); + supportsEnableContaminantPresenceDetection, + supportsComplianceWarnings); portInfo.setStatus(currentMode, canChangeMode, currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, contaminantDetectionStatus, usbDataStatus, - powerTransferLimited, powerBrickConnectionStatus); + powerTransferLimited, powerBrickConnectionStatus, + complianceWarnings); mPorts.put(portId, portInfo); } else { // Validate that ports aren't changing definition out from under us. @@ -987,13 +1027,13 @@ public class UsbPortManager { + ", current=" + supportsEnableContaminantPresenceDetection); } - if (portInfo.setStatus(currentMode, canChangeMode, currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, contaminantDetectionStatus, usbDataStatus, - powerTransferLimited, powerBrickConnectionStatus)) { + powerTransferLimited, powerBrickConnectionStatus, + complianceWarnings)) { portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; } else { portInfo.mDisposition = PortInfo.DISPOSITION_READY; @@ -1019,6 +1059,11 @@ public class UsbPortManager { handlePortLocked(portInfo, pw); } + private void handlePortComplianceWarningLocked(PortInfo portInfo, IndentingPrintWriter pw) { + logAndPrint(Log.INFO, pw, "USB port compliance warning changed: " + portInfo); + sendComplianceWarningBroadcastLocked(portInfo); + } + private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) { logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo); handlePortLocked(portInfo, pw); @@ -1056,6 +1101,23 @@ public class UsbPortManager { Manifest.permission.MANAGE_USB)); } + private void sendComplianceWarningBroadcastLocked(PortInfo portInfo) { + if (portInfo.mComplianceWarningChange == portInfo.COMPLIANCE_WARNING_UNCHANGED) { + return; + } + final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED); + intent.addFlags( + Intent.FLAG_RECEIVER_FOREGROUND | + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort)); + intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); + + // Guard against possible reentrance by posting the broadcast from the handler + // instead of from within the critical section. + mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL, + Manifest.permission.MANAGE_USB)); + } + private void enableContaminantDetectionIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) { if (!mConnected.containsKey(portInfo.mUsbPort.getId())) { return; @@ -1180,6 +1242,9 @@ public class UsbPortManager { public static final int DISPOSITION_READY = 2; public static final int DISPOSITION_REMOVED = 3; + public static final int COMPLIANCE_WARNING_UNCHANGED = 0; + public static final int COMPLIANCE_WARNING_CHANGED = 1; + public final UsbPort mUsbPort; public UsbPortStatus mUsbPortStatus; public boolean mCanChangeMode; @@ -1191,15 +1256,29 @@ public class UsbPortManager { public long mConnectedAtMillis; // 0 when port is connected. Else reports the last connected duration public long mLastConnectDurationMillis; + // default initialized to 0 which means no changes reported + public int mComplianceWarningChange; PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes, int supportedContaminantProtectionModes, boolean supportsEnableContaminantPresenceDetection, - boolean supportsEnableContaminantPresenceProtection) { + boolean supportsEnableContaminantPresenceProtection, + boolean supportsComplianceWarnings) { mUsbPort = new UsbPort(usbManager, portId, supportedModes, supportedContaminantProtectionModes, supportsEnableContaminantPresenceDetection, - supportsEnableContaminantPresenceProtection); + supportsEnableContaminantPresenceProtection, + supportsComplianceWarnings); + mComplianceWarningChange = COMPLIANCE_WARNING_UNCHANGED; + } + + public boolean complianceWarningsChanged(@NonNull int[] complianceWarnings) { + if (Arrays.equals(complianceWarnings, mUsbPortStatus.getComplianceWarnings())) { + mComplianceWarningChange = COMPLIANCE_WARNING_UNCHANGED; + return false; + } + mComplianceWarningChange = COMPLIANCE_WARNING_CHANGED; + return true; } public boolean setStatus(int currentMode, boolean canChangeMode, @@ -1221,7 +1300,8 @@ public class UsbPortManager { supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE, UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, UsbPortStatus.DATA_STATUS_UNKNOWN, false, - UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN); + UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN, + new int[] {}); dispositionChanged = true; } @@ -1266,8 +1346,65 @@ public class UsbPortManager { mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, contaminantProtectionStatus, contaminantDetectionStatus, usbDataStatus, - powerTransferLimited, powerBrickConnectionStatus); + powerTransferLimited, powerBrickConnectionStatus, + new int[] {}); + dispositionChanged = true; + } + + if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { + mConnectedAtMillis = SystemClock.elapsedRealtime(); + mLastConnectDurationMillis = 0; + } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) { + mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis; + mConnectedAtMillis = 0; + } + + return dispositionChanged; + } + + public boolean setStatus(int currentMode, boolean canChangeMode, + int currentPowerRole, boolean canChangePowerRole, + int currentDataRole, boolean canChangeDataRole, + int supportedRoleCombinations, int contaminantProtectionStatus, + int contaminantDetectionStatus, int usbDataStatus, + boolean powerTransferLimited, int powerBrickConnectionStatus, + @NonNull int[] complianceWarnings) { + boolean dispositionChanged = false; + + mCanChangeMode = canChangeMode; + mCanChangePowerRole = canChangePowerRole; + mCanChangeDataRole = canChangeDataRole; + if (mUsbPortStatus == null + || mUsbPortStatus.getCurrentMode() != currentMode + || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole + || mUsbPortStatus.getCurrentDataRole() != currentDataRole + || mUsbPortStatus.getSupportedRoleCombinations() + != supportedRoleCombinations + || mUsbPortStatus.getContaminantProtectionStatus() + != contaminantProtectionStatus + || mUsbPortStatus.getContaminantDetectionStatus() + != contaminantDetectionStatus + || mUsbPortStatus.getUsbDataStatus() + != usbDataStatus + || mUsbPortStatus.isPowerTransferLimited() + != powerTransferLimited + || mUsbPortStatus.getPowerBrickConnectionStatus() + != powerBrickConnectionStatus) { + if (mUsbPortStatus == null && complianceWarnings.length > 0) { + mComplianceWarningChange = COMPLIANCE_WARNING_CHANGED; + } + mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, + supportedRoleCombinations, contaminantProtectionStatus, + contaminantDetectionStatus, usbDataStatus, + powerTransferLimited, powerBrickConnectionStatus, + complianceWarnings); dispositionChanged = true; + } else if (complianceWarningsChanged(complianceWarnings)) { + mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, + supportedRoleCombinations, contaminantProtectionStatus, + contaminantDetectionStatus, usbDataStatus, + powerTransferLimited, powerBrickConnectionStatus, + complianceWarnings); } if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 72f6cc3649a7..d821dee75d7d 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -1093,6 +1093,23 @@ public class UsbService extends IUsbManager.Stub { mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")), "", 0); } + } else if ("set-compliance-reasons".equals(args[0]) && args.length == 3) { + final String portId = args[1]; + final String complianceWarnings = args[2]; + if (mPortManager != null) { + mPortManager.simulateComplianceWarnings(portId, complianceWarnings, pw); + pw.println(); + mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")), + "", 0); + } + } else if ("clear-compliance-reasons".equals(args[0]) && args.length == 2) { + final String portId = args[1]; + if (mPortManager != null) { + mPortManager.simulateComplianceWarnings(portId, "", pw); + pw.println(); + mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")), + "", 0); + } } else if ("ports".equals(args[0]) && args.length == 1) { if (mPortManager != null) { mPortManager.dump(new DualDumpOutputStream(new IndentingPrintWriter(pw, " ")), @@ -1142,6 +1159,17 @@ public class UsbService extends IUsbManager.Stub { pw.println(" dumpsys usb set-contaminant-status \"matrix\" true"); pw.println(" dumpsys usb set-contaminant-status \"matrix\" false"); pw.println(); + pw.println("Example simulate compliance warnings:"); + pw.println(" dumpsys usb add-port \"matrix\" dual"); + pw.println(" dumpsys usb set-compliance-reasons \"matrix\" <reason-list>"); + pw.println(" dumpsys usb clear-compliance-reasons \"matrix\""); + pw.println("<reason-list> is expected to be formatted as \"1, ..., 4\""); + pw.println("with reasons that need to be simulated."); + pw.println(" 1: debug accessory"); + pw.println(" 2: bc12"); + pw.println(" 3: missing rp"); + pw.println(" 4: type c"); + pw.println(); pw.println("Example USB device descriptors:"); pw.println(" dumpsys usb dump-descriptors -dump-short"); pw.println(" dumpsys usb dump-descriptors -dump-tree"); diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java index 128a0512e830..e6a3e5343507 100644 --- a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java +++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java @@ -40,6 +40,8 @@ public final class RawPortInfo implements Parcelable { public int usbDataStatus; public boolean powerTransferLimited; public int powerBrickConnectionStatus; + public final boolean supportsComplianceWarnings; + public int[] complianceWarnings; public RawPortInfo(String portId, int supportedModes) { this.portId = portId; @@ -50,9 +52,10 @@ public final class RawPortInfo implements Parcelable { this.supportsEnableContaminantPresenceDetection = false; this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; this.usbDataStatus = UsbPortStatus.DATA_STATUS_UNKNOWN; - this.powerTransferLimited = false; this.powerBrickConnectionStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN; + this.supportsComplianceWarnings = false; + this.complianceWarnings = new int[] {}; } public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, @@ -66,6 +69,29 @@ public final class RawPortInfo implements Parcelable { int usbDataStatus, boolean powerTransferLimited, int powerBrickConnectionStatus) { + this(portId, supportedModes, supportedContaminantProtectionModes, + currentMode, canChangeMode, + currentPowerRole, canChangePowerRole, + currentDataRole, canChangeDataRole, + supportsEnableContaminantPresenceProtection, contaminantProtectionStatus, + supportsEnableContaminantPresenceDetection, contaminantDetectionStatus, + usbDataStatus, powerTransferLimited, powerBrickConnectionStatus, + false, new int[] {}); + } + + public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, + int currentMode, boolean canChangeMode, + int currentPowerRole, boolean canChangePowerRole, + int currentDataRole, boolean canChangeDataRole, + boolean supportsEnableContaminantPresenceProtection, + int contaminantProtectionStatus, + boolean supportsEnableContaminantPresenceDetection, + int contaminantDetectionStatus, + int usbDataStatus, + boolean powerTransferLimited, + int powerBrickConnectionStatus, + boolean supportsComplianceWarnings, + int[] complianceWarnings) { this.portId = portId; this.supportedModes = supportedModes; this.supportedContaminantProtectionModes = supportedContaminantProtectionModes; @@ -84,6 +110,8 @@ public final class RawPortInfo implements Parcelable { this.usbDataStatus = usbDataStatus; this.powerTransferLimited = powerTransferLimited; this.powerBrickConnectionStatus = powerBrickConnectionStatus; + this.supportsComplianceWarnings = supportsComplianceWarnings; + this.complianceWarnings = complianceWarnings; } @Override @@ -109,6 +137,8 @@ public final class RawPortInfo implements Parcelable { dest.writeInt(usbDataStatus); dest.writeBoolean(powerTransferLimited); dest.writeInt(powerBrickConnectionStatus); + dest.writeBoolean(supportsComplianceWarnings); + dest.writeIntArray(complianceWarnings); } public static final Parcelable.Creator<RawPortInfo> CREATOR = @@ -131,6 +161,8 @@ public final class RawPortInfo implements Parcelable { int usbDataStatus = in.readInt(); boolean powerTransferLimited = in.readBoolean(); int powerBrickConnectionStatus = in.readInt(); + boolean supportsComplianceWarnings = in.readBoolean(); + int[] complianceWarnings = in.createIntArray(); return new RawPortInfo(id, supportedModes, supportedContaminantProtectionModes, currentMode, canChangeMode, currentPowerRole, canChangePowerRole, @@ -139,7 +171,8 @@ public final class RawPortInfo implements Parcelable { contaminantProtectionStatus, supportsEnableContaminantPresenceDetection, contaminantDetectionStatus, usbDataStatus, - powerTransferLimited, powerBrickConnectionStatus); + powerTransferLimited, powerBrickConnectionStatus, + supportsComplianceWarnings, complianceWarnings); } @Override diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java index 94273a37abcd..ca11629800a1 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java @@ -34,9 +34,12 @@ import android.hardware.usb.Status; import android.hardware.usb.IUsbCallback; import android.hardware.usb.PortRole; import android.hardware.usb.PortStatus; +import android.hardware.usb.ComplianceWarning; +import android.os.Build; import android.os.ServiceManager; import android.os.IBinder; import android.os.RemoteException; +import android.util.IntArray; import android.util.Log; import android.util.LongSparseArray; import android.util.Slog; @@ -46,6 +49,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.usb.UsbPortManager; import com.android.server.usb.hal.port.RawPortInfo; +import java.util.Arrays; import java.util.ArrayList; import java.util.concurrent.ThreadLocalRandom; import java.util.NoSuchElementException; @@ -551,6 +555,24 @@ public final class UsbPortAidl implements UsbPortHal { return usbDataStatus; } + private int[] formatComplianceWarnings(int[] complianceWarnings) { + Objects.requireNonNull(complianceWarnings); + IntArray newComplianceWarnings = new IntArray(); + Arrays.sort(complianceWarnings); + for (int warning : complianceWarnings) { + if (newComplianceWarnings.indexOf(warning) == -1 + && warning >= UsbPortStatus.COMPLIANCE_WARNING_OTHER) { + // ComplianceWarnings range from [1, 4] in Android U + if (warning > UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP) { + newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + } else { + newComplianceWarnings.add(warning); + } + } + } + return newComplianceWarnings.toArray(); + } + @Override public void notifyPortStatusChange( android.hardware.usb.PortStatus[] currentPortStatus, int retval) { @@ -584,7 +606,9 @@ public final class UsbPortAidl implements UsbPortHal { current.contaminantDetectionStatus, toUsbDataStatusInt(current.usbDataStatus), current.powerTransferLimited, - current.powerBrickStatus); + current.powerBrickStatus, + current.supportsComplianceWarnings, + formatComplianceWarnings(current.complianceWarnings)); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: " + current.portName); diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java index 23d913cba733..10403c1a5f73 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java @@ -421,7 +421,8 @@ public final class UsbPortHidl implements UsbPortHal { current.currentDataRole, current.canChangeDataRole, false, CONTAMINANT_PROTECTION_NONE, false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataStatus, - false, POWER_BRICK_STATUS_UNKNOWN); + false, POWER_BRICK_STATUS_UNKNOWN, + false, new int[] {}); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: " + current.portName); @@ -455,7 +456,8 @@ public final class UsbPortHidl implements UsbPortHal { current.status.currentDataRole, current.status.canChangeDataRole, false, CONTAMINANT_PROTECTION_NONE, false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataStatus, - false, POWER_BRICK_STATUS_UNKNOWN); + false, POWER_BRICK_STATUS_UNKNOWN, + false, new int[] {}); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: " + current.status.portName); @@ -493,7 +495,8 @@ public final class UsbPortHidl implements UsbPortHal { current.supportsEnableContaminantPresenceDetection, current.contaminantDetectionStatus, sUsbDataStatus, - false, POWER_BRICK_STATUS_UNKNOWN); + false, POWER_BRICK_STATUS_UNKNOWN, + false, new int[] {}); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: " + current.status_1_1.status.portName); diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index 2435243f0044..86b98f1cbe79 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -5,7 +5,6 @@ import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.telecom.Connection; import android.telephony.data.ApnSetting; -import android.telephony.ims.ImsCallProfile; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -495,7 +494,7 @@ public class Annotation { PreciseCallState.PRECISE_CALL_STATE_HOLDING, PreciseCallState.PRECISE_CALL_STATE_DIALING, PreciseCallState.PRECISE_CALL_STATE_ALERTING, - PreciseCallState.PRECISE_CALL_STATE_INCOMING, + PreciseCallState. PRECISE_CALL_STATE_INCOMING, PreciseCallState.PRECISE_CALL_STATE_WAITING, PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED, PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING}) @@ -728,36 +727,6 @@ public class Annotation { }) public @interface ValidationStatus {} - /** - * IMS call Service types - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "SERVICE_TYPE_" }, value = { - ImsCallProfile.SERVICE_TYPE_NONE, - ImsCallProfile.SERVICE_TYPE_NORMAL, - ImsCallProfile.SERVICE_TYPE_EMERGENCY, - }) - public @interface ImsCallServiceType {} - - /** - * IMS call types - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "CALL_TYPE_" }, value = { - ImsCallProfile.CALL_TYPE_NONE, - ImsCallProfile.CALL_TYPE_VOICE_N_VIDEO, - ImsCallProfile.CALL_TYPE_VOICE, - ImsCallProfile.CALL_TYPE_VIDEO_N_VOICE, - ImsCallProfile.CALL_TYPE_VT, - ImsCallProfile.CALL_TYPE_VT_TX, - ImsCallProfile.CALL_TYPE_VT_RX, - ImsCallProfile.CALL_TYPE_VT_NODIR, - ImsCallProfile.CALL_TYPE_VS, - ImsCallProfile.CALL_TYPE_VS_TX, - ImsCallProfile.CALL_TYPE_VS_RX, - }) - public @interface ImsCallType {} - /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = { diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java index 1dc64a9200fc..b7bef39aa275 100644 --- a/telephony/java/android/telephony/CallAttributes.java +++ b/telephony/java/android/telephony/CallAttributes.java @@ -29,10 +29,8 @@ import java.util.Objects; * Contains information about a call's attributes as passed up from the HAL. If there are multiple * ongoing calls, the CallAttributes will pertain to the call in the foreground. * @hide - * @deprecated use {@link CallState} for call information for each call. */ @SystemApi -@Deprecated public final class CallAttributes implements Parcelable { private PreciseCallState mPreciseCallState; @NetworkType diff --git a/telephony/java/android/telephony/CallState.java b/telephony/java/android/telephony/CallState.java deleted file mode 100644 index 0a267cf0ff1f..000000000000 --- a/telephony/java/android/telephony/CallState.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (C) 2018 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 android.telephony; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; -import android.telephony.Annotation.ImsCallServiceType; -import android.telephony.Annotation.ImsCallType; -import android.telephony.Annotation.NetworkType; -import android.telephony.Annotation.PreciseCallStates; -import android.telephony.ims.ImsCallProfile; -import android.telephony.ims.ImsCallSession; - -import java.util.Objects; - -/** - * Contains information about various states for a call. - * @hide - */ -@SystemApi -public final class CallState implements Parcelable { - - /** - * Call classifications are just used for backward compatibility of deprecated API {@link - * TelephonyCallback#CallAttributesListener#onCallAttributesChanged}, Since these will be - * removed when the deprecated API is removed, they should not be opened. - */ - /** - * Call classification is not valid. It should not be opened. - * @hide - */ - public static final int CALL_CLASSIFICATION_UNKNOWN = -1; - - /** - * Call classification indicating foreground call - * @hide - */ - public static final int CALL_CLASSIFICATION_RINGING = 0; - - /** - * Call classification indicating background call - * @hide - */ - public static final int CALL_CLASSIFICATION_FOREGROUND = 1; - - /** - * Call classification indicating ringing call - * @hide - */ - public static final int CALL_CLASSIFICATION_BACKGROUND = 2; - - /** - * Call classification Max value. - * @hide - */ - public static final int CALL_CLASSIFICATION_MAX = CALL_CLASSIFICATION_BACKGROUND + 1; - - @PreciseCallStates - private final int mPreciseCallState; - - @NetworkType - private final int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints - private final CallQuality mCallQuality; - - private final int mCallClassification; - /** - * IMS call session ID. {@link ImsCallSession#getCallId()} - */ - @Nullable - private String mImsCallId; - - /** - * IMS call service type of this call - */ - @ImsCallServiceType - private int mImsCallServiceType; - - /** - * IMS call type of this call. - */ - @ImsCallType - private int mImsCallType; - - /** - * Constructor of CallAttributes - * - * @param callState call state defined in {@link PreciseCallState} - * @param networkType network type for this call attributes - * @param callQuality call quality for this call attributes, only CallState in - * {@link PreciseCallState#PRECISE_CALL_STATE_ACTIVE} will have valid call - * quality. - * @param callClassification call classification - * @param imsCallId IMS call session ID for this call attributes - * @param imsCallServiceType IMS call service type for this call attributes - * @param imsCallType IMS call type for this call attributes - */ - private CallState(@PreciseCallStates int callState, @NetworkType int networkType, - @NonNull CallQuality callQuality, int callClassification, @Nullable String imsCallId, - @ImsCallServiceType int imsCallServiceType, @ImsCallType int imsCallType) { - this.mPreciseCallState = callState; - this.mNetworkType = networkType; - this.mCallQuality = callQuality; - this.mCallClassification = callClassification; - this.mImsCallId = imsCallId; - this.mImsCallServiceType = imsCallServiceType; - this.mImsCallType = imsCallType; - } - - @NonNull - @Override - public String toString() { - return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType - + " mCallQuality=" + mCallQuality + " mCallClassification" + mCallClassification - + " mImsCallId=" + mImsCallId + " mImsCallServiceType=" + mImsCallServiceType - + " mImsCallType=" + mImsCallType; - } - - private CallState(Parcel in) { - this.mPreciseCallState = in.readInt(); - this.mNetworkType = in.readInt(); - this.mCallQuality = in.readParcelable( - CallQuality.class.getClassLoader(), CallQuality.class); - this.mCallClassification = in.readInt(); - this.mImsCallId = in.readString(); - this.mImsCallServiceType = in.readInt(); - this.mImsCallType = in.readInt(); - } - - // getters - /** - * Returns the precise call state of the call. - */ - @PreciseCallStates - public int getCallState() { - return mPreciseCallState; - } - - /** - * Returns the {@link TelephonyManager#NetworkType} of the call. - * - * @see TelephonyManager#NETWORK_TYPE_UNKNOWN - * @see TelephonyManager#NETWORK_TYPE_GPRS - * @see TelephonyManager#NETWORK_TYPE_EDGE - * @see TelephonyManager#NETWORK_TYPE_UMTS - * @see TelephonyManager#NETWORK_TYPE_CDMA - * @see TelephonyManager#NETWORK_TYPE_EVDO_0 - * @see TelephonyManager#NETWORK_TYPE_EVDO_A - * @see TelephonyManager#NETWORK_TYPE_1xRTT - * @see TelephonyManager#NETWORK_TYPE_HSDPA - * @see TelephonyManager#NETWORK_TYPE_HSUPA - * @see TelephonyManager#NETWORK_TYPE_HSPA - * @see TelephonyManager#NETWORK_TYPE_IDEN - * @see TelephonyManager#NETWORK_TYPE_EVDO_B - * @see TelephonyManager#NETWORK_TYPE_LTE - * @see TelephonyManager#NETWORK_TYPE_EHRPD - * @see TelephonyManager#NETWORK_TYPE_HSPAP - * @see TelephonyManager#NETWORK_TYPE_GSM - * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA - * @see TelephonyManager#NETWORK_TYPE_IWLAN - * @see TelephonyManager#NETWORK_TYPE_LTE_CA - * @see TelephonyManager#NETWORK_TYPE_NR - */ - @NetworkType - public int getNetworkType() { - return mNetworkType; - } - - /** - * Returns the {#link CallQuality} of the call. - * @return call quality for this call attributes, only CallState in {@link PreciseCallState# - * PRECISE_CALL_STATE_ACTIVE} will have valid call quality. It will be null for the - * call which is not in {@link PreciseCallState#PRECISE_CALL_STATE_ACTIVE}. - */ - @Nullable - public CallQuality getCallQuality() { - return mCallQuality; - } - - /** - * Returns the call classification. - * @hide - */ - public int getCallClassification() { - return mCallClassification; - } - - /** - * Returns the IMS call session ID. - */ - @Nullable - public String getImsCallSessionId() { - return mImsCallId; - } - - /** - * Returns the IMS call service type. - */ - @ImsCallServiceType - public int getImsCallServiceType() { - return mImsCallServiceType; - } - - /** - * Returns the IMS call type. - */ - @ImsCallType - public int getImsCallType() { - return mImsCallType; - } - - @Override - public int hashCode() { - return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality, mCallClassification, - mImsCallId, mImsCallServiceType, mImsCallType); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o == null || !(o instanceof CallState) || hashCode() != o.hashCode()) { - return false; - } - - if (this == o) { - return true; - } - - CallState s = (CallState) o; - - return (Objects.equals(mPreciseCallState, s.mPreciseCallState) - && mPreciseCallState == s.mPreciseCallState - && mNetworkType == s.mNetworkType - && Objects.equals(mCallQuality, s.mCallQuality) - && mCallClassification == s.mCallClassification - && Objects.equals(mImsCallId, s.mImsCallId) - && mImsCallType == s.mImsCallType - && mImsCallServiceType == s.mImsCallServiceType); - } - - /** - * {@link Parcelable#describeContents} - */ - public int describeContents() { - return 0; - } - - /** - * {@link Parcelable#writeToParcel} - */ - public void writeToParcel(@Nullable Parcel dest, int flags) { - dest.writeInt(mPreciseCallState); - dest.writeInt(mNetworkType); - dest.writeParcelable(mCallQuality, flags); - dest.writeInt(mCallClassification); - dest.writeString(mImsCallId); - dest.writeInt(mImsCallServiceType); - dest.writeInt(mImsCallType); - } - - public static final @NonNull Creator<CallState> CREATOR = new Creator() { - public CallState createFromParcel(Parcel in) { - return new CallState(in); - } - - public CallState[] newArray(int size) { - return new CallState[size]; - } - }; - - /** - * Builder of {@link CallState} - * - * <p>The example below shows how you might create a new {@code CallState}: - * - * <pre><code> - * - * CallState = new CallState.Builder() - * .setCallState(3) - * .setNetworkType({@link TelephonyManager#NETWORK_TYPE_LTE}) - * .setCallQuality({@link CallQuality}) - * .setImsCallSessionId({@link String}) - * .setImsCallServiceType({@link ImsCallProfile#SERVICE_TYPE_NORMAL}) - * .setImsCallType({@link ImsCallProfile#CALL_TYPE_VOICE}) - * .build(); - * </code></pre> - */ - public static final class Builder { - private @PreciseCallStates int mPreciseCallState; - private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; - private CallQuality mCallQuality = null; - private int mCallClassification = CALL_CLASSIFICATION_UNKNOWN; - private String mImsCallId; - private @ImsCallServiceType int mImsCallServiceType = ImsCallProfile.SERVICE_TYPE_NONE; - private @ImsCallType int mImsCallType = ImsCallProfile.CALL_TYPE_NONE; - - - /** - * Default constructor for the Builder. - */ - public Builder(@PreciseCallStates int preciseCallState) { - mPreciseCallState = preciseCallState; - } - - /** - * Set network type of this call. - * - * @param networkType the transport type. - * @return The same instance of the builder. - */ - @NonNull - public CallState.Builder setNetworkType(@NetworkType int networkType) { - this.mNetworkType = networkType; - return this; - } - - /** - * Set the call quality {@link CallQuality} of this call. - * - * @param callQuality call quality of active call. - * @return The same instance of the builder. - */ - @NonNull - public CallState.Builder setCallQuality(@Nullable CallQuality callQuality) { - this.mCallQuality = callQuality; - return this; - } - - /** - * Set call classification for this call. - * - * @param classification call classification type defined in this class. - * @return The same instance of the builder. - * @hide - */ - @NonNull - public CallState.Builder setCallClassification(int classification) { - this.mCallClassification = classification; - return this; - } - - /** - * Set IMS call session ID of this call. - * - * @param imsCallId IMS call session ID. - * @return The same instance of the builder. - */ - @NonNull - public CallState.Builder setImsCallSessionId(@Nullable String imsCallId) { - this.mImsCallId = imsCallId; - return this; - } - - /** - * Set IMS call service type of this call. - * - * @param serviceType IMS call service type defined in {@link ImsCallProfile}. - * @return The same instance of the builder. - */ - @NonNull - public CallState.Builder setImsCallServiceType(@ImsCallServiceType int serviceType) { - this.mImsCallServiceType = serviceType; - return this; - } - - /** - * Set IMS call type of this call. - * - * @param callType IMS call type defined in {@link ImsCallProfile}. - * @return The same instance of the builder. - */ - @NonNull - public CallState.Builder setImsCallType(@ImsCallType int callType) { - this.mImsCallType = callType; - return this; - } - - /** - * Build the {@link CallState} - * - * @return the {@link CallState} object - */ - @NonNull - public CallState build() { - return new CallState( - mPreciseCallState, - mNetworkType, - mCallQuality, - mCallClassification, - mImsCallId, - mImsCallServiceType, - mImsCallType); - } - } -} diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 1ea7fdc982a5..e6d7df34f755 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -78,9 +78,8 @@ public final class ImsCallProfile implements Parcelable { public static final int SERVICE_TYPE_EMERGENCY = 2; /** - * Call type none + * Call types */ - public static final int CALL_TYPE_NONE = 0; /** * IMSPhone to support IR.92 & IR.94 (voice + video upgrade/downgrade) */ diff --git a/tools/fonts/font-scaling-array-generator.js b/tools/fonts/font-scaling-array-generator.js new file mode 100644 index 000000000000..9754697bfb51 --- /dev/null +++ b/tools/fonts/font-scaling-array-generator.js @@ -0,0 +1,195 @@ +/* + * 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. + */ + +/** + Generates arrays for non-linear font scaling, to be pasted into + frameworks/base/core/java/android/content/res/FontScaleConverterFactory.java + + To use: + `node font-scaling-array-generator.js` + or just open a browser, open DevTools, and paste into the Console. +*/ + +/** + * Modify this to match your packages/apps/Settings/res/arrays.xml#entryvalues_font_size + * array so that all possible scales are generated. + */ +const scales = [1.15, 1.30, 1.5, 1.8, 2]; + +const commonSpSizes = [8, 10, 12, 14, 18, 20, 24, 30, 100]; + +/** + * Enum for GENERATION_STYLE which determines how to generate the arrays. + */ +const GenerationStyle = { + /** + * Interpolates between hand-tweaked curves. This is the best option and + * shouldn't require any additional tweaking. + */ + CUSTOM_TWEAKED: 'CUSTOM_TWEAKED', + + /** + * Uses a curve equation that is mostly correct, but will need manual tweaking + * at some scales. + */ + CURVE: 'CURVE', + + /** + * Uses straight linear multiplication. Good starting point for manual + * tweaking. + */ + LINEAR: 'LINEAR' +} + +/** + * Determines how arrays are generated. Must be one of the GenerationStyle + * values. + */ +const GENERATION_STYLE = GenerationStyle.CUSTOM_TWEAKED; + +// These are hand-tweaked curves from which we will derive the other +// interstitial curves using linear interpolation, in the case of using +// GenerationStyle.CUSTOM_TWEAKED. +const interpolationTargets = { + 1.0: commonSpSizes, + 1.5: [12, 15, 18, 22, 24, 26, 28, 30, 100], + 2.0: [16, 20, 24, 26, 30, 34, 36, 38, 100] +}; + +/** + * Interpolate a value with specified extrema, to a new value between new + * extrema. + * + * @param value the current value + * @param inputMin minimum the input value can reach + * @param inputMax maximum the input value can reach + * @param outputMin minimum the output value can reach + * @param outputMax maximum the output value can reach + */ +function map(value, inputMin, inputMax, outputMin, outputMax) { + return outputMin + (outputMax - outputMin) * ((value - inputMin) / (inputMax - inputMin)); +} + +/*** + * Interpolate between values a and b. + */ +function lerp(a, b, fraction) { + return (a * (1.0 - fraction)) + (b * fraction); +} + +function generateRatios(scale) { + // Find the best two arrays to interpolate between. + let startTarget, endTarget; + let startTargetScale, endTargetScale; + const targetScales = Object.keys(interpolationTargets).sort(); + for (let i = 0; i < targetScales.length - 1; i++) { + const targetScaleKey = targetScales[i]; + const targetScale = parseFloat(targetScaleKey, 10); + const startTargetScaleKey = targetScaleKey; + const endTargetScaleKey = targetScales[i + 1]; + + if (scale < parseFloat(startTargetScaleKey, 10)) { + break; + } + + startTargetScale = parseFloat(startTargetScaleKey, 10); + endTargetScale = parseFloat(endTargetScaleKey, 10); + startTarget = interpolationTargets[startTargetScaleKey]; + endTarget = interpolationTargets[endTargetScaleKey]; + } + const interpolationProgress = map(scale, startTargetScale, endTargetScale, 0, 1); + + return commonSpSizes.map((sp, i) => { + const originalSizeDp = sp; + let newSizeDp; + switch (GENERATION_STYLE) { + case GenerationStyle.CUSTOM_TWEAKED: + newSizeDp = lerp(startTarget[i], endTarget[i], interpolationProgress); + break; + case GenerationStyle.CURVE: { + let coeff1; + let coeff2; + if (scale < 1) { + // \left(1.22^{-\left(x+5\right)}+0.5\right)\cdot x + coeff1 = -5; + coeff2 = scale; + } else { + // (1.22^{-\left(x-10\right)}+1\right)\cdot x + coeff1 = map(scale, 1, 2, 2, 8); + coeff2 = 1; + } + newSizeDp = ((Math.pow(1.22, (-(originalSizeDp - coeff1))) + coeff2) * originalSizeDp); + break; + } + case GenerationStyle.LINEAR: + newSizeDp = originalSizeDp * scale; + break; + default: + throw new Error('Invalid GENERATION_STYLE'); + } + return { + fromSp: sp, + toDp: newSizeDp + } + }); +} + +const scaleArrays = + scales + .map(scale => { + const scaleString = (scale * 100).toFixed(0); + return { + scale, + name: `font_size_original_sp_to_scaled_dp_${scaleString}_percent` + } + }) + .map(scaleArray => { + const items = generateRatios(scaleArray.scale); + + return { + ...scaleArray, + items + } + }); + +function formatDigit(d) { + const twoSignificantDigits = Math.round(d * 100) / 100; + return String(twoSignificantDigits).padStart(4, ' '); +} + +console.log( + '' + + scaleArrays.reduce( + (previousScaleArray, currentScaleArray) => { + const itemsFromSp = currentScaleArray.items.map(d => d.fromSp) + .map(formatDigit) + .join('f, '); + const itemsToDp = currentScaleArray.items.map(d => d.toDp) + .map(formatDigit) + .join('f, '); + + return previousScaleArray + ` + put( + /* scaleKey= */ ${currentScaleArray.scale}f, + new FontScaleConverter( + /* fromSp= */ + new float[] {${itemsFromSp}}, + /* toDp= */ + new float[] {${itemsToDp}}) + ); + `; + }, + '')); diff --git a/tools/lint/common/Android.bp b/tools/lint/common/Android.bp new file mode 100644 index 000000000000..898f88b8759c --- /dev/null +++ b/tools/lint/common/Android.bp @@ -0,0 +1,29 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +java_library_host { + name: "AndroidCommonLint", + srcs: ["src/main/java/**/*.kt"], + libs: ["lint_api"], + kotlincflags: ["-Xjvm-default=all"], +} diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt b/tools/lint/common/src/main/java/com/google/android/lint/Constants.kt index 3d5d01c9b7a0..3d5d01c9b7a0 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt +++ b/tools/lint/common/src/main/java/com/google/android/lint/Constants.kt diff --git a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt new file mode 100644 index 000000000000..720f8356f050 --- /dev/null +++ b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt @@ -0,0 +1,36 @@ +/* + * 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.google.android.lint + +import com.android.tools.lint.detector.api.getUMethod +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UMethod +import org.jetbrains.uast.UParameter + +fun isPermissionMethodCall(callExpression: UCallExpression): Boolean { + val method = callExpression.resolve()?.getUMethod() ?: return false + return hasPermissionMethodAnnotation(method) +} + +fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations + .any { + it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD) + } + +fun hasPermissionNameAnnotation(parameter: UParameter) = parameter.annotations.any { + it.hasQualifiedName(ANNOTATION_PERMISSION_NAME) +} diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt b/tools/lint/common/src/main/java/com/google/android/lint/model/Method.kt index 3939b6109eaa..3939b6109eaa 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt +++ b/tools/lint/common/src/main/java/com/google/android/lint/model/Method.kt diff --git a/tools/lint/fix/Android.bp b/tools/lint/fix/Android.bp new file mode 100644 index 000000000000..5f6c6f779f85 --- /dev/null +++ b/tools/lint/fix/Android.bp @@ -0,0 +1,28 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +python_binary_host { + name: "lint_fix", + main: "lint_fix.py", + srcs: ["lint_fix.py"], +} diff --git a/tools/lint/Android.bp b/tools/lint/framework/Android.bp index 96618f413db1..7f27e8a57d19 100644 --- a/tools/lint/Android.bp +++ b/tools/lint/framework/Android.bp @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Android Open Source Project +// 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. @@ -29,6 +29,11 @@ java_library_host { "auto_service_annotations", "lint_api", ], + static_libs: [ + "AndroidCommonLint", + // TODO: remove once b/236558918 is resolved and the below checks actually run globally + "AndroidGlobalLintChecker", + ], kotlincflags: ["-Xjvm-default=all"], } @@ -51,9 +56,3 @@ java_test_host { unit_test: true, }, } - -python_binary_host { - name: "lint_fix", - main: "fix/lint_fix.py", - srcs: ["fix/lint_fix.py"], -} diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt index 413e19717d50..413e19717d50 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt index 0c375c358e61..0c375c358e61 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt index fe567da7c017..fe567da7c017 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt index 48540b1da565..48540b1da565 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt index 1b0f03564c3b..e12ec3d4a77c 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt @@ -26,7 +26,6 @@ import com.android.tools.lint.detector.api.Scope import com.android.tools.lint.detector.api.Severity import com.android.tools.lint.detector.api.SourceCodeScanner import com.android.tools.lint.detector.api.getUMethod -import com.google.android.lint.aidl.hasPermissionMethodAnnotation import com.intellij.psi.PsiType import org.jetbrains.uast.UAnnotation import org.jetbrains.uast.UBlockExpression @@ -193,5 +192,8 @@ class PermissionMethodDetector : Detector(), SourceCodeScanner { else -> false } } + + private fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations + .any { it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD) } } } diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt index c3e0428316c3..c3e0428316c3 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt index 06c098df385d..06c098df385d 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/parcel/CallMigrators.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/parcel/Method.kt index 0826e8e74431..0826e8e74431 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/Method.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/parcel/Method.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt index f92826316be4..f92826316be4 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt +++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/parcel/SaferParcelChecker.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt index d90f3e31baf9..d90f3e31baf9 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt +++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt index e72f38416310..e72f38416310 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt +++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/PackageVisibilityDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PackageVisibilityDetectorTest.kt index a70644ab8532..a70644ab8532 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/PackageVisibilityDetectorTest.kt +++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PackageVisibilityDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt index b76342a81972..b76342a81972 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt +++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt index e686695ca804..e686695ca804 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt +++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/parcel/SaferParcelCheckerTest.kt diff --git a/tools/lint/global/Android.bp b/tools/lint/global/Android.bp new file mode 100644 index 000000000000..3756abea2330 --- /dev/null +++ b/tools/lint/global/Android.bp @@ -0,0 +1,57 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +java_library_host { + name: "AndroidGlobalLintChecker", + srcs: ["checks/src/main/java/**/*.kt"], + plugins: ["auto_service_plugin"], + libs: [ + "auto_service_annotations", + "lint_api", + ], + static_libs: ["AndroidCommonLint"], + kotlincflags: ["-Xjvm-default=all"], + dist: { + targets: ["droid"], + }, +} + +java_test_host { + name: "AndroidGlobalLintCheckerTest", + // TODO(b/239881504): Since this test was written, Android + // Lint was updated, and now includes classes that were + // compiled for java 15. The soong build doesn't support + // java 15 yet, so we can't compile against "lint". Disable + // the test until java 15 is supported. + enabled: false, + srcs: ["checks/src/test/java/**/*.kt"], + static_libs: [ + "AndroidGlobalLintChecker", + "junit", + "lint", + "lint_tests", + ], + test_options: { + unit_test: true, + }, +} diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt new file mode 100644 index 000000000000..b377d503f318 --- /dev/null +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt @@ -0,0 +1,48 @@ +/* + * 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.google.android.lint + +import com.android.tools.lint.client.api.IssueRegistry +import com.android.tools.lint.client.api.Vendor +import com.android.tools.lint.detector.api.CURRENT_API +import com.google.android.lint.aidl.EnforcePermissionDetector +import com.google.android.lint.aidl.EnforcePermissionHelperDetector +import com.google.android.lint.aidl.SimpleManualPermissionEnforcementDetector +import com.google.auto.service.AutoService + +@AutoService(IssueRegistry::class) +@Suppress("UnstableApiUsage") +class AndroidGlobalIssueRegistry : IssueRegistry() { + override val issues = listOf( + EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION, + EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION, + EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER, + SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION, + ) + + override val api: Int + get() = CURRENT_API + + override val minApi: Int + get() = 8 + + override val vendor: Vendor = Vendor( + vendorName = "Android", + feedbackUrl = "http://b/issues/new?component=315013", + contact = "repsonsible-apis@google.com" + ) +}
\ No newline at end of file diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt index 227cdcdc2fec..227cdcdc2fec 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt index 8ee3763e5079..8ee3763e5079 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt index bba819cd9096..bba819cd9096 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt index d120e1d41c99..f1b634898ec9 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt @@ -19,6 +19,8 @@ package com.google.android.lint.aidl import com.android.tools.lint.detector.api.JavaContext import com.android.tools.lint.detector.api.Location import com.android.tools.lint.detector.api.getUMethod +import com.google.android.lint.hasPermissionNameAnnotation +import com.google.android.lint.isPermissionMethodCall import org.jetbrains.kotlin.psi.psiUtil.parameterIndex import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.evaluateString diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt index 3c2ea1db0ad6..3c2ea1db0ad6 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt index edbdd8d2adf3..250ca78bae5e 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt @@ -16,14 +16,9 @@ package com.google.android.lint.aidl -import com.android.tools.lint.detector.api.getUMethod -import com.google.android.lint.ANNOTATION_PERMISSION_METHOD -import com.google.android.lint.ANNOTATION_PERMISSION_NAME import com.google.android.lint.CLASS_STUB import com.intellij.psi.PsiAnonymousClass -import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.UMethod -import org.jetbrains.uast.UParameter /** * Given a UMethod, determine if this method is @@ -51,17 +46,3 @@ private fun isInClassCalledStub(node: UMethod): Boolean { it.referenceName == CLASS_STUB } ?: false } - -fun isPermissionMethodCall(callExpression: UCallExpression): Boolean { - val method = callExpression.resolve()?.getUMethod() ?: return false - return hasPermissionMethodAnnotation(method) -} - -fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations - .any { - it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD) - } - -fun hasPermissionNameAnnotation(parameter: UParameter) = parameter.annotations.any { - it.hasQualifiedName(ANNOTATION_PERMISSION_NAME) -} diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt index 4c0cbe7b3adf..4c0cbe7b3adf 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt index 3c1d1e8e8a59..3c1d1e8e8a59 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt +++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt index 31e484628a04..31e484628a04 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt +++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt index 150fc264506f..150fc264506f 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt +++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt index bd6b1952847c..bd6b1952847c 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt +++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt |