Merge changes from topic "sc_reverts_for_b_183104573" into sc-dev
* changes:
Revert "Correct recycling to take setViewId into account."
Revert "Ensure the view being recycled has been inflated before."
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 2f3ac22..784c63a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -32,7 +32,6 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IUidObserver;
@@ -348,12 +347,20 @@
private final SparseBooleanArray mTempAllowlistCache = new SparseBooleanArray();
/**
- * Mapping of app IDs to the when their temp allowlist grace period ends (in the elapsed
+ * Mapping of UIDs to the when their temp allowlist grace period ends (in the elapsed
* realtime timebase).
*/
private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray();
- private final ActivityManagerInternal mActivityManagerInternal;
+ /** Current set of UIDs in the {@link ActivityManager#PROCESS_STATE_TOP} state. */
+ private final SparseBooleanArray mTopAppCache = new SparseBooleanArray();
+
+ /**
+ * Mapping of UIDs to the when their top app grace period ends (in the elapsed realtime
+ * timebase).
+ */
+ private final SparseLongArray mTopAppGraceCache = new SparseLongArray();
+
private final AlarmManager mAlarmManager;
private final ChargingTracker mChargeTracker;
private final QcHandler mHandler;
@@ -412,7 +419,7 @@
}
};
- private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ private class QcUidObserver extends IUidObserver.Stub {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget();
@@ -433,7 +440,7 @@
@Override
public void onUidCachedChanged(int uid, boolean cached) {
}
- };
+ }
private final BroadcastReceiver mPackageAddedReceiver = new BroadcastReceiver() {
@Override
@@ -548,8 +555,10 @@
*/
private long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS;
- private long mEJTempAllowlistGracePeriodMs =
- QcConstants.DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS;
+ private long mEJGracePeriodTempAllowlistMs =
+ QcConstants.DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS;
+
+ private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
/** The package verifier app. */
@Nullable
@@ -586,7 +595,6 @@
mHandler = new QcHandler(mContext.getMainLooper());
mChargeTracker = new ChargingTracker();
mChargeTracker.startTracking();
- mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mQcConstants = new QcConstants();
mBackgroundJobsController = backgroundJobsController;
@@ -606,9 +614,12 @@
pai.registerTempAllowlistChangeListener(new TempAllowlistTracker());
try {
- ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.getService().registerUidObserver(new QcUidObserver(),
ActivityManager.UID_OBSERVER_PROCSTATE,
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null);
+ ActivityManager.getService().registerUidObserver(new QcUidObserver(),
+ ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_TOP, null);
} catch (RemoteException e) {
// ignored; both services live in system_server
}
@@ -658,7 +669,7 @@
}
final int uid = jobStatus.getSourceUid();
- if (mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_TOP) {
+ if (mTopAppCache.get(uid)) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
@@ -715,6 +726,8 @@
mUidToPackageCache.remove(uid);
mTempAllowlistCache.delete(uid);
mTempAllowlistGraceCache.delete(uid);
+ mTopAppCache.delete(uid);
+ mTopAppGraceCache.delete(uid);
}
@Override
@@ -770,14 +783,10 @@
/** Returns the maximum amount of time this job could run for. */
public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) {
- // Need to look at current proc state as well in the case where the job hasn't started yet.
- final boolean isTop = mActivityManagerInternal
- .getUidProcessState(jobStatus.getSourceUid()) <= ActivityManager.PROCESS_STATE_TOP;
-
if (!jobStatus.shouldTreatAsExpeditedJob()) {
// If quota is currently "free", then the job can run for the full amount of time.
if (mChargeTracker.isCharging()
- || isTop
+ || mTopAppCache.get(jobStatus.getSourceUid())
|| isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
@@ -790,7 +799,7 @@
if (mChargeTracker.isCharging()) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
- if (isTop || isTopStartedJobLocked(jobStatus)) {
+ if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) {
return Math.max(mEJLimitsMs[ACTIVE_INDEX] / 2,
getTimeUntilEJQuotaConsumedLocked(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()));
@@ -817,14 +826,23 @@
if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
return true;
}
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
final long tempAllowlistGracePeriodEndElapsed =
mTempAllowlistGraceCache.get(jobStatus.getSourceUid());
final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid())
- || sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed;
+ || nowElapsed < tempAllowlistGracePeriodEndElapsed;
if (hasTempAllowlistExemption) {
return true;
}
+ final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(jobStatus.getSourceUid());
+ final boolean hasTopAppExemption = mTopAppCache.get(jobStatus.getSourceUid())
+ || nowElapsed < topAppGracePeriodEndElapsed;
+ if (hasTopAppExemption) {
+ return true;
+ }
+
Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
// Any already executing expedited jobs should be allowed to finish.
@@ -2101,8 +2119,12 @@
final boolean hasTempAllowlistExemption = !mRegularJobTimer
&& (mTempAllowlistCache.get(mUid)
|| nowElapsed < tempAllowlistGracePeriodEndElapsed);
+ final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid);
+ final boolean hasTopAppExemption = !mRegularJobTimer
+ && (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed);
return (standbyBucket == RESTRICTED_INDEX || !mChargeTracker.isCharging())
- && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption;
+ && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption
+ && !hasTopAppExemption;
}
void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) {
@@ -2421,11 +2443,11 @@
public void onAppRemoved(int uid) {
synchronized (mLock) {
final long nowElapsed = sElapsedRealtimeClock.millis();
- final long endElapsed = nowElapsed + mEJTempAllowlistGracePeriodMs;
+ final long endElapsed = nowElapsed + mEJGracePeriodTempAllowlistMs;
mTempAllowlistCache.delete(uid);
mTempAllowlistGraceCache.put(uid, endElapsed);
Message msg = mHandler.obtainMessage(MSG_END_GRACE_PERIOD, uid, 0);
- mHandler.sendMessageDelayed(msg, mEJTempAllowlistGracePeriodMs);
+ mHandler.sendMessageDelayed(msg, mEJGracePeriodTempAllowlistMs);
}
}
}
@@ -2571,12 +2593,37 @@
synchronized (mLock) {
boolean isQuotaFree;
- if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+ mTopAppCache.put(uid, true);
+ mTopAppGraceCache.delete(uid);
+ if (mForegroundUids.get(uid)) {
+ // Went from FGS to TOP. We don't need to reprocess timers or
+ // jobs.
+ break;
+ }
mForegroundUids.put(uid, true);
isQuotaFree = true;
} else {
- mForegroundUids.delete(uid);
- isQuotaFree = false;
+ final boolean reprocess;
+ if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ reprocess = !mForegroundUids.get(uid);
+ mForegroundUids.put(uid, true);
+ isQuotaFree = true;
+ } else {
+ reprocess = true;
+ mForegroundUids.delete(uid);
+ isQuotaFree = false;
+ }
+ if (mTopAppCache.get(uid)) {
+ final long endElapsed = nowElapsed + mEJGracePeriodTopAppMs;
+ mTopAppCache.delete(uid);
+ mTopAppGraceCache.put(uid, endElapsed);
+ sendMessageDelayed(obtainMessage(MSG_END_GRACE_PERIOD, uid, 0),
+ mEJGracePeriodTopAppMs);
+ }
+ if (!reprocess) {
+ break;
+ }
}
// Update Timers first.
if (mPkgTimers.indexOfKey(userId) >= 0
@@ -2644,20 +2691,31 @@
case MSG_END_GRACE_PERIOD: {
final int uid = msg.arg1;
synchronized (mLock) {
- if (mTempAllowlistCache.get(uid)) {
- // App added back to the temp allowlist during the grace period.
+ if (mTempAllowlistCache.get(uid) || mTopAppCache.get(uid)) {
+ // App added back to the temp allowlist or became top again
+ // during the grace period.
if (DEBUG) {
Slog.d(TAG, uid + " is still allowed");
}
break;
}
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (nowElapsed < mTempAllowlistGraceCache.get(uid)
+ || nowElapsed < mTopAppGraceCache.get(uid)) {
+ // One of the grace periods is still in effect.
+ if (DEBUG) {
+ Slog.d(TAG, uid + " is still in grace period");
+ }
+ break;
+ }
if (DEBUG) {
Slog.d(TAG, uid + " is now out of grace period");
}
+ mTempAllowlistGraceCache.delete(uid);
+ mTopAppGraceCache.delete(uid);
final ArraySet<String> packages = getPackagesForUidLocked(uid);
if (packages != null) {
final int userId = UserHandle.getUserId(uid);
- final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = packages.size() - 1; i >= 0; --i) {
Timer t = mEJPkgTimers.get(userId, packages.valueAt(i));
if (t != null) {
@@ -2989,8 +3047,11 @@
static final String KEY_EJ_REWARD_NOTIFICATION_SEEN_MS =
QC_CONSTANT_PREFIX + "ej_reward_notification_seen_ms";
@VisibleForTesting
- static final String KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS =
- QC_CONSTANT_PREFIX + "ej_temp_allowlist_grace_period_ms";
+ static final String KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS =
+ QC_CONSTANT_PREFIX + "ej_grace_period_temp_allowlist_ms";
+ @VisibleForTesting
+ static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
+ QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
10 * 60 * 1000L; // 10 minutes
@@ -3043,7 +3104,8 @@
private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS;
private static final long DEFAULT_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS;
private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0;
- private static final long DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = 3 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS;
/** How much time each app will have to run jobs within their standby bucket window. */
public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
@@ -3275,7 +3337,12 @@
* How much additional grace period to add to the end of an app's temp allowlist
* duration.
*/
- public long EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS;
+ public long EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS;
+
+ /**
+ * How much additional grace period to give an app when it leaves the TOP state.
+ */
+ public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
@@ -3470,13 +3537,21 @@
mEJRewardNotificationSeenMs = Math.min(5 * MINUTE_IN_MILLIS,
Math.max(0, EJ_REWARD_NOTIFICATION_SEEN_MS));
break;
- case KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS:
+ case KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
- EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS =
- properties.getLong(key, DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS);
+ EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS =
+ properties.getLong(key, DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS);
// Limit grace period to be in the range [0 minutes, 1 hour].
- mEJTempAllowlistGracePeriodMs = Math.min(HOUR_IN_MILLIS,
- Math.max(0, EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS));
+ mEJGracePeriodTempAllowlistMs = Math.min(HOUR_IN_MILLIS,
+ Math.max(0, EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS));
+ break;
+ case KEY_EJ_GRACE_PERIOD_TOP_APP_MS:
+ // We don't need to re-evaluate execution stats or constraint status for this.
+ EJ_GRACE_PERIOD_TOP_APP_MS =
+ properties.getLong(key, DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS);
+ // Limit grace period to be in the range [0 minutes, 1 hour].
+ mEJGracePeriodTopAppMs = Math.min(HOUR_IN_MILLIS,
+ Math.max(0, EJ_GRACE_PERIOD_TOP_APP_MS));
break;
}
}
@@ -3739,8 +3814,9 @@
pw.print(KEY_EJ_REWARD_TOP_APP_MS, EJ_REWARD_TOP_APP_MS).println();
pw.print(KEY_EJ_REWARD_INTERACTION_MS, EJ_REWARD_INTERACTION_MS).println();
pw.print(KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, EJ_REWARD_NOTIFICATION_SEEN_MS).println();
- pw.print(KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS,
- EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS).println();
+ pw.print(KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS,
+ EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println();
+ pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println();
pw.decreaseIndent();
}
@@ -3853,6 +3929,16 @@
}
@VisibleForTesting
+ long getEJGracePeriodTempAllowlistMs() {
+ return mEJGracePeriodTempAllowlistMs;
+ }
+
+ @VisibleForTesting
+ long getEJGracePeriodTopAppMs() {
+ return mEJGracePeriodTopAppMs;
+ }
+
+ @VisibleForTesting
@NonNull
long[] getEJLimitsMs() {
return mEJLimitsMs;
@@ -3888,11 +3974,6 @@
}
@VisibleForTesting
- long getEJTempAllowlistGracePeriodMs() {
- return mEJTempAllowlistGracePeriodMs;
- }
-
- @VisibleForTesting
@Nullable
List<TimingSession> getEJTimingSessions(int userId, String packageName) {
return mEJTimingSessions.get(userId, packageName);
@@ -3964,6 +4045,17 @@
pw.println(mForegroundUids.toString());
pw.println();
+ pw.print("Cached top apps: ");
+ pw.println(mTopAppCache.toString());
+ pw.print("Cached top app grace period: ");
+ pw.println(mTopAppGraceCache.toString());
+
+ pw.print("Cached temp allowlist: ");
+ pw.println(mTempAllowlistCache.toString());
+ pw.print("Cached temp allowlist grace period: ");
+ pw.println(mTempAllowlistGraceCache.toString());
+ pw.println();
+
pw.println("Cached UID->package map:");
pw.increaseIndent();
for (int i = 0; i < mUidToPackageCache.size(); ++i) {
@@ -3975,12 +4067,6 @@
pw.decreaseIndent();
pw.println();
- pw.print("Cached temp allowlist: ");
- pw.println(mTempAllowlistCache.toString());
- pw.print("Cached temp allowlist grace period: ");
- pw.println(mTempAllowlistGraceCache.toString());
-
- pw.println();
mTrackedJobs.forEach((jobs) -> {
for (int j = 0; j < jobs.size(); j++) {
final JobStatus js = jobs.valueAt(j);
diff --git a/core/api/current.txt b/core/api/current.txt
index 8bc603d..471f610 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34828,6 +34828,7 @@
field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS";
field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
+ field public static final String ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA";
field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE";
field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
@@ -40316,7 +40317,6 @@
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
method public void notifyConfigChangedForSubId(int);
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
- field public static final int CARRIER_NR_AVAILABILITY_NONE = 0; // 0x0
field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0; // 0x0
@@ -40381,7 +40381,7 @@
field public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int";
field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
- field public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int";
+ field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array";
field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool";
field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
@@ -40626,7 +40626,10 @@
public static final class CarrierConfigManager.ImsServiceEntitlement {
field public static final String KEY_ENTITLEMENT_SERVER_URL_STRING = "imsserviceentitlement.entitlement_server_url_string";
+ field public static final String KEY_FCM_SENDER_ID_STRING = "imsserviceentitlement.fcm_sender_id_string";
+ field public static final String KEY_IMS_PROVISIONING_BOOL = "imsserviceentitlement.ims_provisioning_bool";
field public static final String KEY_PREFIX = "imsserviceentitlement.";
+ field public static final String KEY_SHOW_VOWIFI_WEBVIEW_BOOL = "imsserviceentitlement.show_vowifi_webview_bool";
}
public static final class CarrierConfigManager.Iwlan {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fe8566f..e49ef2c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -202,7 +202,6 @@
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
- field public static final String READ_NETWORK_DEVICE_CONFIG = "android.permission.READ_NETWORK_DEVICE_CONFIG";
field public static final String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
field public static final String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
field public static final String READ_PEOPLE_DATA = "android.permission.READ_PEOPLE_DATA";
@@ -911,7 +910,6 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
- method public boolean isNetworkSlicingEnabledForUser(@NonNull android.os.UserHandle);
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8497427..a97c3ae 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1033,6 +1033,14 @@
method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public String getUiPackage();
}
+ public class BiometricPrompt {
+ method @NonNull public java.util.List<java.lang.Integer> getAllowedSensorIds();
+ }
+
+ public static class BiometricPrompt.Builder {
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowedSensorIds(@NonNull java.util.List<java.lang.Integer>);
+ }
+
public class BiometricTestSession implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void acceptAuthentication(int);
method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void cleanupInternalState(int);
@@ -1045,6 +1053,7 @@
}
public class SensorProperties {
+ method @NonNull public java.util.List<android.hardware.biometrics.SensorProperties.ComponentInfo> getComponentInfo();
method public int getSensorId();
method public int getSensorStrength();
field public static final int STRENGTH_CONVENIENCE = 0; // 0x0
@@ -1052,6 +1061,14 @@
field public static final int STRENGTH_WEAK = 1; // 0x1
}
+ public static final class SensorProperties.ComponentInfo {
+ method @NonNull public String getComponentId();
+ method @NonNull public String getFirmwareVersion();
+ method @NonNull public String getHardwareVersion();
+ method @NonNull public String getSerialNumber();
+ method @NonNull public String getSoftwareVersion();
+ }
+
}
package android.hardware.camera2 {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9dcb237..60506b5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2684,7 +2684,17 @@
if (headsUpContentView != null) headsUpContentView.visitUris(visitor);
if (extras != null) {
- visitor.accept(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
+ // NOTE: The documentation of EXTRA_AUDIO_CONTENTS_URI explicitly says that it is a
+ // String representation of a Uri, but the previous implementation (and unit test) of
+ // this method has always treated it as a Uri object. Given the inconsistency,
+ // supporting both going forward is the safest choice.
+ Object audioContentsUri = extras.get(EXTRA_AUDIO_CONTENTS_URI);
+ if (audioContentsUri instanceof Uri) {
+ visitor.accept((Uri) audioContentsUri);
+ } else if (audioContentsUri instanceof String) {
+ visitor.accept(Uri.parse((String) audioContentsUri));
+ }
+
if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) {
visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI)));
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d4d3321..594b005 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10023,37 +10023,6 @@
}
/**
- * Indicates whether 5g slicing is enabled for specific user.
- *
- * This method can be called with permission
- * {@link android.Manifest.permission#READ_NETWORK_DEVICE_CONFIG} by the profile owner of
- * a managed profile. And the caller must hold the
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission if query for
- * other users.
- *
- * @param userHandle indicates the user to query the state
- * @return indicates whether 5g Slice is enabled.
- * @throws SecurityException if the caller is not granted the permission
- * {@link android.Manifest.permission#READ_NETWORK_DEVICE_CONFIG}
- * and not profile owner of a managed profile, and not granted the permission
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if query for
- * other users.
- * @hide
- */
- @SystemApi
- public boolean isNetworkSlicingEnabledForUser(@NonNull UserHandle userHandle) {
- throwIfParentInstance("isNetworkSlicingEnabledForUser");
- if (mService == null) {
- return false;
- }
- try {
- return mService.isNetworkSlicingEnabled(userHandle.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* This method is mostly deprecated.
* Most of the settings that still have an effect have dedicated setter methods or user
* restrictions. See individual settings for details.
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index c0c1aa1..794b512 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -29,6 +29,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
/**
* Represents a scan record from Bluetooth LE scan.
@@ -168,6 +169,27 @@
return mBytes;
}
+ /**
+ * Test if any fields contained inside this scan record are matched by the
+ * given matcher.
+ *
+ * @hide
+ */
+ public boolean matchesAnyField(@NonNull Predicate<byte[]> matcher) {
+ int pos = 0;
+ while (pos < mBytes.length) {
+ final int length = mBytes[pos] & 0xFF;
+ if (length == 0) {
+ break;
+ }
+ if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) {
+ return true;
+ }
+ pos += length + 1;
+ }
+ return false;
+ }
+
private ScanRecord(List<ParcelUuid> serviceUuids,
List<ParcelUuid> serviceSolicitationUuids,
SparseArray<byte[]> manufacturerData,
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 29edd40..ba6416d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -34,6 +34,7 @@
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.os.Bundle;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -89,7 +90,7 @@
ParsingPackage addReqFeature(FeatureInfo reqFeature);
- ParsingPackage addRequestedPermission(String permission);
+ ParsingPackage addUsesPermission(ParsedUsesPermission parsedUsesPermission);
ParsingPackage addService(ParsedService parsedService);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 067787d..b3c26ab 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -44,6 +44,7 @@
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
@@ -71,6 +72,7 @@
import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
import java.security.PublicKey;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -227,8 +229,8 @@
protected List<String> adoptPermissions = emptyList();
@NonNull
- @DataClass.ParcelWith(ForInternedStringList.class)
- private List<String> requestedPermissions = emptyList();
+ private List<ParsedUsesPermission> usesPermissions = emptyList();
+
@NonNull
@DataClass.ParcelWith(ForInternedStringList.class)
private List<String> implicitPermissions = emptyList();
@@ -691,9 +693,8 @@
}
@Override
- public ParsingPackageImpl addRequestedPermission(String permission) {
- this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
- TextUtils.safeIntern(permission));
+ public ParsingPackageImpl addUsesPermission(ParsedUsesPermission permission) {
+ this.usesPermissions = CollectionUtils.add(this.usesPermissions, permission);
return this;
}
@@ -1134,7 +1135,7 @@
dest.writeByteArray(this.restrictUpdateHash);
dest.writeStringList(this.originalPackages);
sForInternedStringList.parcel(this.adoptPermissions, dest, flags);
- sForInternedStringList.parcel(this.requestedPermissions, dest, flags);
+ dest.writeTypedList(this.usesPermissions);
sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
sForStringSet.parcel(this.upgradeKeySets, dest, flags);
dest.writeMap(this.keySetMapping);
@@ -1255,7 +1256,7 @@
this.restrictUpdateHash = in.createByteArray();
this.originalPackages = in.createStringArrayList();
this.adoptPermissions = sForInternedStringList.unparcel(in);
- this.requestedPermissions = sForInternedStringList.unparcel(in);
+ this.usesPermissions = in.createTypedArrayList(ParsedUsesPermission.CREATOR);
this.implicitPermissions = sForInternedStringList.unparcel(in);
this.upgradeKeySets = sForStringSet.unparcel(in);
this.keySetMapping = in.readHashMap(boot);
@@ -1551,11 +1552,23 @@
@NonNull
@Override
public List<String> getRequestedPermissions() {
+ final List<ParsedUsesPermission> usesPermissions = getUsesPermissions();
+ final int size = usesPermissions.size();
+ final List<String> requestedPermissions = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ requestedPermissions.add(usesPermissions.get(i).name);
+ }
return requestedPermissions;
}
@NonNull
@Override
+ public List<ParsedUsesPermission> getUsesPermissions() {
+ return usesPermissions;
+ }
+
+ @NonNull
+ @Override
public List<String> getImplicitPermissions() {
return implicitPermissions;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index f7f3e19..9f52183 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -37,6 +37,7 @@
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -45,6 +46,7 @@
import android.util.SparseIntArray;
import java.security.PublicKey;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -193,6 +195,14 @@
List<FeatureInfo> getReqFeatures();
/**
+ * @deprecated consider migrating to {@link #getUsesPermissions} which has
+ * more parsed details, such as flags
+ */
+ @NonNull
+ @Deprecated
+ List<String> getRequestedPermissions();
+
+ /**
* All the permissions declared. This is an effective set, and may include permissions
* transformed from split/migrated permissions from previous versions, so may not be exactly
* what the package declares in its manifest.
@@ -200,7 +210,7 @@
* @see R.styleable#AndroidManifestUsesPermission
*/
@NonNull
- List<String> getRequestedPermissions();
+ List<ParsedUsesPermission> getUsesPermissions();
/**
* Returns the properties set on the application
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0c033fd..2be0157 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -67,6 +67,7 @@
import android.content.pm.parsing.component.ParsedProviderUtils;
import android.content.pm.parsing.component.ParsedService;
import android.content.pm.parsing.component.ParsedServiceUtils;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
@@ -119,6 +120,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
@@ -1206,6 +1208,10 @@
requiredNotFeatures.add(feature);
}
+ final int usesPermissionFlags = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_usesPermissionFlags,
+ 0);
+
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1270,14 +1276,31 @@
}
}
- if (!pkg.getRequestedPermissions().contains(name)) {
- pkg.addRequestedPermission(name.intern());
- } else {
- Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
- + name + " in package: " + pkg.getPackageName() + " at: "
- + parser.getPositionDescription());
+ // Quietly ignore duplicate permission requests, but fail loudly if
+ // the two requests have conflicting flags
+ boolean found = false;
+ final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
+ final int size = usesPermissions.size();
+ for (int i = 0; i < size; i++) {
+ final ParsedUsesPermission usesPermission = usesPermissions.get(i);
+ if (Objects.equals(usesPermission.name, name)) {
+ if (usesPermission.usesPermissionFlags != usesPermissionFlags) {
+ return input.error("Conflicting uses-permissions flags: "
+ + name + " in package: " + pkg.getPackageName() + " at: "
+ + parser.getPositionDescription());
+ } else {
+ Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+ + name + " in package: " + pkg.getPackageName() + " at: "
+ + parser.getPositionDescription());
+ }
+ found = true;
+ break;
+ }
}
+ if (!found) {
+ pkg.addUsesPermission(new ParsedUsesPermission(name, usesPermissionFlags));
+ }
return success;
} finally {
sa.recycle();
@@ -2755,7 +2778,7 @@
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
- pkg.addRequestedPermission(npi.name)
+ pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0))
.addImplicitPermission(npi.name);
}
}
@@ -2777,7 +2800,7 @@
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!requestedPermissions.contains(perm)) {
- pkg.addRequestedPermission(perm)
+ pkg.addUsesPermission(new ParsedUsesPermission(perm, 0))
.addImplicitPermission(perm);
}
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
new file mode 100644
index 0000000..b9c2e36
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 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.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestUsesPermission
+ * <uses-permission>} tag parsed from the manifest.
+ *
+ * @hide
+ */
+public class ParsedUsesPermission implements Parcelable {
+ /** Name of the permission requested */
+ public @NonNull String name;
+
+ /** Set of flags that should apply to this permission request. */
+ public @UsesPermissionFlags int usesPermissionFlags;
+
+ /**
+ * Strong assertion by a developer that they will never use this permission
+ * to derive the physical location of the device, regardless of
+ * ACCESS_FINE_LOCATION and/or ACCESS_COARSE_LOCATION being granted.
+ */
+ public static final int FLAG_NEVER_FOR_LOCATION = 0x1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_NEVER_FOR_LOCATION
+ })
+ public @interface UsesPermissionFlags {}
+
+ public ParsedUsesPermission(@NonNull String name,
+ @UsesPermissionFlags int usesPermissionFlags) {
+ this.name = name.intern();
+ this.usesPermissionFlags = usesPermissionFlags;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ sForInternedString.parcel(this.name, dest, flags);
+ dest.writeInt(usesPermissionFlags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ protected ParsedUsesPermission(@NonNull Parcel in) {
+ this.name = sForInternedString.unparcel(in);
+ this.usesPermissionFlags = in.readInt();
+ }
+
+ public static final @NonNull Parcelable.Creator<ParsedUsesPermission> CREATOR
+ = new Parcelable.Creator<ParsedUsesPermission>() {
+ @Override
+ public ParsedUsesPermission[] newArray(int size) {
+ return new ParsedUsesPermission[size];
+ }
+
+ @Override
+ public ParsedUsesPermission createFromParcel(@NonNull Parcel in) {
+ return new ParsedUsesPermission(in);
+ }
+ };
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index acfad13..304b2af 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -48,13 +48,6 @@
private static final String TAG = "BiometricManager";
/**
- * An ID that should match any biometric sensor on the device.
- *
- * @hide
- */
- public static final int SENSOR_ID_ANY = -1;
-
- /**
* No error detected.
*/
public static final int BIOMETRIC_SUCCESS =
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 4f6a7c7..1258247 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -16,6 +16,7 @@
package android.hardware.biometrics;
+import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
@@ -25,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.DialogInterface;
import android.hardware.face.FaceManager;
@@ -45,6 +47,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.Signature;
+import java.util.List;
import java.util.concurrent.Executor;
import javax.crypto.Cipher;
@@ -339,6 +342,32 @@
}
/**
+ * If non-empty, requests authentication to be performed only if the sensor is contained
+ * within the list. Note that the actual sensor presented to the user/test will meet all
+ * constraints specified within this builder. For example, on a device with the below
+ * configuration:
+ *
+ * SensorId: 1, Strength: BIOMETRIC_STRONG
+ * SensorId: 2, Strength: BIOMETRIC_WEAK
+ *
+ * If authentication is invoked with setAllowedAuthenticators(BIOMETRIC_STRONG) and
+ * setAllowedSensorIds(2), then no sensor will be eligible for authentication.
+ *
+ * @see {@link BiometricManager#getSensorProperties()}
+ *
+ * @param sensorIds Sensor IDs to constrain this authentication to.
+ * @return This builder
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
+ public Builder setAllowedSensorIds(@NonNull List<Integer> sensorIds) {
+ mPromptInfo.setAllowedSensorIds(sensorIds);
+ return this;
+ }
+
+ /**
* If set check the Device Policy Manager for disabled biometrics.
*
* @param checkDevicePolicyManager
@@ -364,21 +393,6 @@
}
/**
- * If set, authenticate using the biometric sensor with the given ID.
- *
- * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default).
- * @return This builder.
- *
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- @NonNull
- public Builder setSensorId(int sensorId) {
- mPromptInfo.setSensorId(sensorId);
- return this;
- }
-
- /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
@@ -596,6 +610,16 @@
}
/**
+ * @return The values set by {@link Builder#setAllowedSensorIds(List)}
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<Integer> getAllowedSensorIds() {
+ return mPromptInfo.getAllowedSensorIds();
+ }
+
+ /**
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
*
* <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
diff --git a/core/java/android/hardware/biometrics/ComponentInfoInternal.aidl b/core/java/android/hardware/biometrics/ComponentInfoInternal.aidl
new file mode 100644
index 0000000..0c780cc
--- /dev/null
+++ b/core/java/android/hardware/biometrics/ComponentInfoInternal.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+parcelable ComponentInfoInternal;
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/ComponentInfoInternal.java b/core/java/android/hardware/biometrics/ComponentInfoInternal.java
new file mode 100644
index 0000000..fa34e0b
--- /dev/null
+++ b/core/java/android/hardware/biometrics/ComponentInfoInternal.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The internal class for storing the component info for a subsystem of the biometric sensor,
+ * as defined in {@link android.hardware.biometrics.common.ComponentInfo}.
+ * @hide
+ */
+public class ComponentInfoInternal implements Parcelable {
+
+ public final String componentId;
+ public final String hardwareVersion;
+ public final String firmwareVersion;
+ public final String serialNumber;
+ public final String softwareVersion;
+
+ /**
+ * Constructs a {@link ComponentInfoInternal} from another instance.
+ * @hide
+ */
+ public static ComponentInfoInternal from(@NonNull ComponentInfoInternal comp) {
+ return new ComponentInfoInternal(comp.componentId, comp.hardwareVersion,
+ comp.firmwareVersion, comp.serialNumber, comp.softwareVersion);
+ }
+
+ /**
+ * @hide
+ */
+ public ComponentInfoInternal(String componentId, String hardwareVersion,
+ String firmwareVersion, String serialNumber, String softwareVersion) {
+ this.componentId = componentId;
+ this.hardwareVersion = hardwareVersion;
+ this.firmwareVersion = firmwareVersion;
+ this.serialNumber = serialNumber;
+ this.softwareVersion = softwareVersion;
+ }
+
+ protected ComponentInfoInternal(Parcel in) {
+ componentId = in.readString();
+ hardwareVersion = in.readString();
+ firmwareVersion = in.readString();
+ serialNumber = in.readString();
+ softwareVersion = in.readString();
+ }
+
+ public static final Creator<ComponentInfoInternal> CREATOR =
+ new Creator<ComponentInfoInternal>() {
+ @Override
+ public ComponentInfoInternal createFromParcel(Parcel in) {
+ return new ComponentInfoInternal(in);
+ }
+
+ @Override
+ public ComponentInfoInternal[] newArray(int size) {
+ return new ComponentInfoInternal[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(componentId);
+ dest.writeString(hardwareVersion);
+ dest.writeString(firmwareVersion);
+ dest.writeString(serialNumber);
+ dest.writeString(softwareVersion);
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentId: " + componentId
+ + ", HardwareVersion: " + hardwareVersion
+ + ", FirmwareVersion: " + firmwareVersion
+ + ", SerialNumber " + serialNumber
+ + ", SoftwareVersion: " + softwareVersion;
+ }
+}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 0e99f31..20c25fb 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -21,6 +21,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Contains the information set/requested by the caller of the {@link BiometricPrompt}
* @hide
@@ -40,7 +43,7 @@
private @BiometricManager.Authenticators.Types int mAuthenticators;
private boolean mDisallowBiometricsIfPolicyExists;
private boolean mReceiveSystemEvents;
- private int mSensorId = -1;
+ @NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
public PromptInfo() {
@@ -60,7 +63,7 @@
mAuthenticators = in.readInt();
mDisallowBiometricsIfPolicyExists = in.readBoolean();
mReceiveSystemEvents = in.readBoolean();
- mSensorId = in.readInt();
+ mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -95,7 +98,14 @@
dest.writeInt(mAuthenticators);
dest.writeBoolean(mDisallowBiometricsIfPolicyExists);
dest.writeBoolean(mReceiveSystemEvents);
- dest.writeInt(mSensorId);
+ dest.writeList(mAllowedSensorIds);
+ }
+
+ public boolean containsTestConfigurations() {
+ if (!mAllowedSensorIds.isEmpty()) {
+ return true;
+ }
+ return false;
}
public boolean containsPrivateApiConfigurations() {
@@ -169,8 +179,8 @@
mReceiveSystemEvents = receiveSystemEvents;
}
- public void setSensorId(int sensorId) {
- mSensorId = sensorId;
+ public void setAllowedSensorIds(@NonNull List<Integer> sensorIds) {
+ mAllowedSensorIds = sensorIds;
}
// Getters
@@ -234,7 +244,8 @@
return mReceiveSystemEvents;
}
- public int getSensorId() {
- return mSensorId;
+ @NonNull
+ public List<Integer> getAllowedSensorIds() {
+ return mAllowedSensorIds;
}
}
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
index 360f138..3b9cad4 100644
--- a/core/java/android/hardware/biometrics/SensorProperties.java
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -17,10 +17,13 @@
package android.hardware.biometrics;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* The base class containing all modality-agnostic information.
@@ -56,15 +59,93 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Strength {}
+ /**
+ * A class storing the component info for a subsystem of the sensor.
+ */
+ public static final class ComponentInfo {
+ @NonNull private final String mComponentId;
+ @NonNull private final String mHardwareVersion;
+ @NonNull private final String mFirmwareVersion;
+ @NonNull private final String mSerialNumber;
+ @NonNull private final String mSoftwareVersion;
+
+ /**
+ * @hide
+ */
+ public ComponentInfo(@NonNull String componentId, @NonNull String hardwareVersion,
+ @NonNull String firmwareVersion, @NonNull String serialNumber,
+ @NonNull String softwareVersion) {
+ mComponentId = componentId;
+ mHardwareVersion = hardwareVersion;
+ mFirmwareVersion = firmwareVersion;
+ mSerialNumber = serialNumber;
+ mSoftwareVersion = softwareVersion;
+ }
+
+ /**
+ * @return The unique identifier for the subsystem.
+ */
+ @NonNull
+ public String getComponentId() {
+ return mComponentId;
+ }
+
+ /**
+ * @return The hardware version for the subsystem. For example, <vendor>/<model>/<revision>.
+ */
+ @NonNull
+ public String getHardwareVersion() {
+ return mHardwareVersion;
+ }
+
+ /**
+ * @return The firmware version for the subsystem.
+ */
+ @NonNull
+ public String getFirmwareVersion() {
+ return mFirmwareVersion;
+ }
+
+ /**
+ * @return The serial number for the subsystem.
+ */
+ @NonNull
+ public String getSerialNumber() {
+ return mSerialNumber;
+ }
+
+ /**
+ * @return The software version for the subsystem.
+ * For example, <vendor>/<version>/<revision>.
+ */
+ @NonNull
+ public String getSoftwareVersion() {
+ return mSoftwareVersion;
+ }
+
+ /**
+ * Constructs a {@link ComponentInfo} from the internal parcelable representation.
+ * @hide
+ */
+ public static ComponentInfo from(ComponentInfoInternal internalComp) {
+ return new ComponentInfo(internalComp.componentId, internalComp.hardwareVersion,
+ internalComp.firmwareVersion, internalComp.serialNumber,
+ internalComp.softwareVersion);
+ }
+ }
+
private final int mSensorId;
@Strength private final int mSensorStrength;
+ private final List<ComponentInfo> mComponentInfo;
/**
* @hide
*/
- public SensorProperties(int sensorId, @Strength int sensorStrength) {
+ public SensorProperties(int sensorId, @Strength int sensorStrength,
+ List<ComponentInfo> componentInfo) {
mSensorId = sensorId;
mSensorStrength = sensorStrength;
+ mComponentInfo = componentInfo;
}
/**
@@ -83,10 +164,23 @@
}
/**
+ * @return The sensor's component info.
+ */
+ @NonNull
+ public List<ComponentInfo> getComponentInfo() {
+ return mComponentInfo;
+ }
+
+ /**
* Constructs a {@link SensorProperties} from the internal parcelable representation.
* @hide
*/
public static SensorProperties from(SensorPropertiesInternal internalProp) {
- return new SensorProperties(internalProp.sensorId, internalProp.sensorStrength);
+ final List<ComponentInfo> componentInfo = new ArrayList<>();
+ for (ComponentInfoInternal internalComp : internalProp.componentInfo) {
+ componentInfo.add(ComponentInfo.from(internalComp));
+ }
+ return new SensorProperties(internalProp.sensorId, internalProp.sensorStrength,
+ componentInfo);
}
}
diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
index 909f456..eda0ded 100644
--- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
@@ -20,6 +20,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* The base class containing all modality-agnostic information. This is a superset of the
* {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible
@@ -31,21 +34,23 @@
public final int sensorId;
@SensorProperties.Strength public final int sensorStrength;
public final int maxEnrollmentsPerUser;
+ public final List<ComponentInfoInternal> componentInfo;
public final boolean resetLockoutRequiresHardwareAuthToken;
public final boolean resetLockoutRequiresChallenge;
public static SensorPropertiesInternal from(@NonNull SensorPropertiesInternal prop) {
return new SensorPropertiesInternal(prop.sensorId, prop.sensorStrength,
- prop.maxEnrollmentsPerUser, prop.resetLockoutRequiresHardwareAuthToken,
- prop.resetLockoutRequiresChallenge);
+ prop.maxEnrollmentsPerUser, prop.componentInfo,
+ prop.resetLockoutRequiresHardwareAuthToken, prop.resetLockoutRequiresChallenge);
}
protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength,
- int maxEnrollmentsPerUser, boolean resetLockoutRequiresHardwareAuthToken,
- boolean resetLockoutRequiresChallenge) {
+ int maxEnrollmentsPerUser, @NonNull List<ComponentInfoInternal> componentInfo,
+ boolean resetLockoutRequiresHardwareAuthToken, boolean resetLockoutRequiresChallenge) {
this.sensorId = sensorId;
this.sensorStrength = sensorStrength;
this.maxEnrollmentsPerUser = maxEnrollmentsPerUser;
+ this.componentInfo = componentInfo;
this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
this.resetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
}
@@ -54,6 +59,8 @@
sensorId = in.readInt();
sensorStrength = in.readInt();
maxEnrollmentsPerUser = in.readInt();
+ componentInfo = new ArrayList<>();
+ in.readList(componentInfo, ComponentInfoInternal.class.getClassLoader());
resetLockoutRequiresHardwareAuthToken = in.readBoolean();
resetLockoutRequiresChallenge = in.readBoolean();
}
@@ -81,13 +88,23 @@
dest.writeInt(sensorId);
dest.writeInt(sensorStrength);
dest.writeInt(maxEnrollmentsPerUser);
+ dest.writeList(componentInfo);
dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
dest.writeBoolean(resetLockoutRequiresChallenge);
}
@Override
public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[ ");
+ for (ComponentInfoInternal info : componentInfo) {
+ sb.append("[").append(info.toString());
+ sb.append("] ");
+ }
+ sb.append("]");
+
return "ID: " + sensorId + ", Strength: " + sensorStrength
- + ", MaxEnrollmentsPerUser: " + maxEnrollmentsPerUser;
+ + ", MaxEnrollmentsPerUser: " + maxEnrollmentsPerUser
+ + ", ComponentInfo: " + sb.toString();
}
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 157e333..9ac2ff5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3345,7 +3345,7 @@
* <p>A control for selecting whether optical stabilization (OIS) position
* information is included in output result metadata.</p>
* <p>Since optical image stabilization generally involves motion much faster than the duration
- * of individualq image exposure, multiple OIS samples can be included for a single capture
+ * of individual image exposure, multiple OIS samples can be included for a single capture
* result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating
* at 30fps may have 6-7 OIS samples per capture result. This information can be combined
* with the rolling shutter skew to account for lens motion during image exposure in
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index c0eb068..e7457e7 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4635,7 +4635,7 @@
* <p>A control for selecting whether optical stabilization (OIS) position
* information is included in output result metadata.</p>
* <p>Since optical image stabilization generally involves motion much faster than the duration
- * of individualq image exposure, multiple OIS samples can be included for a single capture
+ * of individual image exposure, multiple OIS samples can be included for a single capture
* result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating
* at 30fps may have 6-7 OIS samples per capture result. This information can be combined
* with the rolling shutter skew to account for lens motion during image exposure in
diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java
index e61d931..6ddea50 100644
--- a/core/java/android/hardware/face/FaceSensorProperties.java
+++ b/core/java/android/hardware/face/FaceSensorProperties.java
@@ -16,25 +16,75 @@
package android.hardware.face;
+import android.annotation.IntDef;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Container for face sensor properties.
* @hide
*/
public class FaceSensorProperties extends SensorProperties {
+ /**
+ * @hide
+ */
+ public static final int TYPE_UNKNOWN = 0;
+
+ /**
+ * @hide
+ */
+ public static final int TYPE_RGB = 1;
+
+ /**
+ * @hide
+ */
+ public static final int TYPE_IR = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef({TYPE_UNKNOWN,
+ TYPE_RGB,
+ TYPE_IR})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SensorType {}
+
+ @FaceSensorProperties.SensorType
+ final int mSensorType;
/**
* @hide
*/
public static FaceSensorProperties from(FaceSensorPropertiesInternal internalProp) {
- return new FaceSensorProperties(internalProp.sensorId, internalProp.sensorStrength);
+ final List<ComponentInfo> componentInfo = new ArrayList<>();
+ for (ComponentInfoInternal internalComp : internalProp.componentInfo) {
+ componentInfo.add(ComponentInfo.from(internalComp));
+ }
+ return new FaceSensorProperties(internalProp.sensorId,
+ internalProp.sensorStrength,
+ componentInfo,
+ internalProp.sensorType);
}
/**
* @hide
*/
- public FaceSensorProperties(int sensorId, int sensorStrength) {
- super(sensorId, sensorStrength);
+ public FaceSensorProperties(int sensorId, int sensorStrength,
+ List<ComponentInfo> componentInfo, @FaceSensorProperties.SensorType int sensorType) {
+ super(sensorId, sensorStrength, componentInfo);
+ mSensorType = sensorType;
}
+ /**
+ * @hide
+ * @return The sensor's type.
+ */
+ @FaceSensorProperties.SensorType
+ public int getSensorType() {
+ return mSensorType;
+ }
}
diff --git a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
index 34cbcb4..50ea60a2 100644
--- a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
+++ b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java
@@ -16,16 +16,24 @@
package android.hardware.face;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
+import java.util.List;
+
/**
* Container for face sensor properties.
* @hide
*/
public class FaceSensorPropertiesInternal extends SensorPropertiesInternal {
/**
+ * See {@link FaceSensorProperties.SensorType}.
+ */
+ public final @FaceSensorProperties.SensorType int sensorType;
+
+ /**
* True if the sensor is able to perform generic face detection, without running the
* matching algorithm, and without affecting the lockout counter.
*/
@@ -40,18 +48,21 @@
* Initializes SensorProperties with specified values
*/
public FaceSensorPropertiesInternal(int sensorId, @SensorProperties.Strength int strength,
- int maxEnrollmentsPerUser, boolean supportsFaceDetection,
+ int maxEnrollmentsPerUser, List<ComponentInfoInternal> componentInfo,
+ @FaceSensorProperties.SensorType int sensorType, boolean supportsFaceDetection,
boolean supportsSelfIllumination, boolean resetLockoutRequiresChallenge) {
// resetLockout is managed by the HAL and requires a HardwareAuthToken for all face
// HAL interfaces (IBiometricsFace@1.0 HIDL and IFace@1.0 AIDL).
- super(sensorId, strength, maxEnrollmentsPerUser,
- true /* resetLockoutRequiresHardwareAuthToken */, resetLockoutRequiresChallenge);
+ super(sensorId, strength, maxEnrollmentsPerUser, componentInfo,
+ true /* resetLockoutRequiresHardwareAuthToken */, resetLockoutRequiresChallenge);
+ this.sensorType = sensorType;
this.supportsFaceDetection = supportsFaceDetection;
this.supportsSelfIllumination = supportsSelfIllumination;
}
protected FaceSensorPropertiesInternal(Parcel in) {
super(in);
+ sensorType = in.readInt();
supportsFaceDetection = in.readBoolean();
supportsSelfIllumination = in.readBoolean();
}
@@ -77,12 +88,13 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
+ dest.writeInt(sensorType);
dest.writeBoolean(supportsFaceDetection);
dest.writeBoolean(supportsSelfIllumination);
}
@Override
public String toString() {
- return "ID: " + sensorId + ", Strength: " + sensorStrength;
+ return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType;
}
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index 684d7d9..a338575 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -17,10 +17,13 @@
package android.hardware.fingerprint;
import android.annotation.IntDef;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Container for fingerprint sensor properties.
@@ -77,8 +80,13 @@
*/
public static FingerprintSensorProperties from(
FingerprintSensorPropertiesInternal internalProp) {
+ final List<ComponentInfo> componentInfo = new ArrayList<>();
+ for (ComponentInfoInternal internalComp : internalProp.componentInfo) {
+ componentInfo.add(ComponentInfo.from(internalComp));
+ }
return new FingerprintSensorProperties(internalProp.sensorId,
internalProp.sensorStrength,
+ componentInfo,
internalProp.sensorType);
}
@@ -86,8 +94,8 @@
* @hide
*/
public FingerprintSensorProperties(int sensorId, int sensorStrength,
- @SensorType int sensorType) {
- super(sensorId, sensorStrength);
+ List<ComponentInfo> componentInfo, @SensorType int sensorType) {
+ super(sensorId, sensorStrength, componentInfo);
mSensorType = sensorType;
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index adc61a7..1b13370 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -21,10 +21,13 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
+import java.util.List;
+
/**
* Container for fingerprint sensor properties.
* @hide
@@ -59,6 +62,7 @@
public FingerprintSensorPropertiesInternal(int sensorId,
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
+ List<ComponentInfoInternal> componentInfo,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY,
int sensorRadius) {
@@ -66,8 +70,8 @@
// required as it can only be generated/attested/verified by TEE components.
// IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See
// the HAL interface for more details.
- super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken,
- false /* resetLockoutRequiresChallenge */);
+ super(sensorId, strength, maxEnrollmentsPerUser, componentInfo,
+ resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
this.sensorLocationX = sensorLocationX;
this.sensorLocationY = sensorLocationY;
@@ -79,10 +83,11 @@
*/
public FingerprintSensorPropertiesInternal(int sensorId,
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
+ List<ComponentInfoInternal> componentInfo,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
// TODO(b/179175438): Value should be provided from the HAL
- this(sensorId, strength, maxEnrollmentsPerUser, sensorType,
+ this(sensorId, strength, maxEnrollmentsPerUser, componentInfo, sensorType,
resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
1636 /* sensorLocationY */, 130 /* sensorRadius */);
}
@@ -94,10 +99,11 @@
// TODO(b/179175438): Remove this constructor once all HALs move to AIDL.
public FingerprintSensorPropertiesInternal(@NonNull Context context, int sensorId,
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
+ List<ComponentInfoInternal> componentInfo,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
- super(sensorId, strength, maxEnrollmentsPerUser, resetLockoutRequiresHardwareAuthToken,
- false /* resetLockoutRequiresChallenge */);
+ super(sensorId, strength, maxEnrollmentsPerUser, componentInfo,
+ resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
int[] props = context.getResources().getIntArray(
diff --git a/core/java/android/os/BytesMatcher.java b/core/java/android/os/BytesMatcher.java
index 8537f47..8974c5e 100644
--- a/core/java/android/os/BytesMatcher.java
+++ b/core/java/android/os/BytesMatcher.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.bluetooth.BluetoothUuid;
import android.net.MacAddress;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.util.HexDump;
@@ -42,207 +43,273 @@
* @hide
*/
public class BytesMatcher implements Predicate<byte[]> {
- private static final String TAG = "BytesMatcher";
+ private static final String TAG = "BytesMatcher";
- private static final char TYPE_ACCEPT = '+';
- private static final char TYPE_REJECT = '-';
+ private static final char TYPE_EXACT_ACCEPT = '+';
+ private static final char TYPE_EXACT_REJECT = '-';
+ private static final char TYPE_PREFIX_ACCEPT = '⊆';
+ private static final char TYPE_PREFIX_REJECT = '⊈';
- private final ArrayList<Rule> mRules = new ArrayList<>();
+ private final ArrayList<Rule> mRules = new ArrayList<>();
- private static class Rule {
- public final char type;
- public final @NonNull byte[] value;
- public final @Nullable byte[] mask;
+ private static class Rule {
+ public final char type;
+ public final @NonNull byte[] value;
+ public final @Nullable byte[] mask;
- public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
- if (mask != null && value.length != mask.length) {
- throw new IllegalArgumentException(
- "Expected length " + value.length + " but found " + mask.length);
- }
- this.type = type;
- this.value = value;
- this.mask = mask;
- }
+ public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
+ if (mask != null && value.length != mask.length) {
+ throw new IllegalArgumentException(
+ "Expected length " + value.length + " but found " + mask.length);
+ }
+ this.type = type;
+ this.value = value;
+ this.mask = mask;
+ }
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- encode(builder);
- return builder.toString();
- }
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ encode(builder);
+ return builder.toString();
+ }
- public void encode(@NonNull StringBuilder builder) {
- builder.append(type);
- builder.append(HexDump.toHexString(value));
- if (mask != null) {
- builder.append('/');
- builder.append(HexDump.toHexString(mask));
- }
- }
+ public void encode(@NonNull StringBuilder builder) {
+ builder.append(type);
+ builder.append(HexDump.toHexString(value));
+ if (mask != null) {
+ builder.append('/');
+ builder.append(HexDump.toHexString(mask));
+ }
+ }
- public boolean test(@NonNull byte[] value) {
- if (value.length != this.value.length) {
- return false;
- }
- for (int i = 0; i < this.value.length; i++) {
- byte local = this.value[i];
- byte remote = value[i];
- if (this.mask != null) {
- local &= this.mask[i];
- remote &= this.mask[i];
- }
- if (local != remote) {
- return false;
- }
- }
- return true;
- }
- }
+ public boolean test(@NonNull byte[] value) {
+ switch (type) {
+ case TYPE_EXACT_ACCEPT:
+ case TYPE_EXACT_REJECT:
+ if (value.length != this.value.length) {
+ return false;
+ }
+ break;
+ case TYPE_PREFIX_ACCEPT:
+ case TYPE_PREFIX_REJECT:
+ if (value.length < this.value.length) {
+ return false;
+ }
+ break;
+ }
+ for (int i = 0; i < this.value.length; i++) {
+ byte local = this.value[i];
+ byte remote = value[i];
+ if (this.mask != null) {
+ local &= this.mask[i];
+ remote &= this.mask[i];
+ }
+ if (local != remote) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_ACCEPT, value, mask));
- }
+ /**
+ * Add a rule that will result in {@link #test(byte[])} returning
+ * {@code true} when a value being tested matches it. This rule will only
+ * match values of the exact same length.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader
+ * rule might accept that same value, or vice versa.
+ *
+ * @param value to be matched
+ * @param mask to be applied to both values before testing for equality; if
+ * {@code null} then both values must match exactly
+ */
+ public void addExactAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
+ mRules.add(new Rule(TYPE_EXACT_ACCEPT, value, mask));
+ }
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_REJECT, value, mask));
- }
+ /**
+ * Add a rule that will result in {@link #test(byte[])} returning
+ * {@code false} when a value being tested matches it. This rule will only
+ * match values of the exact same length.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader
+ * rule might accept that same value, or vice versa.
+ *
+ * @param value to be matched
+ * @param mask to be applied to both values before testing for equality; if
+ * {@code null} then both values must match exactly
+ */
+ public void addExactRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
+ mRules.add(new Rule(TYPE_EXACT_REJECT, value, mask));
+ }
- /**
- * Test if the given {@code ParcelUuid} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
- return test(BluetoothUuid.uuidToBytes(value));
- }
+ /**
+ * Add a rule that will result in {@link #test(byte[])} returning
+ * {@code true} when a value being tested matches it. This rule will match
+ * values of the exact same length or longer.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader
+ * rule might accept that same value, or vice versa.
+ *
+ * @param value to be matched
+ * @param mask to be applied to both values before testing for equality; if
+ * {@code null} then both values must match exactly
+ */
+ public void addPrefixAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
+ mRules.add(new Rule(TYPE_PREFIX_ACCEPT, value, mask));
+ }
- /**
- * Test if the given {@code MacAddress} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testMacAddress(@NonNull MacAddress value) {
- return test(value.toByteArray());
- }
+ /**
+ * Add a rule that will result in {@link #test(byte[])} returning
+ * {@code false} when a value being tested matches it. This rule will match
+ * values of the exact same length or longer.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader
+ * rule might accept that same value, or vice versa.
+ *
+ * @param value to be matched
+ * @param mask to be applied to both values before testing for equality; if
+ * {@code null} then both values must match exactly
+ */
+ public void addPrefixRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
+ mRules.add(new Rule(TYPE_PREFIX_REJECT, value, mask));
+ }
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- @Override
- public boolean test(@NonNull byte[] value) {
- return test(value, false);
- }
+ /**
+ * Test if the given {@code ParcelUuid} value matches the set of rules
+ * configured in this matcher.
+ */
+ public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
+ return test(BluetoothUuid.uuidToBytes(value));
+ }
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- public boolean test(@NonNull byte[] value, boolean defaultValue) {
- final int size = mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = mRules.get(i);
- if (rule.test(value)) {
- return (rule.type == TYPE_ACCEPT);
- }
- }
- return defaultValue;
- }
+ /**
+ * Test if the given {@code MacAddress} value matches the set of rules
+ * configured in this matcher.
+ */
+ public boolean testMacAddress(@NonNull MacAddress value) {
+ return test(value.toByteArray());
+ }
- /**
- * Encode the given matcher into a human-readable {@link String} which can
- * be used to transport matchers across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #decode(String)
- */
- public static @NonNull String encode(@NonNull BytesMatcher matcher) {
- final StringBuilder builder = new StringBuilder();
- final int size = matcher.mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = matcher.mRules.get(i);
- rule.encode(builder);
- builder.append(',');
- }
- builder.deleteCharAt(builder.length() - 1);
- return builder.toString();
- }
+ /**
+ * Test if the given {@code byte[]} value matches the set of rules
+ * configured in this matcher.
+ */
+ @Override
+ public boolean test(@NonNull byte[] value) {
+ return test(value, false);
+ }
- /**
- * Decode the given human-readable {@link String} used to transport matchers
- * across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #encode(BytesMatcher)
- */
- public static @NonNull BytesMatcher decode(@NonNull String value) {
- final BytesMatcher matcher = new BytesMatcher();
- final int length = value.length();
- for (int i = 0; i < length;) {
- final char type = value.charAt(i);
+ /**
+ * Test if the given {@code byte[]} value matches the set of rules
+ * configured in this matcher.
+ */
+ public boolean test(@NonNull byte[] value, boolean defaultValue) {
+ final int size = mRules.size();
+ for (int i = 0; i < size; i++) {
+ final Rule rule = mRules.get(i);
+ if (rule.test(value)) {
+ switch (rule.type) {
+ case TYPE_EXACT_ACCEPT:
+ case TYPE_PREFIX_ACCEPT:
+ return true;
+ case TYPE_EXACT_REJECT:
+ case TYPE_PREFIX_REJECT:
+ return false;
+ }
+ }
+ }
+ return defaultValue;
+ }
- int nextRule = value.indexOf(',', i);
- int nextMask = value.indexOf('/', i);
+ /**
+ * Encode the given matcher into a human-readable {@link String} which can
+ * be used to transport matchers across device boundaries.
+ * <p>
+ * The human-readable format is an ordered list separated by commas, where
+ * each rule is a {@code +} or {@code -} symbol indicating if the match
+ * should be accepted or rejected, then followed by a hex value and an
+ * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
+ * encoded matcher.
+ *
+ * @see #decode(String)
+ */
+ public static @NonNull String encode(@NonNull BytesMatcher matcher) {
+ final StringBuilder builder = new StringBuilder();
+ final int size = matcher.mRules.size();
+ for (int i = 0; i < size; i++) {
+ final Rule rule = matcher.mRules.get(i);
+ rule.encode(builder);
+ builder.append(',');
+ }
+ if (builder.length() > 0) {
+ builder.deleteCharAt(builder.length() - 1);
+ }
+ return builder.toString();
+ }
- if (nextRule == -1) nextRule = length;
- if (nextMask > nextRule) nextMask = -1;
+ /**
+ * Decode the given human-readable {@link String} used to transport matchers
+ * across device boundaries.
+ * <p>
+ * The human-readable format is an ordered list separated by commas, where
+ * each rule is a {@code +} or {@code -} symbol indicating if the match
+ * should be accepted or rejected, then followed by a hex value and an
+ * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
+ * encoded matcher.
+ *
+ * @see #encode(BytesMatcher)
+ */
+ public static @NonNull BytesMatcher decode(@Nullable String value) {
+ final BytesMatcher matcher = new BytesMatcher();
+ if (TextUtils.isEmpty(value)) return matcher;
- final byte[] ruleValue;
- final byte[] ruleMask;
- if (nextMask >= 0) {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
- ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
- } else {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
- ruleMask = null;
- }
+ final int length = value.length();
+ for (int i = 0; i < length;) {
+ final char type = value.charAt(i);
- switch (type) {
- case TYPE_ACCEPT:
- matcher.addAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_REJECT:
- matcher.addRejectRule(ruleValue, ruleMask);
- break;
- default:
- Log.w(TAG, "Ignoring unknown type " + type);
- break;
- }
+ int nextRule = value.indexOf(',', i);
+ int nextMask = value.indexOf('/', i);
- i = nextRule + 1;
- }
- return matcher;
- }
+ if (nextRule == -1) nextRule = length;
+ if (nextMask > nextRule) nextMask = -1;
+
+ final byte[] ruleValue;
+ final byte[] ruleMask;
+ if (nextMask >= 0) {
+ ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
+ ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
+ } else {
+ ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
+ ruleMask = null;
+ }
+
+ switch (type) {
+ case TYPE_EXACT_ACCEPT:
+ matcher.addExactAcceptRule(ruleValue, ruleMask);
+ break;
+ case TYPE_EXACT_REJECT:
+ matcher.addExactRejectRule(ruleValue, ruleMask);
+ break;
+ case TYPE_PREFIX_ACCEPT:
+ matcher.addPrefixAcceptRule(ruleValue, ruleMask);
+ break;
+ case TYPE_PREFIX_REJECT:
+ matcher.addPrefixRejectRule(ruleValue, ruleMask);
+ break;
+ default:
+ Log.w(TAG, "Ignoring unknown type " + type);
+ break;
+ }
+
+ i = nextRule + 1;
+ }
+ return matcher;
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 13f3979..f1c80af 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -433,6 +433,20 @@
"android.settings.REQUEST_SCHEDULE_EXACT_ALARM";
/**
+ * Activity Action: Show settings to allow configuration of
+ * {@link Manifest.permission#MANAGE_MEDIA} permission
+ *
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_MANAGE_MEDIA =
+ "android.settings.REQUEST_MANAGE_MEDIA";
+
+ /**
* Activity Action: Show settings to allow configuration of cross-profile access for apps
*
* Input: Optionally, the Intent's data URI can specify the application package name to
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ff4d671..9872dc0 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -462,11 +462,25 @@
*/
@UnsupportedAppUsage
String mCurId;
+
/**
- * The actual instance of the method to make calls on it.
+ * Kept for {@link UnsupportedAppUsage}. Not officially maintained.
+ *
+ * @deprecated New code should use {@link #mCurrentInputMethodSession}.
*/
+ @Deprecated
+ @GuardedBy("mH")
+ @Nullable
@UnsupportedAppUsage
IInputMethodSession mCurMethod;
+
+ /**
+ * Encapsulates IPCs to the currently connected InputMethodService.
+ */
+ @Nullable
+ @GuardedBy("mH")
+ private InputMethodSessionWrapper mCurrentInputMethodSession = null;
+
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
@@ -623,11 +637,8 @@
public void finishInputAndReportToIme() {
synchronized (mH) {
finishInputLocked();
- if (mCurMethod != null) {
- try {
- mCurMethod.finishInput();
- } catch (RemoteException e) {
- }
+ if (mCurrentInputMethodSession != null) {
+ mCurrentInputMethodSession.finishInput();
}
}
}
@@ -754,7 +765,8 @@
@Override
public boolean hasActiveConnection(View view) {
synchronized (mH) {
- if (!hasServedByInputMethodLocked(view) || mCurMethod == null) {
+ if (!hasServedByInputMethodLocked(view)
+ || mCurrentInputMethodSession == null) {
return false;
}
@@ -861,7 +873,9 @@
REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
setInputChannelLocked(res.channel);
- mCurMethod = res.method;
+ mCurMethod = res.method; // for @UnsupportedAppUsage
+ mCurrentInputMethodSession =
+ InputMethodSessionWrapper.createOrNull(res.method);
mCurId = res.id;
mBindSequence = res.sequence;
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
@@ -1004,7 +1018,7 @@
}
mActivityViewToScreenMatrix.setValues(matrixValues);
- if (mCursorAnchorInfo == null || mCurMethod == null
+ if (mCursorAnchorInfo == null || mCurrentInputMethodSession == null
|| mServedInputConnectionWrapper == null) {
return;
}
@@ -1015,13 +1029,9 @@
}
// Since the host ActivityView is moved, we need to issue
// IMS#updateCursorAnchorInfo() again.
- try {
- mCurMethod.updateCursorAnchorInfo(
- CursorAnchorInfo.createForAdditionalParentMatrix(
- mCursorAnchorInfo, mActivityViewToScreenMatrix));
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
- }
+ mCurrentInputMethodSession.updateCursorAnchorInfo(
+ CursorAnchorInfo.createForAdditionalParentMatrix(
+ mCursorAnchorInfo, mActivityViewToScreenMatrix));
}
return;
}
@@ -1498,7 +1508,8 @@
setInputChannelLocked(null);
mBindSequence = -1;
mCurId = null;
- mCurMethod = null;
+ mCurMethod = null; // for @UnsupportedAppUsage
+ mCurrentInputMethodSession = null;
}
void setInputChannelLocked(InputChannel channel) {
@@ -1562,11 +1573,8 @@
}
mCompletions = completions;
- if (mCurMethod != null) {
- try {
- mCurMethod.displayCompletions(mCompletions);
- } catch (RemoteException e) {
- }
+ if (mCurrentInputMethodSession != null) {
+ mCurrentInputMethodSession.displayCompletions(mCompletions);
}
}
}
@@ -1585,11 +1593,8 @@
return;
}
- if (mCurMethod != null) {
- try {
- mCurMethod.updateExtractedText(token, text);
- } catch (RemoteException e) {
- }
+ if (mCurrentInputMethodSession != null) {
+ mCurrentInputMethodSession.updateExtractedText(token, text);
}
}
}
@@ -1850,11 +1855,8 @@
if (servedView == null || servedView.getWindowToken() != windowToken) {
return;
}
- if (mCurMethod != null) {
- try {
- mCurMethod.toggleSoftInput(showFlags, hideFlags);
- } catch (RemoteException e) {
- }
+ if (mCurrentInputMethodSession != null) {
+ mCurrentInputMethodSession.toggleSoftInput(showFlags, hideFlags);
}
}
}
@@ -1875,11 +1877,8 @@
ImeTracing.getInstance().triggerClientDump(
"InputMethodManager#toggleSoftInput", InputMethodManager.this,
null /* icProto */);
- if (mCurMethod != null) {
- try {
- mCurMethod.toggleSoftInput(showFlags, hideFlags);
- } catch (RemoteException e) {
- }
+ if (mCurrentInputMethodSession != null) {
+ mCurrentInputMethodSession.toggleSoftInput(showFlags, hideFlags);
}
}
@@ -2062,7 +2061,8 @@
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
- mCurMethod = res.method;
+ mCurMethod = res.method; // for @UnsupportedAppUsage
+ mCurrentInputMethodSession = InputMethodSessionWrapper.createOrNull(res.method);
mCurId = res.id;
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
@@ -2072,11 +2072,8 @@
mRestartOnNextWindowFocus = true;
break;
}
- if (mCurMethod != null && mCompletions != null) {
- try {
- mCurMethod.displayCompletions(mCompletions);
- } catch (RemoteException e) {
- }
+ if (mCurrentInputMethodSession != null && mCompletions != null) {
+ mCurrentInputMethodSession.displayCompletions(mCompletions);
}
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
@@ -2234,12 +2231,9 @@
ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this,
null /* icProto */);
synchronized (mH) {
- try {
- if (mCurMethod != null && mCurRootView != null
- && mCurRootView.getWindowToken() == windowToken) {
- mCurMethod.notifyImeHidden();
- }
- } catch (RemoteException re) {
+ if (mCurrentInputMethodSession != null && mCurRootView != null
+ && mCurRootView.getWindowToken() == windowToken) {
+ mCurrentInputMethodSession.notifyImeHidden();
}
}
}
@@ -2284,7 +2278,7 @@
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ || mCurrentInputMethodSession == null) {
return;
}
@@ -2293,22 +2287,20 @@
|| mCursorCandEnd != candidatesEnd) {
if (DEBUG) Log.d(TAG, "updateSelection");
- try {
- if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
- final int oldSelStart = mCursorSelStart;
- final int oldSelEnd = mCursorSelEnd;
- // Update internal values before sending updateSelection to the IME, because
- // if it changes the text within its onUpdateSelection handler in a way that
- // does not move the cursor we don't want to call it again with the same values.
- mCursorSelStart = selStart;
- mCursorSelEnd = selEnd;
- mCursorCandStart = candidatesStart;
- mCursorCandEnd = candidatesEnd;
- mCurMethod.updateSelection(oldSelStart, oldSelEnd,
- selStart, selEnd, candidatesStart, candidatesEnd);
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
+ if (DEBUG) {
+ Log.v(TAG, "SELECTION CHANGE: " + mCurrentInputMethodSession);
}
+ final int oldSelStart = mCursorSelStart;
+ final int oldSelEnd = mCursorSelEnd;
+ // Update internal values before sending updateSelection to the IME, because
+ // if it changes the text within its onUpdateSelection handler in a way that
+ // does not move the cursor we don't want to call it again with the same values.
+ mCursorSelStart = selStart;
+ mCursorSelEnd = selEnd;
+ mCursorCandStart = candidatesStart;
+ mCursorCandEnd = candidatesEnd;
+ mCurrentInputMethodSession.updateSelection(
+ oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
}
}
}
@@ -2342,15 +2334,11 @@
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ || mCurrentInputMethodSession == null) {
return;
}
- try {
- if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
- mCurMethod.viewClicked(focusChanged);
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
- }
+ if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
+ mCurrentInputMethodSession.viewClicked(focusChanged);
}
}
@@ -2411,21 +2399,16 @@
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ || mCurrentInputMethodSession == null) {
return;
}
mTmpCursorRect.set(left, top, right, bottom);
if (!mCursorRect.equals(mTmpCursorRect)) {
- if (DEBUG) Log.d(TAG, "updateCursor");
+ if (DEBUG) Log.d(TAG, "updateCursor: " + mCurrentInputMethodSession);
- try {
- if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
- mCurMethod.updateCursor(mTmpCursorRect);
- mCursorRect.set(mTmpCursorRect);
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
- }
+ mCurrentInputMethodSession.updateCursor(mTmpCursorRect);
+ mCursorRect.set(mTmpCursorRect);
}
}
}
@@ -2448,7 +2431,7 @@
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ || mCurrentInputMethodSession == null) {
return;
}
// If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
@@ -2465,21 +2448,16 @@
return;
}
if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
- try {
- if (mActivityViewToScreenMatrix != null) {
- mCurMethod.updateCursorAnchorInfo(
- CursorAnchorInfo.createForAdditionalParentMatrix(
- cursorAnchorInfo, mActivityViewToScreenMatrix));
- } else {
- mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
- }
- mCursorAnchorInfo = cursorAnchorInfo;
- // Clear immediate bit (if any).
- mRequestUpdateCursorAnchorInfoMonitorMode &=
- ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
+ if (mActivityViewToScreenMatrix != null) {
+ mCurrentInputMethodSession.updateCursorAnchorInfo(
+ CursorAnchorInfo.createForAdditionalParentMatrix(
+ cursorAnchorInfo, mActivityViewToScreenMatrix));
+ } else {
+ mCurrentInputMethodSession.updateCursorAnchorInfo(cursorAnchorInfo);
}
+ mCursorAnchorInfo = cursorAnchorInfo;
+ // Clear immediate bit (if any).
+ mRequestUpdateCursorAnchorInfoMonitorMode &= ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
}
}
@@ -2505,15 +2483,11 @@
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ || mCurrentInputMethodSession == null) {
return;
}
- try {
- if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
- mCurMethod.appPrivateCommand(action, data);
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
- }
+ if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
+ mCurrentInputMethodSession.appPrivateCommand(action, data);
}
}
@@ -2671,7 +2645,7 @@
public int dispatchInputEvent(InputEvent event, Object token,
FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
- if (mCurMethod != null) {
+ if (mCurrentInputMethodSession != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
@@ -2682,7 +2656,9 @@
}
}
- if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
+ if (DEBUG) {
+ Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurrentInputMethodSession);
+ }
PendingEvent p = obtainPendingEventLocked(
event, token, mCurId, callback, handler);
@@ -2772,8 +2748,7 @@
return DISPATCH_IN_PROGRESS;
}
- Log.w(TAG, "Unable to send input event to IME: "
- + mCurId + " dropping: " + event);
+ Log.w(TAG, "Unable to send input event to IME: " + mCurId + " dropping: " + event);
}
return DISPATCH_NOT_HANDLED;
}
@@ -3230,7 +3205,11 @@
+ " mBindSequence=" + mBindSequence
+ " mCurId=" + mCurId);
p.println(" mFullscreenMode=" + mFullscreenMode);
- p.println(" mCurMethod=" + mCurMethod);
+ if (mCurrentInputMethodSession != null) {
+ p.println(" mCurMethod=" + mCurrentInputMethodSession);
+ } else {
+ p.println(" mCurMethod= null");
+ }
p.println(" mCurRootView=" + mCurRootView);
p.println(" mServedView=" + getServedViewLocked());
p.println(" mNextServedView=" + getNextServedViewLocked());
@@ -3346,7 +3325,7 @@
*/
@GuardedBy("mH")
public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) {
- if (mCurMethod == null) {
+ if (mCurrentInputMethodSession == null) {
return;
}
diff --git a/core/java/android/view/inputmethod/InputMethodSessionWrapper.java b/core/java/android/view/inputmethod/InputMethodSessionWrapper.java
new file mode 100644
index 0000000..c4a3773
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSessionWrapper.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2021 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.view.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.view.IInputMethodSession;
+
+/**
+ * This class wrap the {@link IInputMethodSession} object from {@link InputMethodManager}.
+ * Using current {@link IInputMethodSession} object to communicate with
+ * {@link android.inputmethodservice.InputMethodService}.
+ */
+final class InputMethodSessionWrapper {
+
+ private static final String TAG = "InputMethodSessionWrapper";
+
+ /**
+ * The actual instance of the method to make calls on it.
+ */
+ @NonNull
+ private final IInputMethodSession mSession;
+
+ private InputMethodSessionWrapper(@NonNull IInputMethodSession inputMethodSession) {
+ mSession = inputMethodSession;
+ }
+
+ /**
+ * Create a {@link InputMethodSessionWrapper} instance if applicability.
+ *
+ * @param inputMethodSession {@link IInputMethodSession} object to be wrapped.
+ * @return an instance of {@link InputMethodSessionWrapper} if {@code inputMethodSession} is not
+ * {@code null}. {@code null} otherwise.
+ */
+ @Nullable
+ public static InputMethodSessionWrapper createOrNull(
+ @NonNull IInputMethodSession inputMethodSession) {
+ return inputMethodSession != null ? new InputMethodSessionWrapper(inputMethodSession)
+ : null;
+ }
+
+ @AnyThread
+ void finishInput() {
+ try {
+ mSession.finishInput();
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ try {
+ mSession.updateCursorAnchorInfo(cursorAnchorInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void displayCompletions(CompletionInfo[] completions) {
+ try {
+ mSession.displayCompletions(completions);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void updateExtractedText(int token, ExtractedText text) {
+ try {
+ mSession.updateExtractedText(token, text);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void toggleSoftInput(int showFlags, int hideFlags) {
+ try {
+ mSession.toggleSoftInput(showFlags, hideFlags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void appPrivateCommand(String action, Bundle data) {
+ try {
+ mSession.appPrivateCommand(action, data);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void notifyImeHidden() {
+ try {
+ mSession.notifyImeHidden();
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void viewClicked(boolean focusChanged) {
+ try {
+ mSession.viewClicked(focusChanged);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void updateCursor(Rect newCursor) {
+ try {
+ mSession.updateCursor(newCursor);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ @AnyThread
+ void updateSelection(int oldSelStart, int oldSelEnd, int selStart, int selEnd,
+ int candidatesStart, int candidatesEnd) {
+ try {
+ mSession.updateSelection(
+ oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died", e);
+ }
+ }
+
+ /**
+ * @return {@link IInputMethodSession#toString()} as a debug string.
+ */
+ @AnyThread
+ @NonNull
+ @Override
+ public String toString() {
+ return mSession.toString();
+ }
+}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 499ce25..a0b4e24 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -16,6 +16,7 @@
package android.window;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -31,6 +32,7 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -284,6 +286,8 @@
private final Rect mEndAbsBounds = new Rect();
private final Point mEndRelOffset = new Point();
private ActivityManager.RunningTaskInfo mTaskInfo = null;
+ private int mStartRotation = ROTATION_UNDEFINED;
+ private int mEndRotation = ROTATION_UNDEFINED;
public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
mContainer = container;
@@ -301,6 +305,8 @@
mEndAbsBounds.readFromParcel(in);
mEndRelOffset.readFromParcel(in);
mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ mStartRotation = in.readInt();
+ mEndRotation = in.readInt();
}
/** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -341,6 +347,12 @@
mTaskInfo = taskInfo;
}
+ /** Sets the start and end rotation of this container. */
+ public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) {
+ mStartRotation = start;
+ mEndRotation = end;
+ }
+
/** @return the container that is changing. May be null if non-remotable (eg. activity) */
@Nullable
public WindowContainerToken getContainer() {
@@ -404,6 +416,14 @@
return mTaskInfo;
}
+ public int getStartRotation() {
+ return mStartRotation;
+ }
+
+ public int getEndRotation() {
+ return mEndRotation;
+ }
+
@Override
/** @hide */
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -416,6 +436,8 @@
mEndAbsBounds.writeToParcel(dest, flags);
mEndRelOffset.writeToParcel(dest, flags);
dest.writeTypedObject(mTaskInfo, flags);
+ dest.writeInt(mStartRotation);
+ dest.writeInt(mEndRotation);
}
@NonNull
@@ -442,7 +464,8 @@
public String toString() {
return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
+ " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
- + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}";
+ + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
+ + mStartRotation + "->" + mEndRotation + "}";
}
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index da750de..5507261 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2310,11 +2310,6 @@
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows read access to privileged network state in the device config.
- @hide Used internally. -->
- <permission android:name="android.permission.READ_NETWORK_DEVICE_CONFIG"
- android:protectionLevel="signature|privileged" />
-
<!-- Allows to read device identifiers and use ICC based authentication like EAP-AKA.
Often required in authentication to access the carrier's server and manage services
of the subscriber.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 601d66e..0318be7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2063,6 +2063,14 @@
requested. If it does support the feature, it will be as if the manifest didn't
request it at all. -->
<attr name="requiredNotFeature" format="string" />
+ <!-- Optional: set of flags that should apply to this permission request. -->
+ <attr name="usesPermissionFlags">
+ <!-- Strong assertion by a developer that they will never use this
+ permission to derive the physical location of the device, even
+ when the app has been granted the ACCESS_FINE_LOCATION and/or
+ ACCESS_COARSE_LOCATION permissions. -->
+ <flag name="neverForLocation" value="0x1" />
+ </attr>
</declare-styleable>
<!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside
diff --git a/core/tests/bluetoothtests/AndroidTest.xml b/core/tests/bluetoothtests/AndroidTest.xml
new file mode 100644
index 0000000..f93c4eb
--- /dev/null
+++ b/core/tests/bluetoothtests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for Bluetooth test cases">
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-suite-tag" value="apct-instrumentation"/>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="BluetoothTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="BluetoothTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.bluetooth.tests" />
+ <option name="hidden-api-checks" value="false"/>
+ <option name="runner" value="android.bluetooth.BluetoothTestRunner"/>
+ </test>
+</configuration>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index 8b3db7e..c287ea9 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -16,13 +16,18 @@
package android.bluetooth.le;
-import android.bluetooth.le.ScanRecord;
+import android.os.BytesMatcher;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.util.HexDump;
+
import junit.framework.TestCase;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
/**
* Unit test cases for {@link ScanRecord}.
@@ -31,6 +36,66 @@
* 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
*/
public class ScanRecordTest extends TestCase {
+ /**
+ * Example raw beacons captured from a Blue Charm BC011
+ */
+ private static final String RECORD_URL = "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
+ private static final String RECORD_UUID = "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000";
+ private static final String RECORD_TLM = "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000";
+ private static final String RECORD_IBEACON = "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000";
+
+ @SmallTest
+ public void testMatchesAnyField_Eddystone_Parser() {
+ final List<String> found = new ArrayList<>();
+ final Predicate<byte[]> matcher = (v) -> {
+ found.add(HexDump.toHexString(v));
+ return false;
+ };
+ ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_URL))
+ .matchesAnyField(matcher);
+
+ assertEquals(Arrays.asList(
+ "020106",
+ "0303AAFE",
+ "1716AAFE10EE01626C7565636861726D626561636F6E7300",
+ "09168020691E0EFE1355",
+ "1109426C7565436861726D5F313639363835"), found);
+ }
+
+ @SmallTest
+ public void testMatchesAnyField_Eddystone() {
+ final BytesMatcher matcher = BytesMatcher.decode("⊆0016AAFE/00FFFFFF");
+ assertMatchesAnyField(RECORD_URL, matcher);
+ assertMatchesAnyField(RECORD_UUID, matcher);
+ assertMatchesAnyField(RECORD_TLM, matcher);
+ assertNotMatchesAnyField(RECORD_IBEACON, matcher);
+ }
+
+ @SmallTest
+ public void testMatchesAnyField_iBeacon_Parser() {
+ final List<String> found = new ArrayList<>();
+ final Predicate<byte[]> matcher = (v) -> {
+ found.add(HexDump.toHexString(v));
+ return false;
+ };
+ ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_IBEACON))
+ .matchesAnyField(matcher);
+
+ assertEquals(Arrays.asList(
+ "020106",
+ "1AFF4C000215426C7565436861726D426561636F6E730EFE1355C5",
+ "09168020691E0EFE1355",
+ "1109426C7565436861726D5F313639363835"), found);
+ }
+
+ @SmallTest
+ public void testMatchesAnyField_iBeacon() {
+ final BytesMatcher matcher = BytesMatcher.decode("⊆00FF4C0002/00FFFFFFFF");
+ assertNotMatchesAnyField(RECORD_URL, matcher);
+ assertNotMatchesAnyField(RECORD_UUID, matcher);
+ assertNotMatchesAnyField(RECORD_TLM, matcher);
+ assertMatchesAnyField(RECORD_IBEACON, matcher);
+ }
@SmallTest
public void testParser() {
@@ -70,4 +135,14 @@
}
}
+
+ private static void assertMatchesAnyField(String record, BytesMatcher matcher) {
+ assertTrue(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
+ .matchesAnyField(matcher));
+ }
+
+ private static void assertNotMatchesAnyField(String record, BytesMatcher matcher) {
+ assertFalse(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record))
+ .matchesAnyField(matcher));
+ }
}
diff --git a/core/tests/coretests/src/android/os/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java
index 67c1b3c9..b28e309 100644
--- a/core/tests/coretests/src/android/os/BytesMatcherTest.java
+++ b/core/tests/coretests/src/android/os/BytesMatcherTest.java
@@ -59,6 +59,19 @@
}
@Test
+ public void testPrefix() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("⊆cafe,⊆beef/ff00");
+ assertTrue(matcher.test(hexStringToByteArray("cafe")));
+ assertFalse(matcher.test(hexStringToByteArray("caff")));
+ assertTrue(matcher.test(hexStringToByteArray("cafecafe")));
+ assertFalse(matcher.test(hexStringToByteArray("ca")));
+ assertTrue(matcher.test(hexStringToByteArray("beef")));
+ assertTrue(matcher.test(hexStringToByteArray("beff")));
+ assertTrue(matcher.test(hexStringToByteArray("beffbeff")));
+ assertFalse(matcher.test(hexStringToByteArray("be")));
+ }
+
+ @Test
public void testMacAddress() throws Exception {
BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000");
assertTrue(matcher.testMacAddress(
@@ -94,13 +107,23 @@
}
@Test
- public void testSerialize() throws Exception {
+ public void testSerialize_Empty() throws Exception {
BytesMatcher matcher = new BytesMatcher();
- matcher.addRejectRule(hexStringToByteArray("cafe00112233"),
+ matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
+
+ // Also very empty and null values
+ BytesMatcher.decode("");
+ BytesMatcher.decode(null);
+ }
+
+ @Test
+ public void testSerialize_Exact() throws Exception {
+ BytesMatcher matcher = new BytesMatcher();
+ matcher.addExactRejectRule(hexStringToByteArray("cafe00112233"),
hexStringToByteArray("ffffff000000"));
- matcher.addRejectRule(hexStringToByteArray("beef00112233"),
+ matcher.addExactRejectRule(hexStringToByteArray("beef00112233"),
null);
- matcher.addAcceptRule(hexStringToByteArray("000000000000"),
+ matcher.addExactAcceptRule(hexStringToByteArray("000000000000"),
hexStringToByteArray("000000000000"));
assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
@@ -116,6 +139,28 @@
}
@Test
+ public void testSerialize_Prefix() throws Exception {
+ BytesMatcher matcher = new BytesMatcher();
+ matcher.addExactRejectRule(hexStringToByteArray("aa"), null);
+ matcher.addExactAcceptRule(hexStringToByteArray("bb"), null);
+ matcher.addPrefixAcceptRule(hexStringToByteArray("aa"), null);
+ matcher.addPrefixRejectRule(hexStringToByteArray("bb"), null);
+
+ assertFalse(matcher.test(hexStringToByteArray("aa")));
+ assertTrue(matcher.test(hexStringToByteArray("bb")));
+ assertTrue(matcher.test(hexStringToByteArray("aaaa")));
+ assertFalse(matcher.test(hexStringToByteArray("bbbb")));
+
+ // Bounce through serialization pass and confirm it still works
+ matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
+
+ assertFalse(matcher.test(hexStringToByteArray("aa")));
+ assertTrue(matcher.test(hexStringToByteArray("bb")));
+ assertTrue(matcher.test(hexStringToByteArray("aaaa")));
+ assertFalse(matcher.test(hexStringToByteArray("bbbb")));
+ }
+
+ @Test
public void testOrdering_RejectFirst() throws Exception {
BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0");
assertFalse(matcher.test(hexStringToByteArray("ff")));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 4319740..623e77e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -29,6 +29,8 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -74,10 +76,9 @@
*
* or
*
- * bit FrameworksCoreTests:com.android.internal.os.BatteryStatsCpuTimesTest
+ * atest FrameworksCoreTests:com.android.internal.os.BatteryStatsCpuTimesTest
*/
@SmallTest
-@SkipPresubmit("b/180015146")
@RunWith(AndroidJUnit4.class)
public class BatteryStatsCpuTimesTest {
@Mock
@@ -89,6 +90,8 @@
@Mock
KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@Mock
+ SystemServerCpuThreadReader mSystemServerCpuThreadReader;
+ @Mock
BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
@Mock
PowerProfile mPowerProfile;
@@ -107,6 +110,7 @@
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
.setKernelCpuUidActiveTimeReader(mCpuUidActiveTimeReader)
.setKernelCpuUidClusterTimeReader(mCpuUidClusterTimeReader)
+ .setSystemServerCpuThreadReader(mSystemServerCpuThreadReader)
.setUserInfoProvider(mUserInfoProvider);
}
@@ -125,8 +129,8 @@
// VERIFY
assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
- verify(mCpuUidUserSysTimeReader).readDelta(null);
- verify(mCpuUidFreqTimeReader).readDelta(null);
+ verify(mCpuUidUserSysTimeReader).readDelta(anyBoolean(), isNull());
+ verify(mCpuUidFreqTimeReader).readDelta(anyBoolean(), isNull());
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
}
@@ -145,16 +149,16 @@
// VERIFY
verify(mUserInfoProvider).refreshUserIds();
- verify(mCpuUidUserSysTimeReader).readDelta(
+ verify(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// perClusterTimesAvailable is called twice, once in updateCpuTimeLocked() and the other
// in readKernelUidCpuFreqTimesLocked.
verify(mCpuUidFreqTimeReader, times(2)).perClusterTimesAvailable();
- verify(mCpuUidFreqTimeReader).readDelta(
+ verify(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
- verify(mCpuUidActiveTimeReader).readDelta(
+ verify(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidActiveTimeReader.Callback.class));
- verify(mCpuUidClusterTimeReader).readDelta(
+ verify(mCpuUidClusterTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidClusterTimeReader.Callback.class));
verifyNoMoreInteractions(mCpuUidFreqTimeReader);
for (int i = 0; i < numClusters; ++i) {
@@ -256,16 +260,16 @@
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesUs = {
- {12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
+ {12, 34}, {34897394, 3123983}, {79775429834L, 8430434903489L}
};
doAnswer(invocation -> {
final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
- (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
return null;
- }).when(mCpuUidUserSysTimeReader).readDelta(
+ }).when(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
@@ -291,16 +295,16 @@
// PRECONDITIONS
final long[][] deltasUs = {
- {9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
+ {9379, 3332409833484L}, {493247, 723234}, {3247819, 123348}
};
doAnswer(invocation -> {
final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
- (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasUs[i]);
}
return null;
- }).when(mCpuUidUserSysTimeReader).readDelta(
+ }).when(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
@@ -331,16 +335,16 @@
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesUs = {
- {12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
+ {12, 34}, {34897394, 3123983}, {79775429834L, 8430434903489L}
};
doAnswer(invocation -> {
final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
- (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
return null;
- }).when(mCpuUidUserSysTimeReader).readDelta(
+ }).when(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
@@ -367,16 +371,16 @@
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
final long[][] deltasUs = {
- {9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
+ {9379, 3332409833484L}, {493247, 723234}, {3247819, 123348}
};
doAnswer(invocation -> {
final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
- (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasUs[i]);
}
return null;
- }).when(mCpuUidUserSysTimeReader).readDelta(
+ }).when(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
@@ -421,18 +425,18 @@
FIRST_APPLICATION_UID + 33
});
final long[][] uidTimesUs = {
- {12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
+ {12, 34}, {34897394, 3123983}, {79775429834L, 8430434903489L}
};
doAnswer(invocation -> {
final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
- (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
// And one for the invalid uid
callback.onUidCpuTime(invalidUid, new long[]{3879, 239});
return null;
- }).when(mCpuUidUserSysTimeReader).readDelta(
+ }).when(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
@@ -474,12 +478,12 @@
};
doAnswer(invocation -> {
final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
- (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
return null;
- }).when(mCpuUidUserSysTimeReader).readDelta(
+ }).when(mCpuUidUserSysTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
@@ -553,13 +557,13 @@
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -582,17 +586,17 @@
updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
- {3248327490475l, 232349349845043l, 123, 2398, 0},
+ {3248327490475L, 232349349845043L, 123, 2398, 0},
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -636,13 +640,13 @@
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
when(mCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
@@ -676,17 +680,17 @@
updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
- {3248327490475l, 232349349845043l, 123, 2398, 0},
+ {3248327490475L, 232349349845043L, 123, 2398, 0},
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -746,13 +750,13 @@
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
when(mCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
@@ -836,13 +840,13 @@
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -865,17 +869,17 @@
updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32, 34984, 27983},
- {3248327490475l, 232349349845043l, 123, 2398, 0, 398, 0},
- {43, 3345, 2143, 123, 4554, 9374983794839l, 979875}
+ {3248327490475L, 232349349845043L, 123, 2398, 0, 398, 0},
+ {43, 3345, 2143, 123, 4554, 9374983794839L, 979875}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -913,13 +917,13 @@
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -953,13 +957,13 @@
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -1008,15 +1012,15 @@
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelCpuUidFreqTimeReader.Callback callback =
- (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
callback.onUidCpuTime(invalidUid, new long[]{12, 839, 32, 34, 21});
return null;
- }).when(mCpuUidFreqTimeReader).readDelta(
+ }).when(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
@@ -1051,13 +1055,13 @@
});
final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
doAnswer(invocation -> {
- final KernelCpuUidActiveTimeReader.Callback callback =
- (KernelCpuUidActiveTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidActiveTimeReader.Callback<Long> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidActiveTimeReader).readDelta(
+ }).when(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
@@ -1077,13 +1081,13 @@
updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000};
doAnswer(invocation -> {
- final KernelCpuUidActiveTimeReader.Callback callback =
- (KernelCpuUidActiveTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidActiveTimeReader.Callback<Long> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mCpuUidActiveTimeReader).readDelta(
+ }).when(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
@@ -1115,15 +1119,15 @@
});
final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
doAnswer(invocation -> {
- final KernelCpuUidActiveTimeReader.Callback callback =
- (KernelCpuUidActiveTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidActiveTimeReader.Callback<Long> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
callback.onUidCpuTime(invalidUid, 1200L);
return null;
- }).when(mCpuUidActiveTimeReader).readDelta(
+ }).when(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
@@ -1159,13 +1163,13 @@
{8000, 0}
};
doAnswer(invocation -> {
- final KernelCpuUidClusterTimeReader.Callback callback =
- (KernelCpuUidClusterTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidClusterTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mCpuUidClusterTimeReader).readDelta(
+ }).when(mCpuUidClusterTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidClusterTimeReader.Callback.class));
// RUN
@@ -1189,13 +1193,13 @@
{43000, 3345000}
};
doAnswer(invocation -> {
- final KernelCpuUidClusterTimeReader.Callback callback =
- (KernelCpuUidClusterTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidClusterTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mCpuUidClusterTimeReader).readDelta(
+ }).when(mCpuUidClusterTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidClusterTimeReader.Callback.class));
// RUN
@@ -1232,15 +1236,15 @@
{8000, 0}
};
doAnswer(invocation -> {
- final KernelCpuUidClusterTimeReader.Callback callback =
- (KernelCpuUidClusterTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidClusterTimeReader.Callback<long[]> callback =
+ invocation.getArgument(1);
for (int i = 0; i < testUids.length; ++i) {
callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
callback.onUidCpuTime(invalidUid, new long[]{400, 1000});
return null;
- }).when(mCpuUidClusterTimeReader).readDelta(
+ }).when(mCpuUidClusterTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidClusterTimeReader.Callback.class));
// RUN
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 5b57f19..c70d6b0 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1231,6 +1231,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "-672355406": {
+ "message": " Rejecting as no-op: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-672228342": {
"message": "resumeTopActivityLocked: Top activity resumed %s",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index cb39b4e..e3594d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -35,6 +35,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* Singleton source of truth for the current state of PIP bounds.
@@ -84,6 +85,7 @@
private @Nullable Runnable mOnMinimalSizeChangeCallback;
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
+ private @Nullable Consumer<Rect> mOnPipExclusionBoundsChangeCallback;
public PipBoundsState(@NonNull Context context) {
mContext = context;
@@ -102,6 +104,9 @@
/** Set the current PIP bounds. */
public void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
+ if (mOnPipExclusionBoundsChangeCallback != null) {
+ mOnPipExclusionBoundsChangeCallback.accept(bounds);
+ }
}
/** Get the current PIP bounds. */
@@ -386,6 +391,18 @@
mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback;
}
+ /**
+ * Set a callback to watch out for PiP bounds. This is mostly used by SystemUI's
+ * Back-gesture handler, to avoid conflicting with PiP when it's stashed.
+ */
+ public void setPipExclusionBoundsChangeCallback(
+ @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
+ mOnPipExclusionBoundsChangeCallback = onPipExclusionBoundsChangeCallback;
+ if (mOnPipExclusionBoundsChangeCallback != null) {
+ mOnPipExclusionBoundsChangeCallback.accept(getBounds());
+ }
+ }
+
/** Source of truth for the current bounds of PIP that may be in motion. */
public static class MotionBoundsState {
/** The bounds used when PIP is in motion (e.g. during a drag or animation) */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 36dc4e4..fa31a0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -566,6 +566,8 @@
mInSwipePipToHomeTransition = false;
mPictureInPictureParams = null;
mState = State.UNDEFINED;
+ // Re-set the PIP bounds to none.
+ mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
mPipMenuController.detach();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 9a584c6..1f5c136 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -494,15 +494,6 @@
mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds);
}
- /**
- * Set a listener to watch out for PiP bounds. This is mostly used by SystemUI's
- * Back-gesture handler, to avoid conflicting with PiP when it's stashed.
- */
- private void setPipExclusionBoundsChangeListener(
- Consumer<Rect> pipExclusionBoundsChangeListener) {
- mTouchHandler.setPipExclusionBoundsChangeListener(pipExclusionBoundsChangeListener);
- }
-
@Override
public void onPipTransitionStarted(int direction, Rect pipBounds) {
if (isOutPipDirection(direction)) {
@@ -712,7 +703,7 @@
@Override
public void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
mMainExecutor.execute(() -> {
- PipController.this.setPipExclusionBoundsChangeListener(listener);
+ mPipBoundsState.setPipExclusionBoundsChangeCallback(listener);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 81a7ae1..402846f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -29,7 +29,6 @@
import android.util.Log;
import android.view.Choreographer;
-import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.AnimationHandler;
import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
import androidx.dynamicanimation.animation.SpringForce;
@@ -489,8 +488,7 @@
/**
* Animates the PiP to offset it from the IME or shelf.
*/
- @VisibleForTesting
- public void animateToOffset(Rect originalBounds, int offset) {
+ void animateToOffset(Rect originalBounds, int offset) {
if (DEBUG) {
Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
+ " callers=\n" + Debug.getCallers(5, " "));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 44e2624..b0a7319 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -57,8 +57,6 @@
import com.android.wm.shell.pip.PipUiEventLogger;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.function.Consumer;
/**
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
@@ -73,7 +71,6 @@
// Allow PIP to resize to a slightly bigger state upon touch
private boolean mEnableResize;
private final Context mContext;
- private final PipTaskOrganizer mPipTaskOrganizer;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
@@ -81,7 +78,6 @@
private final ShellExecutor mMainExecutor;
private PipResizeGestureHandler mPipResizeGestureHandler;
- private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener;
private final PhonePipMenuController mMenuController;
private final AccessibilityManager mAccessibilityManager;
@@ -170,7 +166,6 @@
mContext = context;
mMainExecutor = mainExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
- mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
@@ -290,11 +285,6 @@
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
}
- // Reset exclusion to none.
- if (mPipExclusionBoundsChangeListener != null
- && mPipExclusionBoundsChangeListener.get() != null) {
- mPipExclusionBoundsChangeListener.get().accept(new Rect());
- }
mPipResizeGestureHandler.onActivityUnpinned();
}
@@ -931,10 +921,6 @@
}
private void stashEndAction() {
- if (mPipExclusionBoundsChangeListener != null
- && mPipExclusionBoundsChangeListener.get() != null) {
- mPipExclusionBoundsChangeListener.get().accept(mPipBoundsState.getBounds());
- }
if (mPipBoundsState.getBounds().left < 0
&& mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) {
mPipUiEventLogger.log(
@@ -954,11 +940,6 @@
// dismiss overlay, so just finish it after the animation completes
mMenuController.hideMenu();
}
- // Reset exclusion to none.
- if (mPipExclusionBoundsChangeListener != null
- && mPipExclusionBoundsChangeListener.get() != null) {
- mPipExclusionBoundsChangeListener.get().accept(new Rect());
- }
}
private boolean shouldStash(PointF vel, Rect motionBounds) {
@@ -982,13 +963,6 @@
}
}
- void setPipExclusionBoundsChangeListener(Consumer<Rect> pipExclusionBoundsChangeListener) {
- mPipExclusionBoundsChangeListener = new WeakReference<>(pipExclusionBoundsChangeListener);
- pipExclusionBoundsChangeListener.accept(mPipTaskOrganizer.isInPip()
- ? mPipBoundsState.getBounds() : new Rect());
-
- }
-
/**
* Updates the current movement bounds based on whether the menu is currently visible and
* resized.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 2b0a0cd..963a3dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -28,7 +28,7 @@
// with those in the framework ProtoLogGroup
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index a694e52..d7cae36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -150,6 +150,13 @@
}
return false;
}
+ if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) {
+ if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
+ Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot "
+ + windowInfo.taskInfo.topActivity);
+ }
+ return false;
+ }
final int taskRotation = windowInfo.taskInfo.configuration
.windowConfiguration.getRotation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 629ff0d..b29b18b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -96,6 +96,17 @@
};
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getMode() == TRANSIT_CHANGE) {
+ // No default animation for this, so just update bounds/position.
+ t.setPosition(change.getLeash(),
+ change.getEndAbsBounds().left - change.getEndRelOffset().x,
+ change.getEndAbsBounds().top - change.getEndRelOffset().y);
+ if (change.getTaskInfo() != null) {
+ // Skip non-tasks since those usually have null bounds.
+ t.setWindowCrop(change.getLeash(),
+ change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
+ }
+ }
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
similarity index 72%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 219da27..7d22d4d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.platform.test.annotations.Postsubmit
+import android.provider.Settings
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -28,7 +30,9 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
import org.junit.Assert
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,27 +40,31 @@
import org.junit.runners.Parameterized
/**
- * Test open non-resizable activity will auto exit split screen mode
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNonResizableNotDock`
+ * Test enter split screen from non-resizable activity. When the device doesn't support
+ * non-resizable in multi window, there should be no button to enter split screen for non-resizable
+ * activity.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNotSupportNonResizable`
*/
+@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FlakyTest(bugId = 173875043)
-class EnterSplitScreenNonResizableNotDock(
+class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
- super.transition(this, configuration)
- teardown {
+ cleanSetup(this, configuration)
+ setup {
eachRun {
- nonResizeableApp.exit(wmHelper)
+ nonResizeableApp.launchViaIntent(wmHelper)
}
}
transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
device.openQuickstep(wmHelper)
if (device.canSplitScreen(wmHelper)) {
Assert.fail("Non-resizeable app should not enter split screen")
@@ -71,8 +79,23 @@
nonResizeableApp.defaultWindowName,
splitScreenApp.defaultWindowName)
- @Test
- fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 1) {
+ // Not support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
@FlakyTest(bugId = 178447631)
@Test
@@ -84,6 +107,9 @@
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Test
+ fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+
+ @Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
isInvisible(nonResizeableApp.defaultWindowName)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
new file mode 100644
index 0000000..9b4a103
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.provider.Settings
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen from non-resizable activity. When the device supports
+ * non-resizable in multi window, there should be a button to enter split screen for non-resizable
+ * activity.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenSupportNonResizable`
+ */
+@Postsubmit
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class EnterSplitScreenSupportNonResizable(
+ testSpec: FlickerTestParameter
+) : LegacySplitScreenTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = { configuration ->
+ cleanSetup(this, configuration)
+ setup {
+ eachRun {
+ nonResizeableApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ device.launchSplitScreen(wmHelper)
+ }
+ }
+
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow != 1) {
+ // Support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
+ @FlakyTest(bugId = 178447631)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+
+ @Test
+ fun appWindowIsVisible() {
+ testSpec.assertWmEnd {
+ isVisible(nonResizeableApp.defaultWindowName)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
similarity index 60%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 4c6705f..8923845 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,7 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
+import android.provider.Settings
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -31,7 +32,10 @@
import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,18 +43,20 @@
import org.junit.runners.Parameterized
/**
- * Test launch non resizable activity in split screen mode will trigger exit split screen mode
- * (Non resizable activity launch via intent)
- * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreen`
+ * Test launch non-resizable activity via intent in split screen mode. When the device does not
+ * support non-resizable in multi window, it should trigger exit split screen.
+ * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentNotSupportNonResizable`
*/
-@Presubmit
+@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableLaunchInLegacySplitScreen(
+class LegacySplitScreenFromIntentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
cleanSetup(this, configuration)
@@ -72,33 +78,60 @@
WindowManagerStateHelper.SPLASH_SCREEN_NAME,
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
- @Presubmit
- @Test
- fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 1) {
+ // Not support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+ }
+ }
- @Presubmit
- @Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
@FlakyTest(bugId = 178447631)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
- @Presubmit
- @Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@FlakyTest(bugId = 178447631)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ fun resizableAppLayerBecomesInvisible() =
+ testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+
+ @Test
+ fun nonResizableAppLayerBecomesVisible() =
+ testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun resizableAppWindowBecomesInvisible() =
+ testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+
+ @Test
+ fun nonResizableAppWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+
+ @Test
+ fun onlyNonResizableAppWindowIsVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ isInvisible(splitScreenApp.defaultWindowName)
+ isVisible(nonResizeableApp.defaultWindowName)
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
similarity index 63%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index 4c6705f..2f5e0bd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,22 +16,24 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
+import android.provider.Settings
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.appWindowBecomesInVisible
import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,18 +41,20 @@
import org.junit.runners.Parameterized
/**
- * Test launch non resizable activity in split screen mode will trigger exit split screen mode
- * (Non resizable activity launch via intent)
- * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreen`
+ * Test launch non-resizable activity via intent in split screen mode. When the device supports
+ * non-resizable in multi window, it should show the non-resizable app in split screen.
+ * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentSupportNonResizable`
*/
-@Presubmit
+@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableLaunchInLegacySplitScreen(
+class LegacySplitScreenFromIntentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
cleanSetup(this, configuration)
@@ -72,33 +76,52 @@
WindowManagerStateHelper.SPLASH_SCREEN_NAME,
WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
- @Presubmit
- @Test
- fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 0) {
+ // Support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+ }
+ }
- @Presubmit
- @Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
@FlakyTest(bugId = 178447631)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
- @Presubmit
- @Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@FlakyTest(bugId = 178447631)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ fun nonResizableAppLayerBecomesVisible() =
+ testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun nonResizableAppWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+
+ @Test
+ fun bothAppsWindowsAreVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ isVisible(splitScreenApp.defaultWindowName)
+ isVisible(nonResizeableApp.defaultWindowName)
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
similarity index 61%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 8a1715e..a42774d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,7 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
+import android.provider.Settings
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -32,7 +33,10 @@
import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,17 +44,20 @@
import org.junit.runners.Parameterized
/**
- * Test launch non resizable activity in split screen mode will trigger exit split screen mode
- * (Non resizable activity launch via recent overview)
- * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen`
+ * Test launch non-resizable activity via recent overview in split screen mode. When the device does
+ * not support non-resizable in multi window, it should trigger exit split screen.
+ * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentNotSupportNonResizable`
*/
+@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableDismissInLegacySplitScreen(
+class LegacySplitScreenFromRecentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
cleanSetup(this, configuration)
@@ -68,37 +75,64 @@
override val ignoredWindows: List<String>
get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
- @Presubmit
- @Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 1) {
+ // Not support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
@FlakyTest(bugId = 178447631)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @Presubmit
- @Test
- fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
-
- @Presubmit
- @Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
- @Presubmit
- @Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
-
@FlakyTest(bugId = 178447631)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ fun resizableAppLayerBecomesInvisible() =
+ testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+
+ @Test
+ fun nonResizableAppLayerBecomesVisible() =
+ testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun resizableAppWindowBecomesInvisible() =
+ testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+
+ @Test
+ fun nonResizableAppWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+
+ @Test
+ fun onlyNonResizableAppWindowIsVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ isInvisible(splitScreenApp.defaultWindowName)
+ isVisible(nonResizeableApp.defaultWindowName)
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
similarity index 62%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 8a1715e..14f6dee 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,23 +16,25 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
+import android.provider.Settings
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.appWindowBecomesInVisible
import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,17 +42,20 @@
import org.junit.runners.Parameterized
/**
- * Test launch non resizable activity in split screen mode will trigger exit split screen mode
- * (Non resizable activity launch via recent overview)
- * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen`
+ * Test launch non-resizable activity via recent overview in split screen mode. When the device
+ * supports non-resizable in multi window, it should show the non-resizable app in split screen.
+ * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentSupportNonResizable`
*/
+@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class NonResizableDismissInLegacySplitScreen(
+class LegacySplitScreenFromRecentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
cleanSetup(this, configuration)
@@ -68,37 +73,56 @@
override val ignoredWindows: List<String>
get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
- @Presubmit
- @Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 0) {
+ // Support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
@FlakyTest(bugId = 178447631)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @Presubmit
- @Test
- fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
-
- @Presubmit
- @Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
- @Presubmit
- @Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
-
@FlakyTest(bugId = 178447631)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ fun nonResizableAppLayerBecomesVisible() =
+ testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun nonResizableAppWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+
+ @Test
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+
+ @Test
+ fun bothAppsWindowsAreVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ isVisible(splitScreenApp.defaultWindowName)
+ isVisible(nonResizeableApp.defaultWindowName)
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 319fde1..e13056c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
+import android.content.Context
import android.platform.test.annotations.Presubmit
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
@@ -36,6 +37,7 @@
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val context: Context = instrumentation.context
protected val isRotated = testSpec.config.startRotation.isRotated()
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9e9d02e..f957a73 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1988,7 +1988,7 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public boolean setPreferredDeviceForCapturePreset(int capturePreset,
+ public boolean setPreferredDeviceForCapturePreset(@MediaRecorder.SystemSource int capturePreset,
@NonNull AudioDeviceAttributes device) {
return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
}
@@ -2002,7 +2002,8 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+ public boolean clearPreferredDevicesForCapturePreset(
+ @MediaRecorder.SystemSource int capturePreset) {
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
return false;
}
@@ -2024,7 +2025,8 @@
@NonNull
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+ public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(
+ @MediaRecorder.SystemSource int capturePreset) {
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
return new ArrayList<AudioDeviceAttributes>();
}
@@ -2036,7 +2038,8 @@
}
private boolean setPreferredDevicesForCapturePreset(
- int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ @MediaRecorder.SystemSource int capturePreset,
+ @NonNull List<AudioDeviceAttributes> devices) {
Objects.requireNonNull(devices);
if (!MediaRecorder.isValidAudioSource(capturePreset)) {
return false;
@@ -2081,7 +2084,8 @@
* @param devices a list of newly set preferred audio devices
*/
void onPreferredDevicesForCapturePresetChanged(
- int capturePreset, @NonNull List<AudioDeviceAttributes> devices);
+ @MediaRecorder.SystemSource int capturePreset,
+ @NonNull List<AudioDeviceAttributes> devices);
}
/**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 87e1e5b..dd08d8a 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -395,6 +395,26 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Source {}
+ /** @hide */
+ @IntDef({
+ AudioSource.DEFAULT,
+ AudioSource.MIC,
+ AudioSource.VOICE_UPLINK,
+ AudioSource.VOICE_DOWNLINK,
+ AudioSource.VOICE_CALL,
+ AudioSource.CAMCORDER,
+ AudioSource.VOICE_RECOGNITION,
+ AudioSource.VOICE_COMMUNICATION,
+ AudioSource.REMOTE_SUBMIX,
+ AudioSource.UNPROCESSED,
+ AudioSource.VOICE_PERFORMANCE,
+ AudioSource.ECHO_REFERENCE,
+ AudioSource.RADIO_TUNER,
+ AudioSource.HOTWORD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemSource {}
+
// TODO make AudioSource static (API change) and move this method inside the AudioSource class
/**
* @hide
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 5afe0b5d..10d68ba 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -86,7 +86,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mCm = ConnectivityManager.from(this);
+ mCm = getSystemService(ConnectivityManager.class);
mUrl = getUrlForCaptivePortal();
if (mUrl == null) {
done(false);
@@ -161,7 +161,6 @@
if (network != null) {
network = network.getPrivateDnsBypassingCopy();
mCm.bindProcessToNetwork(network);
- mCm.setProcessDefaultNetworkForHostResolution(network);
}
mNetwork = network;
}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
index 78a02d7..43ca739 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
@@ -49,7 +49,7 @@
case PROVISION_OBSERVER_REEVALUATION_JOB_ID:
if (isProvisioned(this)) {
Log.d(TAG, "device provisioned, force network re-evaluation");
- final ConnectivityManager connMgr = ConnectivityManager.from(this);
+ final ConnectivityManager connMgr = getSystemService(ConnectivityManager.class);
Network[] info = connMgr.getAllNetworks();
for (Network nw : info) {
final NetworkCapabilities nc = connMgr.getNetworkCapabilities(nw);
diff --git a/core/java/android/net/NetworkScore.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkScore.aidl
similarity index 100%
rename from core/java/android/net/NetworkScore.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/NetworkScore.aidl
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index eac896c..b0b3d78 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -4659,7 +4659,7 @@
Log.e(TAG, "Can't set proxy properties", e);
}
// Must flush DNS cache as new network may have different DNS resolutions.
- InetAddress.clearDnsCache();
+ InetAddressCompat.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
// cause subsequent fetches to be performed on old network.
NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
diff --git a/core/java/android/net/IOnCompleteListener.aidl b/packages/Connectivity/framework/src/android/net/IOnCompleteListener.aidl
similarity index 100%
rename from core/java/android/net/IOnCompleteListener.aidl
rename to packages/Connectivity/framework/src/android/net/IOnCompleteListener.aidl
diff --git a/packages/Connectivity/framework/src/android/net/InetAddressCompat.java b/packages/Connectivity/framework/src/android/net/InetAddressCompat.java
new file mode 100644
index 0000000..8404441
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/InetAddressCompat.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 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.net;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Compatibility utility for InetAddress core platform APIs.
+ *
+ * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
+ * (only core_current). Most stable core platform APIs are included manually in the connectivity
+ * build rules, but because InetAddress is also part of the base java SDK that is earlier on the
+ * classpath, the extra core platform APIs are not seen.
+ *
+ * TODO (b/183097033): remove this utility as soon as core_current is part of module_current
+ * @hide
+ */
+public class InetAddressCompat {
+
+ /**
+ * @see InetAddress#clearDnsCache()
+ */
+ public static void clearDnsCache() {
+ try {
+ InetAddress.class.getMethod("clearDnsCache").invoke(null);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ Log.wtf(InetAddressCompat.class.getSimpleName(), "Error clearing DNS cache", e);
+ }
+ }
+
+ /**
+ * @see InetAddress#getAllByNameOnNet(String, int)
+ */
+ public static InetAddress[] getAllByNameOnNet(String host, int netId) throws
+ UnknownHostException {
+ try {
+ return (InetAddress[]) InetAddress.class.getMethod("getAllByNameOnNet",
+ String.class, int.class).invoke(null, host, netId);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ Log.wtf(InetAddressCompat.class.getSimpleName(), "Error calling getAllByNameOnNet", e);
+ throw new IllegalStateException("Error querying via getAllNameOnNet", e);
+ }
+ }
+
+ /**
+ * @see InetAddress#getByNameOnNet(String, int)
+ */
+ public static InetAddress getByNameOnNet(String host, int netId) throws
+ UnknownHostException {
+ try {
+ return (InetAddress) InetAddress.class.getMethod("getByNameOnNet",
+ String.class, int.class).invoke(null, host, netId);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ Log.wtf(InetAddressCompat.class.getSimpleName(), "Error calling getAllByNameOnNet", e);
+ throw new IllegalStateException("Error querying via getByNameOnNet", e);
+ }
+ }
+}
diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
index 7245db3..0741414 100644
--- a/packages/Connectivity/framework/src/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -142,7 +142,7 @@
* @throws UnknownHostException if the address lookup fails.
*/
public InetAddress[] getAllByName(String host) throws UnknownHostException {
- return InetAddress.getAllByNameOnNet(host, getNetIdForResolv());
+ return InetAddressCompat.getAllByNameOnNet(host, getNetIdForResolv());
}
/**
@@ -155,7 +155,7 @@
* if the address lookup fails.
*/
public InetAddress getByName(String host) throws UnknownHostException {
- return InetAddress.getByNameOnNet(host, getNetIdForResolv());
+ return InetAddressCompat.getByNameOnNet(host, getNetIdForResolv());
}
/**
diff --git a/core/java/android/net/NetworkScore.java b/packages/Connectivity/framework/src/android/net/NetworkScore.java
similarity index 100%
rename from core/java/android/net/NetworkScore.java
rename to packages/Connectivity/framework/src/android/net/NetworkScore.java
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 63edc77..4344e94 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -66,6 +66,8 @@
private static final String INCREMENTAL_DIR = "_delta";
private static final String FULL_DATA_DIR = "_full";
+ private static final String DEVICE_NAME_FOR_D2D_RESTORE_SET = "D2D";
+ private static final String DEFAULT_DEVICE_NAME_FOR_RESTORE_SET = "flash";
// The currently-active restore set always has the same (nonzero!) token
private static final long CURRENT_SET_TOKEN = 1;
@@ -603,8 +605,10 @@
existing[num++] = CURRENT_SET_TOKEN;
RestoreSet[] available = new RestoreSet[num];
+ String deviceName = mParameters.isDeviceTransfer() ? DEVICE_NAME_FOR_D2D_RESTORE_SET
+ : DEFAULT_DEVICE_NAME_FOR_RESTORE_SET;
for (int i = 0; i < available.length; i++) {
- available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
+ available[i] = new RestoreSet("Local disk image", deviceName, existing[i]);
}
return available;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 400bf15..2f38f0a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.TransitionOldType;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import android.os.RemoteException;
import android.util.Log;
@@ -35,6 +36,8 @@
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
+import java.util.ArrayList;
+
/**
* @see RemoteAnimationAdapter
*/
@@ -100,6 +103,52 @@
};
}
+ private static class CounterRotator {
+ SurfaceControl mSurface = null;
+ ArrayList<SurfaceControl> mRotateChildren = null;
+
+ void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
+ float displayW, float displayH) {
+ if (rotateDelta == 0) return;
+ mRotateChildren = new ArrayList<>();
+ // We want to counter-rotate, so subtract from 4
+ rotateDelta = 4 - (rotateDelta + 4) % 4;
+ mSurface = new SurfaceControl.Builder()
+ .setName("Transition Unrotate")
+ .setContainerLayer()
+ .setParent(parent)
+ .build();
+ // column-major
+ if (rotateDelta == 1) {
+ t.setMatrix(mSurface, 0, 1, -1, 0);
+ t.setPosition(mSurface, displayW, 0);
+ } else if (rotateDelta == 2) {
+ t.setMatrix(mSurface, -1, 0, 0, -1);
+ t.setPosition(mSurface, displayW, displayH);
+ } else if (rotateDelta == 3) {
+ t.setMatrix(mSurface, 0, -1, 1, 0);
+ t.setPosition(mSurface, 0, displayH);
+ }
+ t.show(mSurface);
+ }
+
+ void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
+ if (mSurface == null) return;
+ t.reparent(child, mSurface);
+ mRotateChildren.add(child);
+ }
+
+ void cleanUp(SurfaceControl rootLeash) {
+ if (mSurface == null) return;
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
+ t.reparent(mRotateChildren.get(i), rootLeash);
+ }
+ t.remove(mSurface);
+ t.apply();
+ }
+ }
+
private static IRemoteTransition.Stub wrapRemoteTransition(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
@@ -116,17 +165,46 @@
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
boolean isReturnToHome = false;
+ TransitionInfo.Change launcherTask = null;
+ TransitionInfo.Change wallpaper = null;
+ int launcherLayer = 0;
+ int rotateDelta = 0;
+ float displayW = 0;
+ float displayH = 0;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() != null
&& change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
isReturnToHome = change.getMode() == TRANSIT_OPEN
|| change.getMode() == TRANSIT_TO_FRONT;
- break;
+ launcherTask = change;
+ launcherLayer = info.getChanges().size() - i;
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ wallpaper = change;
+ }
+ if (change.getParent() == null && change.getEndRotation() >= 0
+ && change.getEndRotation() != change.getStartRotation()) {
+ rotateDelta = change.getEndRotation() - change.getStartRotation();
+ displayW = change.getEndAbsBounds().width();
+ displayH = change.getEndAbsBounds().height();
+ }
+ }
+
+ // Prepare for rotation if there is one
+ final CounterRotator counterLauncher = new CounterRotator();
+ final CounterRotator counterWallpaper = new CounterRotator();
+ if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) {
+ counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(),
+ rotateDelta, displayW, displayH);
+ if (counterLauncher.mSurface != null) {
+ t.setLayer(counterLauncher.mSurface, launcherLayer);
}
}
if (isReturnToHome) {
+ if (counterLauncher.mSurface != null) {
+ t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);
+ }
// Need to "boost" the closing things since that's what launcher expects.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -136,6 +214,7 @@
if (!TransitionInfo.isIndependent(change, info)) continue;
if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
t.setLayer(leash, info.getChanges().size() * 3 - i);
+ counterLauncher.addChild(t, leash);
}
}
// Make wallpaper visible immediately since launcher apparently won't do this.
@@ -143,6 +222,18 @@
t.show(wallpapersCompat[i].leash.getSurfaceControl());
t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);
}
+ } else {
+ if (launcherTask != null) {
+ counterLauncher.addChild(t, launcherTask.getLeash());
+ }
+ if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) {
+ counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(),
+ rotateDelta, displayW, displayH);
+ if (counterWallpaper.mSurface != null) {
+ t.setLayer(counterWallpaper.mSurface, -1);
+ counterWallpaper.addChild(t, wallpaper.getLeash());
+ }
+ }
}
t.apply();
@@ -150,6 +241,8 @@
@Override
public void run() {
try {
+ counterLauncher.cleanUp(info.getRootLeash());
+ counterWallpaper.cleanUp(info.getRootLeash());
finishCallback.onTransitionFinished(null /* wct */);
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 87f6b82..246476a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -56,6 +56,7 @@
public final boolean isNotInRecents;
public final Rect contentInsets;
public final PictureInPictureParams pictureInPictureParams;
+ public final int rotationChange;
private final SurfaceControl mStartLeash;
@@ -74,6 +75,7 @@
contentInsets = app.contentInsets;
activityType = app.windowConfiguration.getActivityType();
pictureInPictureParams = app.pictureInPictureParams;
+ rotationChange = 0;
mStartLeash = app.startLeash;
}
@@ -102,7 +104,7 @@
localBounds = new Rect(change.getEndAbsBounds());
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
sourceContainerBounds = null;
- screenSpaceBounds = change.getEndAbsBounds();
+ screenSpaceBounds = new Rect(change.getEndAbsBounds());
prefixOrderIndex = order;
// TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
contentInsets = new Rect(0, 0, 0, 0);
@@ -115,6 +117,7 @@
}
pictureInPictureParams = null;
mStartLeash = null;
+ rotationChange = change.getEndRotation() - change.getStartRotation();
}
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 1765627..cd53a34 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -326,6 +326,9 @@
if (mBatteryPercentView != null) {
if (mShowPercentMode == MODE_ESTIMATE && !mCharging) {
mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
+ if (mBatteryPercentView == null) {
+ return;
+ }
if (estimate != null) {
mBatteryPercentView.setText(estimate);
setContentDescription(getContext().getString(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index e35e987..52e2016 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -50,6 +50,7 @@
import android.content.pm.ServiceInfo;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
@@ -192,9 +193,19 @@
// IBiometricsFace@1.0 does not support detection, only authentication.
when(mFaceSensorProperties.isEmpty()).thenReturn(false);
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal(
0 /* id */,
FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */,
+ componentInfo, FaceSensorProperties.TYPE_UNKNOWN,
false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
false /* resetLockoutRequiresChallenge */));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index 7f8be91..1565dee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
@@ -49,6 +50,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -341,8 +345,18 @@
final int sensorLocationX = 540;
final int sensorLocationY = 1600;
final int sensorRadius = 100;
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
sensorRadius);
@@ -379,8 +393,18 @@
final int sensorLocationX = 540;
final int sensorLocationY = 1600;
final int sensorRadius = 100;
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
sensorRadius);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 5088a53..f41c100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -34,8 +34,8 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -248,9 +248,19 @@
config.mPromptInfo = promptInfo;
final List<FingerprintSensorPropertiesInternal> fpProps = new ArrayList<>();
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
fpProps.add(new FingerprintSensorPropertiesInternal(0,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_REAR,
false /* resetLockoutRequiresHardwareAuthToken */));
mAuthContainer = new TestableAuthContainer(config, fpProps, null /* faceProps */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 30c4cf6..fa190a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -34,7 +34,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -43,6 +42,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
@@ -123,10 +123,20 @@
when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
1 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
1 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequireHardwareAuthToken */);
List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index be110fc..3f1a927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -26,6 +26,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
@@ -120,9 +121,19 @@
setUpResources();
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
props.add(new FingerprintSensorPropertiesInternal(TEST_UDFPS_SENSOR_ID,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */));
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 3d07da5..5b74cbd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -114,6 +114,8 @@
private boolean mUnregisterPending;
private boolean mDeleteAfterUnregister;
+ private boolean mForceShowMagnifiableBounds;
+
private final int mDisplayId;
private static final int INVALID_ID = -1;
@@ -420,12 +422,18 @@
@GuardedBy("mLock")
void setForceShowMagnifiableBounds(boolean show) {
if (mRegistered) {
+ mForceShowMagnifiableBounds = show;
mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
mDisplayId, show);
}
}
@GuardedBy("mLock")
+ boolean isForceShowMagnifiableBounds() {
+ return mRegistered && mForceShowMagnifiableBounds;
+ }
+
+ @GuardedBy("mLock")
boolean reset(boolean animate) {
return reset(transformToStubCallback(animate));
}
@@ -442,6 +450,7 @@
onMagnificationChangedLocked();
}
mIdOfLastServiceToMagnify = INVALID_ID;
+ mForceShowMagnifiableBounds = false;
sendSpecToAnimation(spec, animationCallback);
return changed;
}
@@ -1158,6 +1167,21 @@
}
}
+ /**
+ * Returns {@code true} if the magnifiable regions of the display is forced to be shown.
+ *
+ * @param displayId The logical display id.
+ */
+ public boolean isForceShowMagnifiableBounds(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isForceShowMagnifiableBounds();
+ }
+ }
+
private void onScreenTurnedOff() {
final Message m = PooledLambda.obtainMessage(
FullScreenMagnificationController::resetAllIfNeeded, this, false);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 17a7d39..2073c70 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -52,6 +52,9 @@
* <li> 4. {@link #onTripleTapped} updates magnification switch UI depending on magnification
* capabilities and magnification active state when triple-tap gesture is detected. </li>
* </ol>
+ *
+ * <b>Note</b> Updates magnification switch UI when magnification mode transition
+ * is done {@link DisableMagnificationCallback#onResult}.
*/
public class MagnificationController implements WindowMagnificationManager.Callback,
MagnificationGestureHandler.Callback,
@@ -358,7 +361,8 @@
boolean isActivated = false;
if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
&& mFullScreenMagnificationController != null) {
- isActivated = mFullScreenMagnificationController.isMagnifying(displayId);
+ isActivated = mFullScreenMagnificationController.isMagnifying(displayId)
+ || mFullScreenMagnificationController.isForceShowMagnifiableBounds(displayId);
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
&& mWindowMagnificationMgr != null) {
isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
@@ -400,6 +404,7 @@
adjustCurrentCenterIfNeededLocked();
applyMagnificationModeLocked(mTargetMode);
}
+ updateMagnificationButton(mDisplayId, mTargetMode);
mTransitionCallBack.onResult(success);
}
}
@@ -424,6 +429,7 @@
}
setExpiredAndRemoveFromListLocked();
applyMagnificationModeLocked(mCurrentMode);
+ updateMagnificationButton(mDisplayId, mCurrentMode);
mTransitionCallBack.onResult(true);
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 602dc24..d0a8881 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -16,6 +16,8 @@
package com.android.server.backup.restore;
+import static android.app.backup.BackupManager.OperationType;
+
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -24,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
@@ -51,6 +54,7 @@
*/
public class ActiveRestoreSession extends IRestoreSession.Stub {
private static final String TAG = "RestoreSession";
+ private static final String DEVICE_NAME_FOR_D2D_SET = "D2D";
private final TransportManager mTransportManager;
private final String mTransportName;
@@ -174,6 +178,7 @@
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
final long oldId = Binder.clearCallingIdentity();
+ RestoreSet restoreSet = mRestoreSets[i];
try {
return sendRestoreToHandlerLocked(
(transportClient, listener) ->
@@ -183,7 +188,7 @@
monitor,
token,
listener,
- mBackupEligibilityRules),
+ getBackupEligibilityRules(restoreSet)),
"RestoreSession.restoreAll()");
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -266,6 +271,7 @@
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
final long oldId = Binder.clearCallingIdentity();
+ RestoreSet restoreSet = mRestoreSets[i];
try {
return sendRestoreToHandlerLocked(
(transportClient, listener) ->
@@ -277,7 +283,7 @@
packages,
/* isSystemRestore */ packages.length > 1,
listener,
- mBackupEligibilityRules),
+ getBackupEligibilityRules(restoreSet)),
"RestoreSession.restorePackages(" + packages.length + " packages)");
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -290,6 +296,14 @@
return -1;
}
+ private BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
+ // TODO(b/182986784): Remove device name comparison once a designated field for operation
+ // type is added to RestoreSet object.
+ int operationType = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device)
+ ? OperationType.MIGRATION : OperationType.BACKUP;
+ return mBackupManagerService.getEligibilityRulesForOperation(operationType);
+ }
+
public synchronized int restorePackage(String packageName, IRestoreObserver observer,
IBackupManagerMonitor monitor) {
if (DEBUG) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 92af080..9ac93d9 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1078,6 +1078,7 @@
Date lastSeen = mDevicesLastNearby.get(address);
if (isDeviceDisappeared(lastSeen)) {
onDeviceDisappeared(address);
+ unscheduleTriggerDeviceDisappearedRunnable(address);
}
}
@@ -1213,7 +1214,18 @@
@Override
public void run() {
Slog.d(LOG_TAG, "TriggerDeviceDisappearedRunnable.run(address = " + mAddress + ")");
- onDeviceDisappeared(mAddress);
+ if (!mCurrentlyConnectedDevices.contains(mAddress)) {
+ onDeviceDisappeared(mAddress);
+ }
+ }
+ }
+
+ private void unscheduleTriggerDeviceDisappearedRunnable(String address) {
+ Runnable r = mTriggerDeviceDisappearedRunnables.get(address);
+ if (r != null) {
+ Slog.d(LOG_TAG,
+ "unscheduling TriggerDeviceDisappearedRunnable(address = " + address + ")");
+ mMainHandler.removeCallbacks(r);
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index f4a8ccd..25ea12b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -58,6 +58,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -69,7 +70,6 @@
import android.service.contentcapture.ActivityEvent.ActivityEventType;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.IDataShareReadAdapter;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
@@ -174,6 +174,9 @@
@GuardedBy("mLock")
private final Set<String> mPackagesWithShareRequests = new HashSet<>();
+ private final RemoteCallbackList<IContentCaptureOptionsCallback> mCallbacks =
+ new RemoteCallbackList<>();
+
final GlobalContentCaptureOptions mGlobalContentCaptureOptions =
new GlobalContentCaptureOptions();
@@ -497,16 +500,15 @@
}
void updateOptions(String packageName, ContentCaptureOptions options) {
- ArraySet<CallbackRecord> records;
- synchronized (mLock) {
- records = mContentCaptureManagerServiceStub.mCallbacks.get(packageName);
- if (records != null) {
- int N = records.size();
- for (int i = 0; i < N; i++) {
- records.valueAt(i).setContentCaptureOptions(options);
+ mCallbacks.broadcast((callback, pkg) -> {
+ if (pkg.equals(packageName)) {
+ try {
+ callback.setContentCaptureOptions(options);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to send setContentCaptureOptions(): " + e);
}
}
- }
+ });
}
private ActivityManagerInternal getAmInternal() {
@@ -616,8 +618,6 @@
}
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
- @GuardedBy("mLock")
- private final ArrayMap<String, ArraySet<CallbackRecord>> mCallbacks = new ArrayMap<>();
@Override
public void startSession(@NonNull IBinder activityToken,
@@ -778,39 +778,19 @@
IContentCaptureOptionsCallback callback) {
assertCalledByPackageOwner(packageName);
- CallbackRecord record = new CallbackRecord(callback, packageName);
- record.registerObserver();
-
- synchronized (mLock) {
- ArraySet<CallbackRecord> records = mCallbacks.get(packageName);
- if (records == null) {
- records = new ArraySet<>();
- }
- records.add(record);
- mCallbacks.put(packageName, records);
- }
+ mCallbacks.register(callback, packageName);
// Set options here in case it was updated before this was registered.
final int userId = UserHandle.getCallingUserId();
final ContentCaptureOptions options = mGlobalContentCaptureOptions.getOptions(userId,
packageName);
if (options != null) {
- record.setContentCaptureOptions(options);
- }
- }
-
- private void unregisterContentCaptureOptionsCallback(CallbackRecord record) {
- synchronized (mLock) {
- ArraySet<CallbackRecord> records = mCallbacks.get(record.mPackageName);
- if (records != null) {
- records.remove(record);
- }
-
- if (records == null || records.isEmpty()) {
- mCallbacks.remove(record.mPackageName);
+ try {
+ callback.setContentCaptureOptions(options);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to send setContentCaptureOptions(): " + e);
}
}
- record.unregisterObserver();
}
@Override
@@ -1277,39 +1257,4 @@
mDataShareRequest.getPackageName());
}
}
-
- private final class CallbackRecord implements IBinder.DeathRecipient {
- private final String mPackageName;
- private final IContentCaptureOptionsCallback mCallback;
-
- private CallbackRecord(IContentCaptureOptionsCallback callback, String packageName) {
- mCallback = callback;
- mPackageName = packageName;
- }
-
- private void setContentCaptureOptions(ContentCaptureOptions options) {
- try {
- mCallback.setContentCaptureOptions(options);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to send setContentCaptureOptions(): " + e);
- }
- }
-
- private void registerObserver() {
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to register callback cleanup " + e);
- }
- }
-
- private void unregisterObserver() {
- mCallback.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- mContentCaptureManagerServiceStub.unregisterContentCaptureOptionsCallback(this);
- }
- }
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index c295778..b089014 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1141,4 +1141,12 @@
*/
public abstract boolean isPackageFrozen(
@NonNull String packageName, int callingUid, int userId);
+
+ /**
+ * Returns true if the given {@code packageName} has declared the
+ * {@code neverForLocation} flag in the {@code uses-permission} manifest tag
+ * where they request the given {@code permissionName}.
+ */
+ public abstract boolean isPackageUsesPermissionNeverForLocation(@NonNull String packageName,
+ @NonNull String permissionName);
}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 871de0d..ebd32e8 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -521,13 +521,6 @@
return pinKeys;
}
- private static boolean shouldPinSplitApks() {
- // For now this is disabled by default bcause the pinlist support for split APKs are
- // missing in the toolchain. This flag should be removed once it is ready. b/174697187.
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
- "pin_split_apks", false);
- }
-
private synchronized ArraySet<Integer> getPinKeys() {
return mPinKeys;
}
@@ -685,7 +678,7 @@
List<String> apks = new ArrayList<>();
apks.add(appInfo.sourceDir);
- if (shouldPinSplitApks() && appInfo.splitSourceDirs != null) {
+ if (appInfo.splitSourceDirs != null) {
for (String splitApk : appInfo.splitSourceDirs) {
apks.add(splitApk);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 5b9fa79..7296679 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -214,6 +214,13 @@
return;
}
+ if (promptInfo.containsTestConfigurations()) {
+ if (getContext().checkCallingOrSelfPermission(TEST_BIOMETRIC)
+ != PackageManager.PERMISSION_GRANTED) {
+ checkInternalPermission();
+ }
+ }
+
// Only allow internal clients to enable non-public options.
if (promptInfo.containsPrivateApiConfigurations()) {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 6851d71..ef43bc5 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -23,6 +23,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -112,7 +113,8 @@
@AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
- checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId());
+ checkDevicePolicyManager, requestedStrength,
+ promptInfo.getAllowedSensorIds());
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -142,9 +144,10 @@
DevicePolicyManager devicePolicyManager,
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
- boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) {
+ boolean checkDevicePolicyManager, int requestedStrength,
+ @NonNull List<Integer> requestedSensorIds) {
- if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) {
+ if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
return BIOMETRIC_NO_HARDWARE;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 07a653f..ebf13e0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -23,6 +23,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
@@ -134,10 +135,21 @@
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (android.hardware.biometrics.common.ComponentInfo info
+ : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
prop.commonProps.sensorId, prop.commonProps.sensorStrength,
- prop.commonProps.maxEnrollmentsPerUser, false /* supportsFaceDetection */,
- prop.halControlsPreview, false /* resetLockoutRequiresChallenge */);
+ prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType,
+ false /* supportsFaceDetection */, prop.halControlsPreview,
+ false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 40c050f..55e9a83 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -27,11 +27,13 @@
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
+import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
@@ -332,8 +334,9 @@
@NonNull BiometricScheduler scheduler) {
mSensorProperties = new FaceSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength),
- maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination,
- true /* resetLockoutRequiresChallenge */);
+ maxTemplatesAllowed, new ArrayList<ComponentInfoInternal>() /* componentInfo */,
+ FaceSensorProperties.TYPE_UNKNOWN, false /* supportsFaceDetect */,
+ supportsSelfIllumination, true /* resetLockoutRequiresChallenge */);
mContext = context;
mSensorId = sensorId;
mScheduler = scheduler;
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 3f489e9..aa5afb7 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
@@ -315,7 +315,8 @@
Slog.e(TAG, "Remote exception in negative button onClick()", e);
}
})
- .setSensorId(props.sensorId)
+ .setAllowedSensorIds(new ArrayList<>(
+ Collections.singletonList(props.sensorId)))
.build();
final BiometricPrompt.AuthenticationCallback promptCallback =
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index d798198..2c85dc9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -25,6 +25,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
@@ -138,10 +139,21 @@
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ if (prop.commonProps.componentInfo != null) {
+ for (android.hardware.biometrics.common.ComponentInfo info
+ : prop.commonProps.componentInfo) {
+ componentInfo.add(new ComponentInfoInternal(info.componentId,
+ info.hardwareVersion, info.firmwareVersion, info.serialNumber,
+ info.softwareVersion));
+ }
+ }
+
final FingerprintSensorPropertiesInternal internalProp =
new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
prop.commonProps.sensorStrength,
prop.commonProps.maxEnrollmentsPerUser,
+ componentInfo,
prop.sensorType,
true /* resetLockoutRequiresHardwareAuthToken */);
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index e737677..f112549 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
@@ -355,7 +356,8 @@
mSensorProperties = new FingerprintSensorPropertiesInternal(context, sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
- sensorType, resetLockoutRequiresHardwareAuthToken);
+ new ArrayList<ComponentInfoInternal>() /* componentInfo */, sensorType,
+ resetLockoutRequiresHardwareAuthToken);
}
public static Fingerprint21 newInstance(@NonNull Context context, int sensorId, int strength,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 2394a70..90c4b4a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -22,6 +22,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -421,6 +422,7 @@
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
mSensorProperties = new FingerprintSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxTemplatesAllowed,
+ new ArrayList<ComponentInfoInternal>() /* componentInfo */,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
resetLockoutRequiresHardwareAuthToken);
mMockHalResultController = controller;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
index 60e8cce..348a03b 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -92,6 +92,9 @@
break;
}
}
+ if (keyToIndexMap.size() < 2) {
+ throw new IllegalStateException("Indexing file is corrupt.");
+ }
return keyToIndexMap;
}
@@ -106,6 +109,9 @@
private static List<String> searchKeysRangeContainingKey(
List<String> sortedKeyList, String key, int startIndex, int endIndex) {
+ if (endIndex <= startIndex) {
+ throw new IllegalStateException("Indexing file is corrupt.");
+ }
if (endIndex - startIndex == 1) {
return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1530e41..ff042c2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -252,6 +252,7 @@
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Resources;
@@ -882,7 +883,7 @@
// Lock for global state used when modifying package state or settings.
// Methods that must be called with this lock held have
// the suffix "Locked". Some methods may use the legacy suffix "LP"
- final Object mLock;
+ final PackageManagerTracedLock mLock;
// Keys are String (package name), values are Package.
@Watched
@@ -1041,7 +1042,7 @@
private final PackageAbiHelper mAbiHelper;
private final Context mContext;
- private final Object mLock;
+ private final PackageManagerTracedLock mLock;
private final Installer mInstaller;
private final Object mInstallLock;
private final Handler mBackgroundHandler;
@@ -1081,7 +1082,7 @@
mDomainVerificationManagerInternalProducer;
private final Singleton<Handler> mHandlerProducer;
- Injector(Context context, Object lock, Installer installer,
+ Injector(Context context, PackageManagerTracedLock lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
Handler backgroundHandler,
List<ScanPartition> systemPartitions,
@@ -1181,7 +1182,7 @@
return mUserManagerProducer.get(this, mPackageManager);
}
- public Object getLock() {
+ public PackageManagerTracedLock getLock() {
return mLock;
}
@@ -5963,7 +5964,7 @@
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
t.traceBegin("create package manager");
- final Object lock = new Object();
+ final PackageManagerTracedLock lock = new PackageManagerTracedLock();
final Object installLock = new Object();
HandlerThread backgroundThread = new HandlerThread("PackageManagerBg");
backgroundThread.start();
@@ -26889,11 +26890,12 @@
outUpdatedPackageNames.add(targetPackageName);
modified = true;
}
+
+ if (modified) {
+ invalidatePackageInfoCache();
+ }
}
- if (modified) {
- invalidatePackageInfoCache();
- }
return true;
}
@@ -27322,6 +27324,28 @@
return PackageManagerService.this.getPackageStartability(
packageName, callingUid, userId) == PACKAGE_STARTABILITY_FROZEN;
}
+
+ @Override
+ public boolean isPackageUsesPermissionNeverForLocation(@NonNull String packageName,
+ @NonNull String permissionName) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(permissionName);
+ final AndroidPackage pkg;
+ synchronized (mLock) {
+ pkg = mPackages.get(packageName);
+ }
+ if (pkg == null) return false;
+ final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
+ final int size = usesPermissions.size();
+ for (int i = 0; i < size; i++) {
+ final ParsedUsesPermission usesPermission = usesPermissions.get(i);
+ if (Objects.equals(usesPermission.name, permissionName)) {
+ return (usesPermission.usesPermissionFlags
+ & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0;
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
new file mode 100644
index 0000000..e15e8a8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+/**
+ * This is a unique class that is used as the PackageManager lock. It can be targeted for lock
+ * injection, similar to {@link ActivityManagerGlobalLock}.
+ */
+public class PackageManagerTracedLock {
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ec7b451..b51b833 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -353,7 +353,7 @@
private static final String ATTR_DATABASE_VERSION = "databaseVersion";
private static final String ATTR_VALUE = "value";
- private final Object mLock;
+ private final PackageManagerTracedLock mLock;
private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
@@ -525,7 +525,7 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public Settings(Map<String, PackageSetting> pkgSettings) {
- mLock = new Object();
+ mLock = new PackageManagerTracedLock();
mPackages.putAll(pkgSettings);
mAppIds = new WatchedArrayList<>();
mOtherAppIds = new WatchedSparseArray<>();
@@ -562,7 +562,7 @@
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
- @NonNull Object lock) {
+ @NonNull PackageManagerTracedLock lock) {
mLock = lock;
mAppIds = new WatchedArrayList<>();
mOtherAppIds = new WatchedSparseArray<>();
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 0a74032..8f87b19 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -214,7 +214,7 @@
newSigningDetails = ApkSignatureVerifier.verify(apexPath, minSignatureScheme);
} catch (PackageParserException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + apexPath, e);
+ "Failed to parse APEX package " + apexPath + " : " + e, e);
}
// Get signing details of the existing package
@@ -232,7 +232,8 @@
existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
} catch (PackageParserException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir, e);
+ "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
+ + " : " + e, e);
}
// Verify signing details for upgrade
@@ -291,7 +292,7 @@
}
} catch (PackageParserException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + apexInfo.modulePath, e);
+ "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
}
final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
ApexManager.MATCH_ACTIVE_PACKAGE);
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 9c3a394..5b48abb 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -24,11 +24,11 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
-import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -113,7 +113,7 @@
private boolean mDeviceProvisioned = false;
private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
private boolean mIsWaitingForEcmExit = false;
- private boolean mHasTelephony;
+ private final boolean mHasTelephony;
private boolean mHasVibrator;
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
@@ -137,9 +137,8 @@
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
- ConnectivityManager cm = (ConnectivityManager)
- context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ mHasTelephony =
+ context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
// get notified of phone state changes
TelephonyManager telephonyManager =
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b12ce67..5884102 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2114,6 +2114,10 @@
if (snapshot == null) {
return false;
}
+ if (!snapshot.getTopActivityComponent().equals(mActivityComponent)) {
+ // Obsoleted snapshot.
+ return false;
+ }
final int rotation = mDisplayContent.rotationForActivityInDifferentOrientation(this);
final int targetRotation = rotation != ROTATION_UNDEFINED
// The display may rotate according to the orientation of this activity.
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 28a509b..0ec0142 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -74,45 +74,59 @@
/**
* CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling
* changes. Disabling this change will prevent the following scaling factors from working:
- * CompatModePackages#DOWNSCALE_87_5
- * CompatModePackages#DOWNSCALE_75
- * CompatModePackages#DOWNSCALE_62_5
+ * CompatModePackages#DOWNSCALE_90
+ * CompatModePackages#DOWNSCALE_80
+ * CompatModePackages#DOWNSCALE_70
+ * CompatModePackages#DOWNSCALE_60
* CompatModePackages#DOWNSCALE_50
*
* If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly
- * resized to the highest enabled scaling factor e.g. 87.5% if both 87.5% and 75% were
- * enabled.
+ * resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were enabled.
*/
@ChangeId
@Disabled
- private static final long DOWNSCALED = 168419799L;
+ @Overridable
+ public static final long DOWNSCALED = 168419799L;
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
- * CompatModePackages#DOWNSCALE_87_5 for a package will force the app to assume it's
- * running on a display with 87.5% the vertical and horizontal resolution of the real display.
+ * CompatModePackages#DOWNSCALE_90 for a package will force the app to assume it's
+ * running on a display with 90% the vertical and horizontal resolution of the real display.
*/
@ChangeId
@Disabled
- private static final long DOWNSCALE_87_5 = 176926753L;
+ @Overridable
+ public static final long DOWNSCALE_90 = 182811243L;
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
- * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's
- * running on a display with 75% the vertical and horizontal resolution of the real display.
+ * CompatModePackages#DOWNSCALE_80 for a package will force the app to assume it's
+ * running on a display with 80% the vertical and horizontal resolution of the real display.
*/
@ChangeId
@Disabled
- private static final long DOWNSCALE_75 = 176926829L;
+ @Overridable
+ public static final long DOWNSCALE_80 = 176926753L;
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
- * CompatModePackages#DOWNSCALE_62_5 for a package will force the app to assume it's
- * running on a display with 62.5% the vertical and horizontal resolution of the real display.
+ * CompatModePackages#DOWNSCALE_70 for a package will force the app to assume it's
+ * running on a display with 70% the vertical and horizontal resolution of the real display.
*/
@ChangeId
@Disabled
- private static final long DOWNSCALE_62_5 = 176926771L;
+ @Overridable
+ public static final long DOWNSCALE_70 = 176926829L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_60 for a package will force the app to assume it's
+ * running on a display with 60% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_60 = 176926771L;
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
@@ -121,7 +135,8 @@
*/
@ChangeId
@Disabled
- private static final long DOWNSCALE_50 = 176926741L;
+ @Overridable
+ public static final long DOWNSCALE_50 = 176926741L;
/**
* On Android TV applications that target pre-S are not expecting to receive a Window larger
@@ -273,17 +288,20 @@
float getCompatScale(String packageName, int uid) {
final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
if (CompatChanges.isChangeEnabled(DOWNSCALED, packageName, userHandle)) {
- if (CompatChanges.isChangeEnabled(DOWNSCALE_87_5, packageName, userHandle)) {
- return 8f / 7f; // 1.14285714286
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
+ return 1f / 0.9f;
}
- if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) {
- return 4f / 3f; // 1.333333333
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) {
+ return 1f / 0.8f;
}
- if (CompatChanges.isChangeEnabled(DOWNSCALE_62_5, packageName, userHandle)) {
- return /* 1 / 0.625 */ 1.6f;
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) {
+ return 1f / 0.7f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) {
+ return 1f / 0.6f;
}
if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) {
- return /* 1 / 0.5 */ 2f;
+ return 1f / 0.5f;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 95b2b5d..6d24105 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1428,7 +1428,8 @@
computeScreenConfiguration(config);
} else if (currentConfig != null
// If waiting for a remote rotation, don't prematurely update configuration.
- && !mDisplayRotation.isWaitingForRemoteRotation()) {
+ && !(mDisplayRotation.isWaitingForRemoteRotation()
+ || mAtmService.getTransitionController().isCollecting(this))) {
// No obvious action we need to take, but if our current state mismatches the
// activity manager's, update it, disregarding font scale, which should remain set
// to the value of the previous configuration.
@@ -1470,6 +1471,12 @@
return mDisplayRotation.updateOrientation(orientation, forceUpdate);
}
+ @Override
+ boolean isSyncFinished() {
+ if (mDisplayRotation.isWaitingForRemoteRotation()) return false;
+ return super.isSyncFinished();
+ }
+
/**
* Returns a valid rotation if the activity can use different orientation than the display.
* Otherwise {@link #ROTATION_UNDEFINED}.
@@ -1763,7 +1770,7 @@
* Update rotation of the display.
*
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
- * {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN.
+ * {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN unless using Shell transitions.
*/
boolean updateRotationUnchecked() {
return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);
@@ -1778,8 +1785,12 @@
*/
private void applyRotation(final int oldRotation, final int rotation) {
mDisplayRotation.applyCurrentRotation(rotation);
- final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly();
- final Transaction transaction = getPendingTransaction();
+ final boolean shellTransitions =
+ mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null;
+ final boolean rotateSeamlessly =
+ mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
+ final Transaction transaction =
+ shellTransitions ? getSyncTransaction() : getPendingTransaction();
ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
? null : getRotationAnimation();
// We need to update our screen size information to match the new rotation. If the rotation
@@ -1795,9 +1806,11 @@
screenRotationAnimation.setRotation(transaction, rotation);
}
- forAllWindows(w -> {
- w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
- }, true /* traverseTopToBottom */);
+ if (!shellTransitions) {
+ forAllWindows(w -> {
+ w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
+ }, true /* traverseTopToBottom */);
+ }
mWmService.mDisplayManagerInternal.performTraversal(transaction);
scheduleAnimation();
@@ -4573,6 +4586,12 @@
return mWmService.makeSurfaceBuilder(mSession).setParent(getOverlayLayer());
}
+ @Override
+ public SurfaceControl.Builder makeAnimationLeash() {
+ return mWmService.makeSurfaceBuilder(mSession).setParent(mSurfaceControl)
+ .setContainerLayer();
+ }
+
/**
* Reparents the given surface to {@link #mOverlayLayer} SurfaceControl.
*/
@@ -5919,4 +5938,9 @@
return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options,
ownerCanManageAppToken, roundedCornerOverlay);
}
+
+ @Override
+ DisplayContent asDisplayContent() {
+ return this;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 5df1355..d0e4c40 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
@@ -408,8 +409,11 @@
* THE SCREEN.
*/
boolean updateRotationUnchecked(boolean forceUpdate) {
+ final boolean useShellTransitions =
+ mService.mAtmService.getTransitionController().getTransitionPlayer() != null;
+
final int displayId = mDisplayContent.getDisplayId();
- if (!forceUpdate) {
+ if (!forceUpdate && !useShellTransitions) {
if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until updates
// have been resumed.
@@ -472,6 +476,12 @@
return false;
}
+ final Transition t = (useShellTransitions
+ && !mService.mAtmService.getTransitionController().isCollecting())
+ ? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE)
+ : null;
+ mService.mAtmService.getTransitionController().collect(mDisplayContent);
+
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Display id=%d rotation changed to %d from %d, lastOrientation=%d",
displayId, rotation, oldRotation, lastOrientation);
@@ -484,6 +494,20 @@
mDisplayContent.setLayoutNeeded();
+ if (useShellTransitions) {
+ if (t != null) {
+ // This created its own transition, so send a start request.
+ mService.mAtmService.getTransitionController().requestStartTransition(
+ t, null /* trigger */, null /* remote */);
+ } else {
+ // Use remote-rotation infra since the transition has already been requested
+ // TODO(shell-transitions): Remove this once lifecycle management can cover all
+ // rotation cases.
+ startRemoteRotation(oldRotation, mRotation);
+ }
+ return true;
+ }
+
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
@@ -504,6 +528,19 @@
}
/**
+ * Utility to get a rotating displaycontent from a Transition.
+ * @return null if the transition doesn't contain a rotating display.
+ */
+ static DisplayContent getDisplayFromTransition(Transition transition) {
+ for (int i = transition.mParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = transition.mParticipants.valueAt(i);
+ if (!(wc instanceof DisplayContent)) continue;
+ return (DisplayContent) wc;
+ }
+ return null;
+ }
+
+ /**
* A Remote rotation is when we are waiting for some registered (remote)
* {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations
* to perform in sync with the rotation.
@@ -537,6 +574,22 @@
}
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mIsWaitingForRemoteRotation = false;
+
+ if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ if (!mService.mAtmService.getTransitionController().isCollecting()) {
+ throw new IllegalStateException("Trying to rotate outside a transition");
+ }
+ mService.mAtmService.getTransitionController().collect(mDisplayContent);
+ // Go through all tasks and collect them before the rotation
+ // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
+ // handling is synchronized.
+ mDisplayContent.forAllTasks(task -> {
+ if (task.isVisible()) {
+ mService.mAtmService.getTransitionController().collect(task);
+ }
+ });
+ mDisplayContent.getInsetsStateController().addProvidersToTransition();
+ }
mService.mAtmService.deferWindowLayout();
try {
mDisplayContent.sendNewConfiguration();
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a971794..b6057c6 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -248,6 +248,16 @@
return result;
}
+ public void addProvidersToTransition() {
+ for (int i = mProviders.size() - 1; i >= 0; --i) {
+ final InsetsSourceProvider p = mProviders.valueAt(i);
+ if (p == null) continue;
+ final WindowContainer wc = p.mWin;
+ if (wc == null) continue;
+ mDisplayContent.mAtmService.getTransitionController().collect(wc);
+ }
+ }
+
/**
* @return The provider of a specific type.
*/
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index aadb272..75be444 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -411,8 +412,9 @@
private static boolean reportIfNotTop(WindowContainer wc) {
// Organized tasks need to be reported anyways because Core won't show() their surfaces
// and we can't rely on onTaskAppeared because it isn't in sync.
+ // Also report wallpaper so it can be handled properly during display change/rotation.
// TODO(shell-transitions): switch onTaskAppeared usage over to transitions OPEN.
- return wc.isOrganized();
+ return wc.isOrganized() || isWallpaper(wc);
}
/** @return the depth of child within ancestor, 0 if child == ancestor, or -1 if not a child. */
@@ -430,7 +432,7 @@
}
private static boolean isWallpaper(WindowContainer wc) {
- return wc instanceof WallpaperWindowToken;
+ return wc.asWallpaperToken() != null;
}
/**
@@ -576,7 +578,8 @@
final ArrayList<WindowContainer> tmpList = new ArrayList<>();
// Build initial set of top-level participants by removing any participants that are no-ops
- // or children of other participants or are otherwise invalid.
+ // or children of other participants or are otherwise invalid; however, keep around a list
+ // of participants that should always be reported even if they aren't top.
for (WindowContainer wc : participants) {
// Don't include detached windows.
if (!wc.isAttached()) continue;
@@ -584,7 +587,11 @@
final ChangeInfo changeInfo = changes.get(wc);
// Reject no-ops
- if (!changeInfo.hasChanged(wc)) continue;
+ if (!changeInfo.hasChanged(wc)) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Rejecting as no-op: %s", wc);
+ continue;
+ }
// Search through ancestors to find the top-most participant (if one exists)
WindowContainer topParent = null;
@@ -651,10 +658,21 @@
/** Gets the leash surface for a window container */
private static SurfaceControl getLeashSurface(WindowContainer wc) {
+ final DisplayContent asDC = wc.asDisplayContent();
+ if (asDC != null) {
+ // DisplayContent is the "root", so we use the windowing layer instead to avoid
+ // hardware-screen-level surfaces.
+ return asDC.getWindowingLayer();
+ }
return wc.getSurfaceControl();
}
private static SurfaceControl getOrigParentSurface(WindowContainer wc) {
+ if (wc.asDisplayContent() != null) {
+ // DisplayContent is the "root", so we reinterpret it's wc as the window layer
+ // making the parent surface the displaycontent's surface.
+ return wc.getSurfaceControl();
+ }
return wc.getParent().getSurfaceControl();
}
@@ -744,6 +762,7 @@
change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left,
target.getBounds().top - target.getParent().getBounds().top);
change.setFlags(info.getChangeFlags(target));
+ change.setRotation(info.mRotation, target.getWindowConfiguration().getRotation());
final Task task = target.asTask();
if (task != null) {
final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
@@ -773,12 +792,14 @@
int mWindowingMode;
final Rect mAbsoluteBounds = new Rect();
boolean mShowWallpaper;
+ int mRotation = ROTATION_UNDEFINED;
ChangeInfo(@NonNull WindowContainer origState) {
mVisible = origState.isVisibleRequested();
mWindowingMode = origState.getWindowingMode();
mAbsoluteBounds.set(origState.getBounds());
mShowWallpaper = origState.showWallpaper();
+ mRotation = origState.getWindowConfiguration().getRotation();
}
@VisibleForTesting
@@ -797,7 +818,8 @@
// if mWindowingMode is 0, this container wasn't attached at collect time, so
// assume no change in windowing-mode.
|| (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode)
- || !newState.getBounds().equals(mAbsoluteBounds);
+ || !newState.getBounds().equals(mAbsoluteBounds)
+ || mRotation != newState.getWindowConfiguration().getRotation();
}
@TransitionInfo.TransitionMode
@@ -826,8 +848,7 @@
// checks to use requested visibility.
flags |= FLAG_TRANSLUCENT;
}
- if (wc instanceof ActivityRecord
- && wc.asActivityRecord().mUseTransferredAnimation) {
+ if (wc.asActivityRecord() != null && wc.asActivityRecord().mUseTransferredAnimation) {
flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
}
if (isWallpaper(wc)) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0bb9854..a5843d4 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3084,6 +3084,11 @@
return null;
}
+ /** Cheap way of doing cast and instanceof. */
+ DisplayContent asDisplayContent() {
+ return null;
+ }
+
/**
* @return {@code true} if window container is manage by a
* {@link android.window.WindowOrganizer}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c5e0000..57394d6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4148,12 +4148,19 @@
.notifyOnActivityRotation(displayContent.mDisplayId);
}
- if (!rotationChanged || forceRelayout) {
- displayContent.setLayoutNeeded();
- layoutNeeded = true;
- }
- if (rotationChanged || alwaysSendConfiguration) {
- displayContent.sendNewConfiguration();
+ final boolean pendingRemoteRotation = rotationChanged
+ && (displayContent.getDisplayRotation().isWaitingForRemoteRotation()
+ || mAtmService.getTransitionController().isCollecting());
+ // Even if alwaysSend, we are waiting for a transition or remote to provide
+ // rotated configuration, so we can't update configuration yet.
+ if (!pendingRemoteRotation) {
+ if (!rotationChanged || forceRelayout) {
+ displayContent.setLayoutNeeded();
+ layoutNeeded = true;
+ }
+ if (rotationChanged || alwaysSendConfiguration) {
+ displayContent.sendNewConfiguration();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 9973664..e3d549b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -246,6 +246,21 @@
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
try {
+ if (transition != null) {
+ // First check if we have a display rotation transition and if so, update it.
+ final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition);
+ if (dc != null && transition.mChanges.get(dc).mRotation != dc.getRotation()) {
+ // Go through all tasks and collect them before the rotation
+ // TODO(shell-transitions): move collect() to onConfigurationChange once
+ // wallpaper handling is synchronized.
+ dc.forAllTasks(task -> {
+ if (task.isVisible()) transition.collect(task);
+ });
+ dc.getInsetsStateController().addProvidersToTransition();
+ dc.sendNewConfiguration();
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f2b4529..a8131e4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -101,6 +101,8 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+// TODO (b/178655595) import static android.net.ConnectivityManager.USER_PREFERENCE_ENTERPRISE;
+// TODO (b/178655595) import static android.net.ConnectivityManager.USER_PREFERENCE_SYSTEM_DEFAULT;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
@@ -3076,6 +3078,13 @@
updatePermissionPolicyCache(userId);
updateAdminCanGrantSensorsPermissionCache(userId);
+ boolean enableEnterpriseNetworkSlice = true;
+ synchronized (getLockObject()) {
+ ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+ enableEnterpriseNetworkSlice = owner != null ? owner.mNetworkSlicingEnabled : true;
+ }
+ updateNetworkPreferenceForUser(userId, enableEnterpriseNetworkSlice);
+
startOwnerService(userId, "start-user");
}
@@ -3091,6 +3100,7 @@
@Override
void handleStopUser(int userId) {
+ updateNetworkPreferenceForUser(userId, false);
stopOwnerService(userId, "stop-user");
}
@@ -11394,21 +11404,22 @@
if (!mHasFeature) {
return;
}
-
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(isProfileOwner(caller),
"Caller is not profile owner; only profile owner may control the network slicing");
-
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
caller.getUserId());
if (requiredAdmin != null && requiredAdmin.mNetworkSlicingEnabled != enabled) {
requiredAdmin.mNetworkSlicingEnabled = enabled;
saveSettingsLocked(caller.getUserId());
- // TODO(b/178655595) notify CS the change.
- // TODO(b/178655595) DevicePolicyEventLogger metrics
}
}
+ updateNetworkPreferenceForUser(caller.getUserId(), enabled);
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_NETWORK_SLICING_ENABLED)
+ .setBoolean(enabled)
+ .write();
}
@Override
@@ -11418,11 +11429,8 @@
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
- permission.READ_NETWORK_DEVICE_CONFIG) || isProfileOwner(caller),
- "Caller is not profile owner and not granted"
- + " READ_NETWORK_DEVICE_CONFIG permission");
+ Preconditions.checkCallAuthorization(isProfileOwner(caller),
+ "Caller is not profile owner");
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(userHandle);
if (requiredAdmin != null) {
@@ -16946,6 +16954,20 @@
}
}
+ private void updateNetworkPreferenceForUser(int userId, boolean enableEnterprise) {
+ if (!isManagedProfile(userId)) {
+ return;
+ }
+ // TODO(b/178655595)
+ // int networkPreference = enable ? ConnectivityManager.USER_PREFERENCE_ENTERPRISE :
+ // ConnectivityManager.USER_PREFERENCE_SYSTEM_DEFAULT;
+ // mInjector.binderWithCleanCallingIdentity(() ->
+ // mInjector.getConnectivityManager().setNetworkPreferenceForUser(
+ // UserHandle.of(userId),
+ // networkPreference,
+ // null /* executor */, null /* listener */));
+ }
+
@Override
public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
if (!mHasFeature) {
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 8f12b2e..9869b07 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -255,13 +255,12 @@
binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId,
const std::string& path,
int32_t* _aidl_return) {
- *_aidl_return = mImpl.isFileFullyLoaded(storageId, path);
+ *_aidl_return = (int)mImpl.isFileFullyLoaded(storageId, path);
return ok();
}
binder::Status BinderIncrementalService::isFullyLoaded(int32_t storageId, int32_t* _aidl_return) {
- *_aidl_return = mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/true)
- .blocksRemainingOrError();
+ *_aidl_return = (int)mImpl.isMountFullyLoaded(storageId);
return ok();
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index db70d44..ebfcc32 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1980,35 +1980,30 @@
return 0;
}
-int IncrementalService::isFileFullyLoaded(StorageId storage, std::string_view filePath) const {
+incfs::LoadingState IncrementalService::isFileFullyLoaded(StorageId storage,
+ std::string_view filePath) const {
std::unique_lock l(mLock);
const auto ifs = getIfsLocked(storage);
if (!ifs) {
LOG(ERROR) << "isFileFullyLoaded failed, invalid storageId: " << storage;
- return -EINVAL;
+ return incfs::LoadingState(-EINVAL);
}
const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) {
LOG(ERROR) << "isFileFullyLoaded failed, no storage: " << storage;
- return -EINVAL;
+ return incfs::LoadingState(-EINVAL);
}
l.unlock();
- return isFileFullyLoadedFromPath(*ifs, filePath);
+ return mIncFs->isFileFullyLoaded(ifs->control, filePath);
}
-int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs,
- std::string_view filePath) const {
- const auto [filledBlocks, totalBlocks] = mIncFs->countFilledBlocks(ifs.control, filePath);
- if (filledBlocks < 0) {
- LOG(ERROR) << "isFileFullyLoadedFromPath failed to get filled blocks count for: "
- << filePath << " errno: " << filledBlocks;
- return filledBlocks;
+incfs::LoadingState IncrementalService::isMountFullyLoaded(StorageId storage) const {
+ const auto ifs = getIfs(storage);
+ if (!ifs) {
+ LOG(ERROR) << "isMountFullyLoaded failed, invalid storageId: " << storage;
+ return incfs::LoadingState(-EINVAL);
}
- if (totalBlocks < filledBlocks) {
- LOG(ERROR) << "isFileFullyLoadedFromPath failed to get total num of blocks";
- return -EINVAL;
- }
- return totalBlocks - filledBlocks;
+ return mIncFs->isEverythingFullyLoaded(ifs->control);
}
IncrementalService::LoadingProgress IncrementalService::getLoadingProgress(
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index bc441c7..aa80bd4 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -164,7 +164,8 @@
std::string_view newPath);
int unlink(StorageId storage, std::string_view path);
- int isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
+ incfs::LoadingState isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
+ incfs::LoadingState isMountFullyLoaded(StorageId storage) const;
LoadingProgress getLoadingProgress(StorageId storage, bool stopOnFirstIncomplete) const;
@@ -412,7 +413,6 @@
int setStorageParams(IncFsMount& ifs, StorageId storageId, bool enableReadLogs);
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
- int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path,
bool stopOnFirstIncomplete) const;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 2a06122..eb204c5 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -197,6 +197,13 @@
}
return {filledBlockCount, totalBlocksCount};
}
+ incfs::LoadingState isFileFullyLoaded(const Control& control,
+ std::string_view path) const final {
+ return incfs::isFullyLoaded(control, path);
+ }
+ incfs::LoadingState isEverythingFullyLoaded(const Control& control) const final {
+ return incfs::isEverythingFullyLoaded(control);
+ }
ErrorCode link(const Control& control, std::string_view from, std::string_view to) const final {
return incfs::link(control, from, to);
}
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 231b76f..2f3eb24 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -102,6 +102,9 @@
virtual std::string toString(FileId fileId) const = 0;
virtual std::pair<IncFsBlockIndex, IncFsBlockIndex> countFilledBlocks(
const Control& control, std::string_view path) const = 0;
+ virtual incfs::LoadingState isFileFullyLoaded(const Control& control,
+ std::string_view path) const = 0;
+ virtual incfs::LoadingState isEverythingFullyLoaded(const Control& control) const = 0;
virtual ErrorCode link(const Control& control, std::string_view from,
std::string_view to) const = 0;
virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 45b796b..50d1e36 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -366,6 +366,9 @@
MOCK_CONST_METHOD2(countFilledBlocks,
std::pair<IncFsBlockIndex, IncFsBlockIndex>(const Control& control,
std::string_view path));
+ MOCK_CONST_METHOD2(isFileFullyLoaded,
+ incfs::LoadingState(const Control& control, std::string_view path));
+ MOCK_CONST_METHOD1(isEverythingFullyLoaded, incfs::LoadingState(const Control& control));
MOCK_CONST_METHOD3(link,
ErrorCode(const Control& control, std::string_view from,
std::string_view to));
@@ -1563,51 +1566,37 @@
ASSERT_EQ(res, 0);
}
-TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) {
- mIncFs->countFilledBlocksFails();
- mFs->hasNoFile();
-
+TEST_F(IncrementalServiceTest, testIsFileFullyLoadedNoData) {
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
IncrementalService::CreateOptions::CreateNew);
- ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+ EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, _))
+ .Times(1)
+ .WillOnce(Return(incfs::LoadingState::MissingBlocks));
+ ASSERT_GT((int)mIncrementalService->isFileFullyLoaded(storageId, "base.apk"), 0);
}
-TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) {
- mIncFs->countFilledBlocksFails();
- mFs->hasFiles();
-
+TEST_F(IncrementalServiceTest, testIsFileFullyLoadedError) {
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
IncrementalService::CreateOptions::CreateNew);
- EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
- ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
-}
-
-TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) {
- mIncFs->countFilledBlocksEmpty();
- mFs->hasFiles();
-
- TemporaryDir tempDir;
- int storageId =
- mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
- IncrementalService::CreateOptions::CreateNew);
- EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
- ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+ EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, _))
+ .Times(1)
+ .WillOnce(Return(incfs::LoadingState(-1)));
+ ASSERT_LT((int)mIncrementalService->isFileFullyLoaded(storageId, "base.apk"), 0);
}
TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) {
- mIncFs->countFilledBlocksFullyLoaded();
- mFs->hasFiles();
-
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
IncrementalService::CreateOptions::CreateNew);
- EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
- ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+ EXPECT_CALL(*mIncFs, isFileFullyLoaded(_, _))
+ .Times(1)
+ .WillOnce(Return(incfs::LoadingState::Full));
+ ASSERT_EQ(0, (int)mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 7e4bc1e..281c1aa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -75,6 +75,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.platform.test.annotations.LargeTest;
import android.provider.DeviceConfig;
import android.util.SparseBooleanArray;
@@ -286,14 +287,20 @@
doReturn(procState).when(mActivityMangerInternal).getUidProcessState(uid);
SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids();
spyOn(foregroundUids);
+ final boolean contained = foregroundUids.get(uid);
mUidObserver.onUidStateChanged(uid, procState, 0,
ActivityManager.PROCESS_CAPABILITY_NONE);
if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1))
- .put(eq(uid), eq(true));
+ if (!contained) {
+ verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1))
+ .put(eq(uid), eq(true));
+ }
assertTrue(foregroundUids.get(uid));
} else {
- verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid));
+ if (contained) {
+ verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1))
+ .delete(eq(uid));
+ }
assertFalse(foregroundUids.get(uid));
}
waitForNonDelayedMessagesProcessed();
@@ -1993,7 +2000,7 @@
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
- setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs);
Handler handler = mQuotaController.getHandler();
spyOn(handler);
@@ -2017,6 +2024,52 @@
}
}
+ /**
+ * Tests that Timers properly track sessions when an app becomes top and is closed.
+ */
+ @Test
+ public void testIsWithinEJQuotaLocked_TopApp() {
+ setDischarging();
+ JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TopApp", 1);
+ setStandbyBucket(FREQUENT_INDEX, js);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS);
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true);
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+
+ final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, gracePeriodMs);
+ Handler handler = mQuotaController.getHandler();
+ spyOn(handler);
+
+ // Apps on top should be able to schedule & start EJs, even if they're out
+ // of quota (as long as they are in the top grace period).
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ // Still in grace period
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ advanceElapsedClock(6 * SECOND_IN_MILLIS);
+ // Out of grace period.
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinEJQuotaLocked(js));
+ }
+ }
+
@Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
@@ -2657,8 +2710,9 @@
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 86 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 85 * SECOND_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS,
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS,
84 * SECOND_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2697,7 +2751,8 @@
assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs());
assertEquals(86 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs());
assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
- assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJTempAllowlistGracePeriodMs());
+ assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
+ assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
}
@Test
@@ -2737,7 +2792,8 @@
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1);
- setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(0, mQuotaController.getInQuotaBufferMs());
@@ -2773,7 +2829,8 @@
assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs());
assertEquals(5 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs());
assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs());
- assertEquals(0, mQuotaController.getEJTempAllowlistGracePeriodMs());
+ assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs());
+ assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs());
// Invalid configurations.
// In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
@@ -2807,7 +2864,8 @@
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2833,7 +2891,8 @@
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardInteractionMs());
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
- assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJTempAllowlistGracePeriodMs());
+ assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
+ assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
}
/** Tests that TimingSessions aren't saved when the device is charging. */
@@ -3433,7 +3492,7 @@
setDischarging();
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
- setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs);
Handler handler = mQuotaController.getHandler();
spyOn(handler);
@@ -4910,6 +4969,7 @@
@Test
public void testEJTimerTracking_TopAndNonTop() {
setDischarging();
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0);
JobStatus jobBg1 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 1);
JobStatus jobBg2 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 2);
@@ -5032,7 +5092,7 @@
setDischarging();
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
final long gracePeriodMs = 15 * SECOND_IN_MILLIS;
- setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs);
Handler handler = mQuotaController.getHandler();
spyOn(handler);
@@ -5134,11 +5194,158 @@
}
/**
+ * Tests that Timers properly track sessions when TOP state and temp allowlisting overlaps.
+ */
+ @Test
+ @LargeTest
+ public void testEJTimerTracking_TopAndTempAllowlisting() throws Exception {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ final long gracePeriodMs = 5 * SECOND_IN_MILLIS;
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs);
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, gracePeriodMs);
+ Handler handler = mQuotaController.getHandler();
+ spyOn(handler);
+
+ JobStatus job1 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 1);
+ JobStatus job2 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 2);
+ JobStatus job3 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 3);
+ JobStatus job4 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 4);
+ JobStatus job5 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 5);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job1, null);
+ }
+ assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ // Case 1: job starts in TA grace period then app becomes TOP
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ advanceElapsedClock(gracePeriodMs / 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(job1);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ advanceElapsedClock(gracePeriodMs);
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(gracePeriodMs);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job1, job1, true);
+ }
+ assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(gracePeriodMs);
+
+ // Case 2: job starts in TOP grace period then is TAed
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ advanceElapsedClock(gracePeriodMs / 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job2, null);
+ mQuotaController.prepareForExecutionLocked(job2);
+ }
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ advanceElapsedClock(gracePeriodMs);
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(gracePeriodMs);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job2, null, false);
+ }
+ assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(gracePeriodMs);
+
+ // Case 3: job starts in TA grace period then app becomes TOP; job ends after TOP grace
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ advanceElapsedClock(gracePeriodMs / 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job3, null);
+ mQuotaController.prepareForExecutionLocked(job3);
+ }
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ advanceElapsedClock(gracePeriodMs);
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(gracePeriodMs);
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ advanceElapsedClock(gracePeriodMs);
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(2 * gracePeriodMs);
+ advanceElapsedClock(gracePeriodMs);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job3, job3, true);
+ }
+ expected.add(createTimingSession(start, gracePeriodMs, 1));
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(gracePeriodMs);
+
+ // Case 4: job starts in TOP grace period then app becomes TAed; job ends after TA grace
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ advanceElapsedClock(gracePeriodMs / 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job4, null);
+ mQuotaController.prepareForExecutionLocked(job4);
+ }
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ advanceElapsedClock(gracePeriodMs);
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(gracePeriodMs);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ advanceElapsedClock(gracePeriodMs);
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(2 * gracePeriodMs);
+ advanceElapsedClock(gracePeriodMs);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job4, job4, true);
+ }
+ expected.add(createTimingSession(start, gracePeriodMs, 1));
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(gracePeriodMs);
+
+ // Case 5: job starts during overlapping grace period
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ mTempAllowlistListener.onAppAdded(mSourceUid);
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ mTempAllowlistListener.onAppRemoved(mSourceUid);
+ advanceElapsedClock(gracePeriodMs - SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job5, null);
+ mQuotaController.prepareForExecutionLocked(job5);
+ }
+ advanceElapsedClock(SECOND_IN_MILLIS);
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Wait for the grace period to expire so the handler can process the message.
+ Thread.sleep(2 * gracePeriodMs);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job5, job5, true);
+ }
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
* Tests that expedited jobs aren't stopped when an app runs out of quota.
*/
@Test
public void testEJTracking_OutOfQuota_ForegroundAndBackground() {
setDischarging();
+ setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0);
JobStatus jobBg =
createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index e853fd3..17c6b6f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -163,7 +163,7 @@
/** Collection of mocks used for PackageManagerService tests. */
class Mocks {
- val lock = Any()
+ val lock = PackageManagerTracedLock()
val installLock = Any()
val injector: PackageManagerService.Injector = mock()
val systemWrapper: PackageManagerService.SystemWrapper = mock()
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 872b955..29691fb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -65,7 +65,6 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.Locale;
@@ -1115,6 +1114,23 @@
}
@Test
+ public void testSetForceShowMagnifiableBounds() {
+ register(DISPLAY_0);
+
+ mFullScreenMagnificationController.setForceShowMagnifiableBounds(DISPLAY_0, true);
+
+ verify(mMockWindowManager).setForceShowMagnifiableBounds(DISPLAY_0, true);
+ }
+
+ @Test
+ public void testIsForceShowMagnifiableBounds() {
+ register(DISPLAY_0);
+ mFullScreenMagnificationController.setForceShowMagnifiableBounds(DISPLAY_0, true);
+
+ assertTrue(mFullScreenMagnificationController.isForceShowMagnifiableBounds(DISPLAY_0));
+ }
+
+ @Test
public void testSetScale_toMagnifying_shouldNotifyActivatedState() {
setScaleToMagnifying();
@@ -1142,14 +1158,11 @@
for (int i = 0; i < DISPLAY_COUNT; i++) {
when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true);
}
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Region regionArg = (Region) args[1];
- regionArg.set(INITIAL_MAGNIFICATION_REGION);
- return null;
- }
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ Region regionArg = (Region) args[1];
+ regionArg.set(INITIAL_MAGNIFICATION_REGION);
+ return null;
}).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject());
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 84c76b7..cf23197 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -174,6 +175,23 @@
}
@Test
+ public void transitionToWindowMode_disablingWindowMode_showMagnificationButton()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMockConnection.invokeCallbacks();
+ verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+ eq(MODE_WINDOW));
+ }
+
+ @Test
public void transitionToFullScreenMode_windowMagnifying_disableWindowAndEnableFullScreen()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -228,7 +246,6 @@
verify(mTransitionCallBack).onResult(true);
}
-
@Test
public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
throws RemoteException {
@@ -365,6 +382,19 @@
eq(MODE_FULLSCREEN));
}
+
+ @Test
+ public void onTouchInteractionChanged_fullscreenNotActivated_notShowMagnificationButton()
+ throws RemoteException {
+ setMagnificationModeSettings(MODE_FULLSCREEN);
+
+ mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
+ mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
+
+ verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ eq(MODE_FULLSCREEN));
+ }
+
@Test
public void onShortcutTriggered_windowModeEnabledAndCapabilitiesAll_showMagnificationButton()
throws RemoteException {
@@ -427,6 +457,35 @@
verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
}
+ @Test
+ public void transitionToFullScreenMode_fullscreenModeActivated_showMagnificationButton()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN, mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+ eq(MODE_FULLSCREEN));
+ }
+
+ @Test
+ public void transitionToWindow_windowModeActivated_showMagnificationButton()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW, mTransitionCallBack);
+
+ verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY),
+ mCallbackArgumentCaptor.capture());
+ mCallbackArgumentCaptor.getValue().onResult(true);
+ mMockConnection.invokeCallbacks();
+ verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+ eq(MODE_WINDOW));
+ }
+
private void setMagnificationEnabled(int mode) throws RemoteException {
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
@@ -450,6 +509,8 @@
}
if (mode == MODE_FULLSCREEN) {
when(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)).thenReturn(true);
+ when(mScreenMagnificationController.isForceShowMagnifiableBounds(
+ TEST_DISPLAY)).thenReturn(true);
when(mScreenMagnificationController.getPersistedScale()).thenReturn(DEFAULT_SCALE);
when(mScreenMagnificationController.getScale(TEST_DISPLAY)).thenReturn(DEFAULT_SCALE);
when(mScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(
@@ -457,6 +518,11 @@
when(mScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(
centerY);
} else {
+ doAnswer(invocation -> {
+ when(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)).thenReturn(true);
+ return null;
+ }).when(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+ eq(DEFAULT_SCALE), anyFloat(), anyFloat(), anyBoolean(), anyInt());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
centerX, centerY, null);
mMockConnection.invokeCallbacks();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 7a4b901..a6d146e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -39,6 +39,7 @@
import android.app.trust.ITrustManager;
import android.content.Context;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -292,9 +293,18 @@
}
});
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
mFingerprintSensorProps.add(new FingerprintSensorPropertiesInternal(id,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
+ componentInfo,
type,
false /* resetLockoutRequiresHardwareAuthToken */));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 77a39d8..576f9c2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4016,6 +4016,27 @@
}
@Test
+ public void testUpdateNetworkPreferenceOnStartOnStopUser() throws Exception {
+ dpms.handleStartUser(CALLER_USER_HANDLE);
+ // TODO(b/178655595)
+ // verify(getServices().connectivityManager, times(1)).setNetworkPreferenceForUser(
+ // any(UserHandle.class),
+ // anyInt(),
+ // any(Executor.class),
+ // any(Runnable.class)
+ //);
+
+ dpms.handleStopUser(CALLER_USER_HANDLE);
+ // TODO(b/178655595)
+ // verify(getServices().connectivityManager, times(1)).setNetworkPreferenceForUser(
+ // any(UserHandle.class),
+ // eq(ConnectivityManager.USER_PREFERENCE_SYSTEM_DEFAULT),
+ // any(Executor.class),
+ // any(Runnable.class)
+ //);
+ }
+
+ @Test
public void testGetSetNetworkSlicing() throws Exception {
assertExpectException(SecurityException.class, null,
() -> dpm.setNetworkSlicingEnabled(false));
@@ -4023,20 +4044,26 @@
assertExpectException(SecurityException.class, null,
() -> dpm.isNetworkSlicingEnabled());
- assertExpectException(SecurityException.class, null,
- () -> dpm.isNetworkSlicingEnabledForUser(UserHandle.of(CALLER_USER_HANDLE)));
-
- mContext.callerPermissions.add(permission.READ_NETWORK_DEVICE_CONFIG);
- mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- try {
- dpm.isNetworkSlicingEnabledForUser(UserHandle.of(CALLER_USER_HANDLE));
- } catch (SecurityException se) {
- fail("Threw SecurityException with right permission");
- }
-
setupProfileOwner();
dpm.setNetworkSlicingEnabled(false);
assertThat(dpm.isNetworkSlicingEnabled()).isFalse();
+ // TODO(b/178655595)
+ // verify(getServices().connectivityManager, times(1)).setNetworkPreferenceForUser(
+ // any(UserHandle.class),
+ // eq(ConnectivityManager.USER_PREFERENCE_SYSTEM_DEFAULT),
+ // any(Executor.class),
+ // any(Runnable.class)
+ //);
+
+ dpm.setNetworkSlicingEnabled(true);
+ assertThat(dpm.isNetworkSlicingEnabled()).isTrue();
+ // TODO(b/178655595)
+ // verify(getServices().connectivityManager, times(1)).setNetworkPreferenceForUser(
+ // any(UserHandle.class),
+ // eq(ConnectivityManager.USER_PREFERENCE_ENTERPRISE),
+ // any(Executor.class),
+ // any(Runnable.class)
+ //);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 6068fdf..2fcc021 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -224,6 +224,8 @@
return mMockSystemServices.accountManager;
case Context.TELEPHONY_SERVICE:
return mMockSystemServices.telephonyManager;
+ case Context.CONNECTIVITY_SERVICE:
+ return mMockSystemServices.connectivityManager;
case Context.APP_OPS_SERVICE:
return mMockSystemServices.appOpsManager;
case Context.CROSS_PROFILE_APPS_SERVICE:
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index f6dee38..9cc0572 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -47,6 +47,7 @@
import android.database.Cursor;
import android.hardware.usb.UsbManager;
import android.media.IAudioService;
+import android.net.ConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.Uri;
import android.net.wifi.WifiManager;
@@ -114,6 +115,7 @@
public final SettingsForMock settings;
public final MockContentResolver contentResolver;
public final TelephonyManager telephonyManager;
+ public final ConnectivityManager connectivityManager;
public final AccountManager accountManager;
public final AlarmManager alarmManager;
public final KeyChain.KeyChainConnection keyChainConnection;
@@ -159,6 +161,7 @@
wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
telephonyManager = mock(TelephonyManager.class);
+ connectivityManager = mock(ConnectivityManager.class);
accountManager = mock(AccountManager.class);
alarmManager = mock(AlarmManager.class);
keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
index 0784b7a..415e635 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -25,6 +25,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
import android.content.integrity.AppInstallMetadata;
import org.junit.Test;
@@ -163,6 +165,22 @@
new RuleIndexRange(900, 945));
}
+ @Test
+ public void verifyIndexingFileIsCorrupt() throws IOException {
+ byte[] stringBytes =
+ getBytes(
+ getKeyValueString(START_INDEXING_KEY, 100)
+ + getKeyValueString("ccc", 200)
+ + getKeyValueString(END_INDEXING_KEY, 300)
+ + getKeyValueString(END_INDEXING_KEY, 900));
+ ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+ rule.put(stringBytes);
+ InputStream inputStream = new ByteArrayInputStream(rule.array());
+
+ assertThrows(IllegalStateException.class,
+ () -> new RuleIndexingController(inputStream));
+ }
+
private static InputStream obtainDefaultIndexingMapForTest() {
byte[] stringBytes =
getBytes(
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 59458e8..d63a467 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -59,10 +59,10 @@
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.LocalServices;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.utils.WatchableTester;
import com.google.common.truth.Truth;
@@ -1197,7 +1197,7 @@
private Settings makeSettings() {
return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider,
- mDomainVerificationManager, new Object());
+ mDomainVerificationManager, new PackageManagerTracedLock());
}
private void verifyKeySetMetaData(Settings settings)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index ba60111..128cbaa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -48,6 +48,7 @@
import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -854,7 +855,7 @@
.addProvider(new ParsedProvider())
.addService(new ParsedService())
.addInstrumentation(new ParsedInstrumentation())
- .addRequestedPermission("foo7")
+ .addUsesPermission(new ParsedUsesPermission("foo7", 0))
.addImplicitPermission("foo25")
.addProtectedBroadcast("foo8")
.setStaticSharedLibName("foo23")
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index b5add84..8e1fc16 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -43,6 +43,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.component.ParsedUsesPermission;
import android.content.res.TypedArray;
import android.os.Environment;
import android.os.UserHandle;
@@ -429,7 +430,7 @@
@Test
public void factoryTestFlagSet() throws Exception {
final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
- .addRequestedPermission(Manifest.permission.FACTORY_TEST);
+ .addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1905e2f..ec3a1af 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4038,6 +4038,24 @@
}
@Test
+ public void testVisitUris_audioContentsString() throws Exception {
+ final Uri audioContents = Uri.parse("content://com.example/audio");
+
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_AUDIO_CONTENTS_URI, audioContents.toString());
+
+ Notification n = new Notification.Builder(mContext, "a")
+ .setContentTitle("notification with uris")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addExtras(extras)
+ .build();
+
+ Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+ n.visitUris(visitor);
+ verify(visitor, times(1)).accept(eq(audioContents));
+ }
+
+ @Test
public void testSetNotificationPolicy_preP_setOldFields() {
ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = mZenModeHelper;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c19f348..96ebd24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1687,6 +1687,7 @@
public void testIsSnapshotCompatible() {
final ActivityRecord activity = createActivityWithTask();
final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
+ .setTopActivityComponent(activity.mActivityComponent)
.setRotation(activity.getWindowConfiguration().getRotation())
.build();
@@ -1697,6 +1698,26 @@
assertFalse(activity.isSnapshotCompatible(snapshot));
}
+ /**
+ * Test that the snapshot should be obsoleted if the top activity changed.
+ */
+ @Test
+ public void testIsSnapshotCompatibleTopActivityChanged() {
+ final ActivityRecord activity = createActivityWithTask();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask())
+ .setOnTop(true)
+ .build();
+ final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
+ .setTopActivityComponent(secondActivity.mActivityComponent)
+ .build();
+
+ assertTrue(secondActivity.isSnapshotCompatible(snapshot));
+
+ // Emulate the top activity changed.
+ assertFalse(activity.isSnapshotCompatible(snapshot));
+ }
+
@Test
public void testFixedRotationSnapshotStartingWindow() {
final ActivityRecord activity = createActivityWithTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 67fe7bf..33bcc5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -119,6 +119,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
+import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -1611,6 +1612,54 @@
}
@Test
+ public void testShellTransitRotation() {
+ DisplayContent dc = createNewDisplay();
+
+ // Set-up mock shell transitions
+ final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
+ mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
+ mAtm.getTransitionController().registerTransitionPlayer(testPlayer);
+
+ final DisplayRotation dr = dc.getDisplayRotation();
+ doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
+ // Rotate 180 degree so the display doesn't have configuration change. This condition is
+ // used for the later verification of stop-freezing (without setting mWaitingForConfig).
+ doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
+ mWm.mDisplayRotationController =
+ new IDisplayWindowRotationController.Stub() {
+ @Override
+ public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
+ IDisplayWindowRotationCallback callback) {
+ try {
+ callback.continueRotateDisplay(toRotation, null);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+ };
+
+ // kill any existing rotation animation (vestigial from test setup).
+ dc.setRotationAnimation(null);
+
+ final int origRot = dc.getConfiguration().windowConfiguration.getRotation();
+
+ mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */);
+ // Should create a transition request without performing rotation
+ assertNotNull(testPlayer.mLastRequest);
+ assertEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
+
+ // Once transition starts, rotation is applied and transition shows DC rotating.
+ testPlayer.start();
+ assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
+ assertNotNull(testPlayer.mLastReady);
+ assertEquals(dc, DisplayRotation.getDisplayFromTransition(testPlayer.mLastTransit));
+ WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken();
+ assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(),
+ testPlayer.mLastReady.getChange(dcToken).getStartRotation());
+ testPlayer.finish();
+ }
+
+ @Test
public void testGetOrCreateRootHomeTask_defaultDisplay() {
TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index edf7056..b5219fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.window.TaskSnapshot;
import android.content.ComponentName;
import android.content.ContextWrapper;
import android.content.res.Resources;
@@ -42,6 +41,7 @@
import android.hardware.HardwareBuffer;
import android.os.UserManager;
import android.view.Surface;
+import android.window.TaskSnapshot;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -154,10 +154,16 @@
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
private int mSystemUiVisibility = 0;
private int mRotation = Surface.ROTATION_0;
+ private ComponentName mTopActivityComponent = new ComponentName("", "");
TaskSnapshotBuilder() {
}
+ TaskSnapshotBuilder setTopActivityComponent(ComponentName topActivityComponent) {
+ mTopActivityComponent = topActivityComponent;
+ return this;
+ }
+
TaskSnapshotBuilder setScaleFraction(float scale) {
mScaleFraction = scale;
return this;
@@ -199,7 +205,7 @@
Canvas c = buffer.lockCanvas();
c.drawColor(Color.RED);
buffer.unlockCanvasAndPost(c);
- return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""),
+ return new TaskSnapshot(MOCK_SNAPSHOT_ID, mTopActivityComponent,
HardwareBuffer.createFromGraphicBuffer(buffer),
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
mRotation, taskSize, TEST_INSETS,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 779457b..b210dfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -60,6 +60,7 @@
import static org.mockito.Mockito.mock;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -90,7 +91,10 @@
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.window.ITaskOrganizer;
+import android.window.ITransitionPlayer;
import android.window.StartingWindowInfo;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
import com.android.internal.policy.AttributeCache;
import com.android.internal.util.ArrayUtils;
@@ -1359,4 +1363,48 @@
mHasSurface = hadSurface;
}
}
+
+ class TestTransitionPlayer extends ITransitionPlayer.Stub {
+ final TransitionController mController;
+ final WindowOrganizerController mOrganizer;
+ Transition mLastTransit = null;
+ TransitionRequestInfo mLastRequest = null;
+ TransitionInfo mLastReady = null;
+
+ TestTransitionPlayer(@NonNull TransitionController controller,
+ @NonNull WindowOrganizerController organizer) {
+ mController = controller;
+ mOrganizer = organizer;
+ }
+
+ void clear() {
+ mLastTransit = null;
+ mLastReady = null;
+ mLastRequest = null;
+ }
+
+ @Override
+ public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
+ SurfaceControl.Transaction transaction) throws RemoteException {
+ mLastTransit = Transition.fromBinder(transitToken);
+ mLastReady = transitionInfo;
+ }
+
+ @Override
+ public void requestStartTransition(IBinder transitToken,
+ TransitionRequestInfo request) throws RemoteException {
+ mLastTransit = Transition.fromBinder(transitToken);
+ mLastRequest = request;
+ }
+
+ public void start() {
+ mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null);
+ mLastTransit.onTransactionReady(mLastTransit.getSyncId(),
+ mock(SurfaceControl.Transaction.class));
+ }
+
+ public void finish() {
+ mController.finishTransition(mLastTransit);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 74421a0..04a0aba 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -44,8 +44,6 @@
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.TimeUnit;
/**
@@ -116,31 +114,17 @@
*/
public static final int USSD_OVER_IMS_ONLY = 3;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "CARRIER_NR_AVAILABILITY_" }, value = {
- CARRIER_NR_AVAILABILITY_NONE,
- CARRIER_NR_AVAILABILITY_NSA,
- CARRIER_NR_AVAILABILITY_SA,
- })
- public @interface DeviceNrCapability {}
-
- /**
- * Indicates CARRIER_NR_AVAILABILITY_NONE determine that the carrier does not enable 5G NR.
- */
- public static final int CARRIER_NR_AVAILABILITY_NONE = 0;
-
/**
* Indicates CARRIER_NR_AVAILABILITY_NSA determine that the carrier enable the non-standalone
* (NSA) mode of 5G NR.
*/
- public static final int CARRIER_NR_AVAILABILITY_NSA = 1 << 0;
+ public static final int CARRIER_NR_AVAILABILITY_NSA = 1;
/**
* Indicates CARRIER_NR_AVAILABILITY_SA determine that the carrier enable the standalone (SA)
* mode of 5G NR.
*/
- public static final int CARRIER_NR_AVAILABILITY_SA = 1 << 1;
+ public static final int CARRIER_NR_AVAILABILITY_SA = 2;
private final Context mContext;
@@ -1882,23 +1866,20 @@
"show_precise_failed_cause_bool";
/**
- * Bit-field integer to determine whether the carrier enable the non-standalone (NSA) mode of
- * 5G NR, standalone (SA) mode of 5G NR
+ * A list of carrier nr availability is used to determine whether the carrier enable the
+ * non-standalone (NSA) mode of 5G NR, standalone (SA) mode of 5G NR
*
- * <UL>
- * <LI>CARRIER_NR_AVAILABILITY_NONE: non-NR = 0 </LI>
- * <LI>CARRIER_NR_AVAILABILITY_NSA: NSA = 1 << 0</LI>
- * <LI>CARRIER_NR_AVAILABILITY_SA: SA = 1 << 1</LI>
- * </UL>
- * <p> The value of this key must be bitwise OR of
- * {@link #CARRIER_NR_AVAILABILITY_NONE}, {@link #CARRIER_NR_AVAILABILITY_NSA},
- * {@link #CARRIER_NR_AVAILABILITY_SA}.
+ * <p> The value of list is
+ * {@link #CARRIER_NR_AVAILABILITY_NSA}, or {@link #CARRIER_NR_AVAILABILITY_SA}.
*
- * <p> For example, if both NSA and SA are used, the value of key is 3 (1 << 0 | 1 << 1).
- * If the carrier doesn't support 5G NR, the value of key is 0 (non-NR).
- * If the key is invalid or not configured, a default value 3 (NSA|SA = 3) will apply.
+ * <p> For example, if both NSA and SA are used, the list value is {
+ * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}}.
+ * If the carrier doesn't support 5G NR, the value is the empty array.
+ * If the key is invalid or not configured, the default value {
+ * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}} will apply.
*/
- public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int";
+ public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY =
+ "carrier_nr_availabilities_int_array";
/**
* Boolean to decide whether LTE is enabled.
@@ -3784,13 +3765,64 @@
/** Prefix of all ImsServiceEntitlement.KEY_* constants. */
public static final String KEY_PREFIX = "imsserviceentitlement.";
- /** The address of the entitlement configuration server. */
+ /**
+ * The address of the entitlement configuration server.
+ *
+ * Reference: GSMA TS.43-v5, section 2.1 Default Entitlement Configuration Server.
+ */
public static final String KEY_ENTITLEMENT_SERVER_URL_STRING =
KEY_PREFIX + "entitlement_server_url_string";
+ /**
+ * For some carriers, end-users may be presented with a web portal of the carrier before
+ * being allowed to use the VoWiFi service.
+ * To support this feature, the app hosts a {@link android.webkit.WebView} in the foreground
+ * VoWiFi entitlement configuration flow to show the web portal.
+ *
+ * {@code true} - show the VoWiFi portal in a webview.
+ *
+ * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING}
+ * is set to this app.
+ *
+ * Reference: GSMA TS.43-v5, section 3, VoWiFi entitlement configuration.
+ */
+ public static final String KEY_SHOW_VOWIFI_WEBVIEW_BOOL =
+ KEY_PREFIX + "show_vowifi_webview_bool";
+
+ /**
+ * For some carriers, the network is not provisioned by default to support
+ * IMS (VoLTE/VoWiFi/SMSoIP) service for all end users. Some type of network-side
+ * provisioning must then take place before offering the IMS service to the end-user.
+ *
+ * {@code true} - need this ImsServiceEntitlement app to do IMS (VoLTE/VoWiFi/SMSoIP)
+ * provisioning in the background before offering the IMS service to the end-user.
+ *
+ * Note: this is effective only if the carrier needs IMS provisioning, i.e.
+ * {@link #KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL} is set to true.
+ *
+ * Reference: GSMA TS.43-v5, section 3 - 5, VoWiFi/VoLTE/SMSoIP entitlement configuration.
+ */
+ public static final String KEY_IMS_PROVISIONING_BOOL = KEY_PREFIX + "ims_provisioning_bool";
+
+ /**
+ * The FCM sender ID for the carrier.
+ * Used to trigger a carrier network requested entitlement configuration
+ * via Firebase Cloud Messaging (FCM). Do not set if the carrier doesn't use FCM for network
+ * requested entitlement configuration.
+ *
+ * Reference: GSMA TS.43-v5, section 2.4, Network Requested Entitlement Configuration.
+ *
+ * @see <a href="https://firebase.google.com/docs/cloud-messaging/concept-options#senderid">
+ * About FCM messages - Credentials</a>
+ */
+ public static final String KEY_FCM_SENDER_ID_STRING = KEY_PREFIX + "fcm_sender_id_string";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, "");
+ defaults.putString(KEY_FCM_SENDER_ID_STRING, "");
+ defaults.putBoolean(KEY_SHOW_VOWIFI_WEBVIEW_BOOL, false);
+ defaults.putBoolean(KEY_IMS_PROVISIONING_BOOL, false);
return defaults;
}
}
@@ -5199,8 +5231,8 @@
sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
- sDefaults.putInt(KEY_CARRIER_NR_AVAILABILITY_INT,
- CARRIER_NR_AVAILABILITY_NSA | CARRIER_NR_AVAILABILITY_SA);
+ sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+ new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/data/NetworkSliceInfo.java b/telephony/java/android/telephony/data/NetworkSliceInfo.java
index 3383696..1d90095 100644
--- a/telephony/java/android/telephony/data/NetworkSliceInfo.java
+++ b/telephony/java/android/telephony/data/NetworkSliceInfo.java
@@ -29,7 +29,12 @@
import java.util.Objects;
/**
- * Represents a S-NSSAI as defined in 3GPP TS 24.501.
+ * Represents a S-NSSAI as defined in 3GPP TS 24.501, which represents a network slice.
+ *
+ * There are 2 main fields that define a slice, SliceServiceType and SliceDifferentiator.
+ * SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is
+ * used to differentiate between multiple slices of the same type. If the devices is not on HPLMN,
+ * the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN.
*
* @hide
*/