Merge "[DO NOT MERGE] Prevent RemoteViews crashing SystemUi" into udc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index e38e21f..0650ce3 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1970,7 +1970,7 @@
} break;
case MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR: {
updatePreIdleFactor();
- maybeDoImmediateMaintenance();
+ maybeDoImmediateMaintenance("idle factor");
} break;
case MSG_REPORT_STATIONARY_STATUS: {
final DeviceIdleInternal.StationaryListener newListener =
@@ -3517,11 +3517,11 @@
// doze alarm to after the upcoming AlarmClock alarm.
scheduleAlarmLocked(
mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
- + mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+ + mConstants.QUICK_DOZE_DELAY_TIMEOUT);
} else {
// Wait a small amount of time in case something (eg: background service from
// recently closed app) needs to finish running.
- scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+ scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT);
}
} else if (mState == STATE_ACTIVE) {
moveToStateLocked(STATE_INACTIVE, "no activity");
@@ -3536,9 +3536,9 @@
// alarm to after the upcoming AlarmClock alarm.
scheduleAlarmLocked(
mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
- + delay, false);
+ + delay);
} else {
- scheduleAlarmLocked(delay, false);
+ scheduleAlarmLocked(delay);
}
}
}
@@ -3753,7 +3753,7 @@
if (shouldUseIdleTimeoutFactorLocked()) {
delay = (long) (mPreIdleFactor * delay);
}
- scheduleAlarmLocked(delay, false);
+ scheduleAlarmLocked(delay);
moveToStateLocked(STATE_IDLE_PENDING, reason);
break;
case STATE_IDLE_PENDING:
@@ -3779,7 +3779,7 @@
case STATE_SENSING:
cancelSensingTimeoutAlarmLocked();
moveToStateLocked(STATE_LOCATING, reason);
- scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
+ scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
LocationManager locationManager = mInjector.getLocationManager();
if (locationManager != null
&& locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
@@ -3819,7 +3819,7 @@
// Everything is in place to go into IDLE state.
case STATE_IDLE_MAINTENANCE:
moveToStateLocked(STATE_IDLE, reason);
- scheduleAlarmLocked(mNextIdleDelay, true);
+ scheduleAlarmLocked(mNextIdleDelay);
if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
" ms.");
mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
@@ -3842,7 +3842,7 @@
mActiveIdleOpCount = 1;
mActiveIdleWakeLock.acquire();
moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
- scheduleAlarmLocked(mNextIdlePendingDelay, false);
+ scheduleAlarmLocked(mNextIdlePendingDelay);
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
"Next alarm in " + mNextIdlePendingDelay + " ms.");
mMaintenanceStartTime = SystemClock.elapsedRealtime();
@@ -4013,19 +4013,18 @@
if (Math.abs(delay - newDelay) < MIN_STATE_STEP_ALARM_CHANGE) {
return;
}
- scheduleAlarmLocked(newDelay, false);
+ scheduleAlarmLocked(newDelay);
}
}
}
- private void maybeDoImmediateMaintenance() {
+ private void maybeDoImmediateMaintenance(String reason) {
synchronized (this) {
if (mState == STATE_IDLE) {
long duration = SystemClock.elapsedRealtime() - mIdleStartTime;
- /* Let's trgger a immediate maintenance,
- * if it has been idle for a long time */
+ // Trigger an immediate maintenance window if it has been IDLE for long enough.
if (duration > mConstants.IDLE_TIMEOUT) {
- scheduleAlarmLocked(0, false);
+ stepIdleStateLocked(reason);
}
}
}
@@ -4045,7 +4044,7 @@
void setIdleStartTimeForTest(long idleStartTime) {
synchronized (this) {
mIdleStartTime = idleStartTime;
- maybeDoImmediateMaintenance();
+ maybeDoImmediateMaintenance("testing");
}
}
@@ -4224,8 +4223,9 @@
}
@GuardedBy("this")
- void scheduleAlarmLocked(long delay, boolean idleUntil) {
- if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
+ @VisibleForTesting
+ void scheduleAlarmLocked(long delay) {
+ if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + stateToString(mState) + ")");
if (mUseMotionSensor && mMotionSensor == null
&& mState != STATE_QUICK_DOZE_DELAY
@@ -4241,7 +4241,7 @@
return;
}
mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
- if (idleUntil) {
+ if (mState == STATE_IDLE) {
mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
} else if (mState == STATE_LOCATING) {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 394be6e..26c0eef 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -30,6 +30,9 @@
import static android.app.AlarmManager.INTERVAL_HOUR;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.content.PermissionChecker.PID_UNKNOWN;
+import static android.content.PermissionChecker.checkPermissionForPreflight;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.os.PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
import static android.os.PowerExemptionManager.REASON_DENIED;
@@ -87,11 +90,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserPackage;
-import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryStatsInternal;
@@ -182,7 +183,6 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
-import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
@@ -269,7 +269,8 @@
/**
* A map from uid to the last op-mode we have seen for
- * {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM}
+ * {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM}. Used for evaluating permission state change
+ * when the denylist changes.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -2097,20 +2098,31 @@
if (oldMode == newMode) {
return;
}
- final boolean allowedByDefault =
- isScheduleExactAlarmAllowedByDefault(packageName, uid);
+ final boolean deniedByDefault = isScheduleExactAlarmDeniedByDefault(
+ packageName, UserHandle.getUserId(uid));
final boolean hadPermission;
- if (oldMode != AppOpsManager.MODE_DEFAULT) {
- hadPermission = (oldMode == AppOpsManager.MODE_ALLOWED);
- } else {
- hadPermission = allowedByDefault;
- }
final boolean hasPermission;
- if (newMode != AppOpsManager.MODE_DEFAULT) {
- hasPermission = (newMode == AppOpsManager.MODE_ALLOWED);
+
+ if (deniedByDefault) {
+ final boolean permissionState = getContext().checkPermission(
+ Manifest.permission.SCHEDULE_EXACT_ALARM, PID_UNKNOWN,
+ uid) == PackageManager.PERMISSION_GRANTED;
+ hadPermission = (oldMode == AppOpsManager.MODE_DEFAULT)
+ ? permissionState
+ : (oldMode == AppOpsManager.MODE_ALLOWED);
+ hasPermission = (newMode == AppOpsManager.MODE_DEFAULT)
+ ? permissionState
+ : (newMode == AppOpsManager.MODE_ALLOWED);
} else {
- hasPermission = allowedByDefault;
+ final boolean allowedByDefault =
+ !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
+ hadPermission = (oldMode == AppOpsManager.MODE_DEFAULT)
+ ? allowedByDefault
+ : (oldMode == AppOpsManager.MODE_ALLOWED);
+ hasPermission = (newMode == AppOpsManager.MODE_DEFAULT)
+ ? allowedByDefault
+ : (newMode == AppOpsManager.MODE_ALLOWED);
}
if (hadPermission && !hasPermission) {
@@ -2754,41 +2766,13 @@
boolean hasUseExactAlarmInternal(String packageName, int uid) {
return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid))
- && (PermissionChecker.checkPermissionForPreflight(getContext(),
- Manifest.permission.USE_EXACT_ALARM, PermissionChecker.PID_UNKNOWN, uid,
- packageName) == PermissionChecker.PERMISSION_GRANTED);
- }
-
- /**
- * Returns whether SCHEDULE_EXACT_ALARM is allowed by default.
- */
- boolean isScheduleExactAlarmAllowedByDefault(String packageName, int uid) {
- if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) {
-
- // This is essentially like changing the protection level of the permission to
- // (privileged|signature|role|appop), but have to implement this logic to maintain
- // compatibility for older apps.
- if (mPackageManagerInternal.isPlatformSigned(packageName)
- || mPackageManagerInternal.isUidPrivileged(uid)) {
- return true;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- final List<String> wellbeingHolders = (mRoleManager != null)
- ? mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)
- : Collections.emptyList();
- return wellbeingHolders.contains(packageName);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- return !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
+ && (checkPermissionForPreflight(getContext(), Manifest.permission.USE_EXACT_ALARM,
+ PID_UNKNOWN, uid, packageName) == PERMISSION_GRANTED);
}
boolean hasScheduleExactAlarmInternal(String packageName, int uid) {
final long start = mStatLogger.getTime();
- // Not using getScheduleExactAlarmState as this can avoid some calls to AppOpsService.
// Not using #mLastOpScheduleExactAlarm as it may contain stale values.
// No locking needed as all internal containers being queried are immutable.
final boolean hasPermission;
@@ -2796,11 +2780,16 @@
hasPermission = false;
} else if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
hasPermission = false;
+ } else if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) {
+ hasPermission = (checkPermissionForPreflight(getContext(),
+ Manifest.permission.SCHEDULE_EXACT_ALARM, PID_UNKNOWN, uid, packageName)
+ == PERMISSION_GRANTED);
} else {
+ // Compatibility permission check for older apps.
final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
packageName);
if (mode == AppOpsManager.MODE_DEFAULT) {
- hasPermission = isScheduleExactAlarmAllowedByDefault(packageName, uid);
+ hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
} else {
hasPermission = (mode == AppOpsManager.MODE_ALLOWED);
}
@@ -4685,10 +4674,6 @@
return service.new ClockReceiver();
}
- void registerContentObserver(ContentObserver contentObserver, Uri uri) {
- mContext.getContentResolver().registerContentObserver(uri, false, contentObserver);
- }
-
void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ALARM_MANAGER,
AppSchedulingModuleThread.getExecutor(), listener);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3cc67e7..056b6b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -429,6 +429,7 @@
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
+ boolean persistenceUpdated = false;
boolean runtimeUpdated = false;
for (int controller = 0; controller < mControllers.size(); controller++) {
final StateController sc = mControllers.get(controller);
@@ -478,19 +479,23 @@
case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS:
case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS:
case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS:
- case Constants.KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS:
- case Constants.KEY_RUNTIME_USER_INITIATED_LIMIT_MS:
- case Constants.KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR:
- case Constants.KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS:
- case Constants.KEY_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS:
+ case Constants.KEY_RUNTIME_MIN_UI_GUARANTEE_MS:
+ case Constants.KEY_RUNTIME_UI_LIMIT_MS:
+ case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR:
+ case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS:
+ case Constants.KEY_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS:
if (!runtimeUpdated) {
mConstants.updateRuntimeConstantsLocked();
runtimeUpdated = true;
}
break;
+ case Constants.KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS:
case Constants.KEY_PERSIST_IN_SPLIT_FILES:
- mConstants.updatePersistingConstantsLocked();
- mJobs.setUseSplitFiles(mConstants.PERSIST_IN_SPLIT_FILES);
+ if (!persistenceUpdated) {
+ mConstants.updatePersistingConstantsLocked();
+ mJobs.setUseSplitFiles(mConstants.PERSIST_IN_SPLIT_FILES);
+ persistenceUpdated = true;
+ }
break;
default:
if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY)
@@ -572,20 +577,20 @@
"runtime_free_quota_max_limit_ms";
private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms";
private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms";
- private static final String KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS =
- "runtime_min_user_initiated_guarantee_ms";
- private static final String KEY_RUNTIME_USER_INITIATED_LIMIT_MS =
- "runtime_user_initiated_limit_ms";
- private static final String
- KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
- "runtime_min_user_initiated_data_transfer_guarantee_buffer_factor";
- private static final String KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS =
- "runtime_min_user_initiated_data_transfer_guarantee_ms";
- private static final String KEY_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS =
- "runtime_user_initiated_data_transfer_limit_ms";
+ private static final String KEY_RUNTIME_MIN_UI_GUARANTEE_MS = "runtime_min_ui_guarantee_ms";
+ private static final String KEY_RUNTIME_UI_LIMIT_MS = "runtime_ui_limit_ms";
+ private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
+ "runtime_min_ui_data_transfer_guarantee_buffer_factor";
+ private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS =
+ "runtime_min_ui_data_transfer_guarantee_ms";
+ private static final String KEY_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS =
+ "runtime_ui_data_transfer_limit_ms";
private static final String KEY_PERSIST_IN_SPLIT_FILES = "persist_in_split_files";
+ private static final String KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS =
+ "max_num_persisted_job_work_items";
+
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
@@ -610,17 +615,18 @@
public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
@VisibleForTesting
public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
- public static final long DEFAULT_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS =
+ public static final long DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS =
Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS);
- public static final long DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS =
+ public static final long DEFAULT_RUNTIME_UI_LIMIT_MS =
Math.max(60 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS);
- public static final float
- DEFAULT_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 1.35f;
- public static final long DEFAULT_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS =
- Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS);
- public static final long DEFAULT_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS =
- Math.min(Long.MAX_VALUE, DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS);
+ public static final float DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
+ 1.35f;
+ public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS =
+ Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS);
+ public static final long DEFAULT_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS =
+ Math.min(Long.MAX_VALUE, DEFAULT_RUNTIME_UI_LIMIT_MS);
static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true;
+ static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -731,33 +737,31 @@
/**
* The minimum amount of time we try to guarantee normal user-initiated jobs will run for.
*/
- public long RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS =
- DEFAULT_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS;
+ public long RUNTIME_MIN_UI_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS;
/**
* The maximum amount of time we will let a user-initiated job run for. This will only
* apply if there are no other limits that apply to the specific user-initiated job.
*/
- public long RUNTIME_USER_INITIATED_LIMIT_MS = DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS;
+ public long RUNTIME_UI_LIMIT_MS = DEFAULT_RUNTIME_UI_LIMIT_MS;
/**
* A factor to apply to estimated transfer durations for user-initiated data transfer jobs
* so that we give some extra time for unexpected situations. This will be at least 1 and
* so can just be multiplied with the original value to get the final value.
*/
- public float RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
- DEFAULT_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR;
+ public float RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
+ DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR;
/**
* The minimum amount of time we try to guarantee user-initiated data transfer jobs
* will run for.
*/
- public long RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS =
- DEFAULT_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS;
+ public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS =
+ DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS;
/** The maximum amount of time we will let a user-initiated data transfer job run for. */
- public long RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS =
- DEFAULT_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS;
+ public long RUNTIME_UI_DATA_TRANSFER_LIMIT_MS = DEFAULT_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS;
/**
* Whether to persist jobs in split files (by UID). If false, all persisted jobs will be
@@ -766,6 +770,11 @@
public boolean PERSIST_IN_SPLIT_FILES = DEFAULT_PERSIST_IN_SPLIT_FILES;
/**
+ * The maximum number of {@link JobWorkItem JobWorkItems} that can be persisted per job.
+ */
+ public int MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS;
+
+ /**
* If true, use TARE policy for job limiting. If false, use quotas.
*/
public boolean USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER
@@ -827,6 +836,10 @@
private void updatePersistingConstantsLocked() {
PERSIST_IN_SPLIT_FILES = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_PERSIST_IN_SPLIT_FILES, DEFAULT_PERSIST_IN_SPLIT_FILES);
+ MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS,
+ DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS);
}
private void updatePrefetchConstantsLocked() {
@@ -862,11 +875,11 @@
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS,
- KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
- KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
- KEY_RUNTIME_USER_INITIATED_LIMIT_MS,
- KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
- KEY_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS);
+ KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
+ KEY_RUNTIME_MIN_UI_GUARANTEE_MS,
+ KEY_RUNTIME_UI_LIMIT_MS,
+ KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
+ KEY_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS);
// Make sure min runtime for regular jobs is at least 10 minutes.
RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS,
@@ -880,37 +893,35 @@
properties.getLong(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
// Make sure min runtime is at least as long as regular jobs.
- RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
+ RUNTIME_MIN_UI_GUARANTEE_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
properties.getLong(
- KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
- DEFAULT_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS));
+ KEY_RUNTIME_MIN_UI_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS));
// Max limit should be at least the min guarantee AND the free quota.
- RUNTIME_USER_INITIATED_LIMIT_MS = Math.max(RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
- Math.max(RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
+ RUNTIME_UI_LIMIT_MS = Math.max(RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ Math.max(RUNTIME_MIN_UI_GUARANTEE_MS,
properties.getLong(
- KEY_RUNTIME_USER_INITIATED_LIMIT_MS,
- DEFAULT_RUNTIME_USER_INITIATED_LIMIT_MS)));
+ KEY_RUNTIME_UI_LIMIT_MS, DEFAULT_RUNTIME_UI_LIMIT_MS)));
// The buffer factor should be at least 1 (so we don't decrease the time).
- RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = Math.max(1,
+ RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = Math.max(1,
properties.getFloat(
- KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
- DEFAULT_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR
+ KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
+ DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR
));
// Make sure min runtime is at least as long as other user-initiated jobs.
- RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS = Math.max(
- RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
+ RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = Math.max(
+ RUNTIME_MIN_UI_GUARANTEE_MS,
properties.getLong(
- KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
- DEFAULT_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS));
+ KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
+ DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS));
// User-initiated requires RUN_USER_INITIATED_JOBS permission, so the upper limit will
// be higher than other jobs.
// Max limit should be the min guarantee and the max of other user-initiated jobs.
- RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS = Math.max(
- RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
- Math.max(RUNTIME_USER_INITIATED_LIMIT_MS,
+ RUNTIME_UI_DATA_TRANSFER_LIMIT_MS = Math.max(
+ RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
+ Math.max(RUNTIME_UI_LIMIT_MS,
properties.getLong(
- KEY_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS,
- DEFAULT_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS)));
+ KEY_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS,
+ DEFAULT_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS)));
}
private boolean updateTareSettingsLocked(@EconomyManager.EnabledMode int enabledMode) {
@@ -958,18 +969,18 @@
pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println();
pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
.println();
- pw.print(KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS,
- RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS).println();
- pw.print(KEY_RUNTIME_USER_INITIATED_LIMIT_MS,
- RUNTIME_USER_INITIATED_LIMIT_MS).println();
- pw.print(KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
- RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println();
- pw.print(KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
- RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS).println();
- pw.print(KEY_RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS,
- RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS).println();
+ pw.print(KEY_RUNTIME_MIN_UI_GUARANTEE_MS, RUNTIME_MIN_UI_GUARANTEE_MS).println();
+ pw.print(KEY_RUNTIME_UI_LIMIT_MS, RUNTIME_UI_LIMIT_MS).println();
+ pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
+ RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println();
+ pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
+ RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println();
+ pw.print(KEY_RUNTIME_UI_DATA_TRANSFER_LIMIT_MS,
+ RUNTIME_UI_DATA_TRANSFER_LIMIT_MS).println();
pw.print(KEY_PERSIST_IN_SPLIT_FILES, PERSIST_IN_SPLIT_FILES).println();
+ pw.print(KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS, MAX_NUM_PERSISTED_JOB_WORK_ITEMS)
+ .println();
pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println();
@@ -1353,6 +1364,25 @@
// Fast path: we are adding work to an existing job, and the JobInfo is not
// changing. We can just directly enqueue this work in to the job.
if (toCancel.getJob().equals(job)) {
+ // On T and below, JobWorkItem count was unlimited but they could not be
+ // persisted. Now in U and above, we allow persisting them. In both cases,
+ // there is a danger of apps adding too many JobWorkItems and causing the
+ // system to OOM since we keep everything in memory. The persisting danger
+ // is greater because it could technically lead to a boot loop if the system
+ // keeps trying to load all the JobWorkItems that led to the initial OOM.
+ // Therefore, for now (partly for app compatibility), we tackle the latter
+ // and limit the number of JobWorkItems that can be persisted.
+ // Moving forward, we should look into two things:
+ // 1. Limiting the number of unpersisted JobWorkItems
+ // 2. Offloading some state to disk so we don't keep everything in memory
+ // TODO(273758274): improve JobScheduler's resilience and memory management
+ if (toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
+ && toCancel.isPersisted()) {
+ Slog.w(TAG, "Too many JWIs for uid " + uId);
+ throw new IllegalStateException("Apps may not persist more than "
+ + mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
+ + " JobWorkItems per job");
+ }
toCancel.enqueueWorkLocked(work);
mJobs.touchJob(toCancel);
@@ -1397,6 +1427,26 @@
jobStatus.prepareLocked();
if (toCancel != null) {
+ // On T and below, JobWorkItem count was unlimited but they could not be
+ // persisted. Now in U and above, we allow persisting them. In both cases,
+ // there is a danger of apps adding too many JobWorkItems and causing the
+ // system to OOM since we keep everything in memory. The persisting danger
+ // is greater because it could technically lead to a boot loop if the system
+ // keeps trying to load all the JobWorkItems that led to the initial OOM.
+ // Therefore, for now (partly for app compatibility), we tackle the latter
+ // and limit the number of JobWorkItems that can be persisted.
+ // Moving forward, we should look into two things:
+ // 1. Limiting the number of unpersisted JobWorkItems
+ // 2. Offloading some state to disk so we don't keep everything in memory
+ // TODO(273758274): improve JobScheduler's resilience and memory management
+ if (work != null && toCancel.isPersisted()
+ && toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS) {
+ Slog.w(TAG, "Too many JWIs for uid " + uId);
+ throw new IllegalStateException("Apps may not persist more than "
+ + mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
+ + " JobWorkItems per job");
+ }
+
// Implicitly replaces the existing job record with the new instance
cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app");
@@ -1438,7 +1488,9 @@
/* isDeviceIdle */ false,
/* hasConnectivityConstraintSatisfied */ false,
/* hasContentTriggerConstraintSatisfied */ false,
- 0);
+ 0,
+ jobStatus.getJob().isUserInitiated(),
+ /* isRunningAsUserInitiatedJob */ false);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -1857,7 +1909,9 @@
cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
- 0);
+ 0,
+ cancelled.getJob().isUserInitiated(),
+ /* isRunningAsUserInitiatedJob */ false);
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
@@ -3256,19 +3310,18 @@
final long estimatedTransferTimeMs =
mConnectivityController.getEstimatedTransferTimeMs(job);
if (estimatedTransferTimeMs == ConnectivityController.UNKNOWN_TIME) {
- return mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS;
+ return mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS;
}
// Try to give the job at least as much time as we think the transfer will take,
// but cap it at the maximum limit
final long factoredTransferTimeMs = (long) (estimatedTransferTimeMs
- * mConstants
- .RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR);
- return Math.min(mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS,
+ * mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR);
+ return Math.min(mConstants.RUNTIME_UI_DATA_TRANSFER_LIMIT_MS,
Math.max(factoredTransferTimeMs,
- mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS
+ mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS
));
}
- return mConstants.RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS;
+ return mConstants.RUNTIME_MIN_UI_GUARANTEE_MS;
} else if (job.shouldTreatAsExpeditedJob()) {
// Don't guarantee RESTRICTED jobs more than 5 minutes.
return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
@@ -3287,10 +3340,10 @@
&& checkRunUserInitiatedJobsPermission(
job.getSourceUid(), job.getSourcePackageName());
if (job.getJob().getRequiredNetwork() != null && allowLongerJob) { // UI+DT
- return mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS;
+ return mConstants.RUNTIME_UI_DATA_TRANSFER_LIMIT_MS;
}
if (allowLongerJob) { // UI with LRJ permission
- return mConstants.RUNTIME_USER_INITIATED_LIMIT_MS;
+ return mConstants.RUNTIME_UI_LIMIT_MS;
}
if (job.shouldTreatAsUserInitiatedJob()) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index e60ed4a..4c339ac 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -462,7 +462,9 @@
job.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
- mExecutionStartTimeElapsed - job.enqueueTime);
+ mExecutionStartTimeElapsed - job.enqueueTime,
+ job.getJob().isUserInitiated(),
+ job.shouldTreatAsUserInitiatedJob());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
// Use the context's ID to distinguish traces since there'll only be one job
// running per context.
@@ -1361,7 +1363,9 @@
completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
- 0);
+ 0,
+ completedJob.getJob().isUserInitiated(),
+ completedJob.startedAsUserInitiatedJob);
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 537a670..0cc7758 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -814,6 +814,13 @@
return null;
}
+ /** Returns the number of {@link JobWorkItem JobWorkItems} attached to this job. */
+ public int getWorkCount() {
+ final int pendingCount = pendingWork == null ? 0 : pendingWork.size();
+ final int executingCount = executingWork == null ? 0 : executingWork.size();
+ return pendingCount + executingCount;
+ }
+
public boolean hasWorkLocked() {
return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
index 1ff389d..dffed0f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.Context;
import android.content.PermissionChecker;
@@ -41,7 +42,8 @@
@Nullable
public final String installerPackageName;
- InstalledPackageInfo(@NonNull Context context, @NonNull PackageInfo packageInfo) {
+ InstalledPackageInfo(@NonNull Context context, @UserIdInt int userId,
+ @NonNull PackageInfo packageInfo) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
packageName = packageInfo.packageName;
@@ -55,7 +57,8 @@
applicationInfo.uid, packageName);
InstallSourceInfo installSourceInfo = null;
try {
- installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName);
+ installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName,
+ userId);
} catch (RemoteException e) {
// Shouldn't happen.
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index caf72e8..ffb2c03 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -625,7 +625,8 @@
mPackageToUidCache.add(userId, pkgName, uid);
}
synchronized (mLock) {
- final InstalledPackageInfo ipo = new InstalledPackageInfo(getContext(), packageInfo);
+ final InstalledPackageInfo ipo = new InstalledPackageInfo(getContext(), userId,
+ packageInfo);
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
mUidToPackageCache.add(uid, pkgName);
@@ -683,7 +684,7 @@
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo =
- new InstalledPackageInfo(getContext(), pkgs.get(i));
+ new InstalledPackageInfo(getContext(), userId, pkgs.get(i));
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
@@ -963,7 +964,7 @@
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo =
- new InstalledPackageInfo(getContext(), pkgs.get(i));
+ new InstalledPackageInfo(getContext(), userId, pkgs.get(i));
final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
diff --git a/api/api.go b/api/api.go
index 25d9728..9876abb 100644
--- a/api/api.go
+++ b/api/api.go
@@ -418,7 +418,6 @@
// combined_apis bp2build converter
func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
basePrefix := "non-updatable"
- scopeNames := []string{"public", "system", "module-lib", "system-server"}
scopeToSuffix := map[string]string{
"public": "-current.txt",
"system": "-system-current.txt",
@@ -426,8 +425,7 @@
"system-server": "-system-server-current.txt",
}
- for _, scopeName := range scopeNames{
- suffix := scopeToSuffix[scopeName]
+ for scopeName, suffix := range scopeToSuffix{
name := a.Name() + suffix
var scope bazel.StringAttribute
diff --git a/core/api/current.txt b/core/api/current.txt
index 02de1cd..79123c7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8049,6 +8049,7 @@
field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final String ACTION_CHECK_POLICY_COMPLIANCE = "android.app.action.CHECK_POLICY_COMPLIANCE";
field public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
+ field public static final String ACTION_DEVICE_FINANCING_STATE_CHANGED = "android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED";
field public static final String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED = "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED";
field public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE";
@@ -27478,7 +27479,7 @@
method public boolean onTrackballEvent(android.view.MotionEvent);
method public abstract boolean onTune(android.net.Uri);
method public boolean onTune(android.net.Uri, android.os.Bundle);
- method public void onTvMessage(@NonNull String, @NonNull android.os.Bundle);
+ method public void onTvMessage(int, @NonNull android.os.Bundle);
method public void onUnblockContent(android.media.tv.TvContentRating);
method public void setOverlayViewEnabled(boolean);
}
@@ -53907,6 +53908,14 @@
method public void removeViewImmediate(android.view.View);
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+ field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION";
+ field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
+ field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
+ field public static final String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
+ field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
+ field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS";
+ field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS";
+ field public static final String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION = "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
}
public static class WindowManager.BadTokenException extends java.lang.RuntimeException {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb7ce5b..d592769 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9984,6 +9984,7 @@
public final class HotspotNetwork implements android.os.Parcelable {
method public int describeContents();
method public long getDeviceId();
+ method @NonNull public android.os.Bundle getExtras();
method public int getHostNetworkType();
method @Nullable public String getHotspotBssid();
method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes();
@@ -10003,6 +10004,7 @@
method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder addHotspotSecurityType(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork build();
method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setDeviceId(long);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHostNetworkType(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHotspotBssid(@NonNull String);
method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHotspotSsid(@NonNull String);
@@ -10039,6 +10041,7 @@
public final class KnownNetwork implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public android.os.Bundle getExtras();
method @Nullable public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo getNetworkProviderInfo();
method public int getNetworkSource();
method @NonNull public java.util.Set<java.lang.Integer> getSecurityTypes();
@@ -10054,6 +10057,7 @@
ctor public KnownNetwork.Builder();
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder addSecurityType(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkProviderInfo(@Nullable android.net.wifi.sharedconnectivity.app.NetworkProviderInfo);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String);
@@ -10085,6 +10089,7 @@
method @IntRange(from=0, to=3) public int getConnectionStrength();
method @NonNull public String getDeviceName();
method public int getDeviceType();
+ method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getModelName();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.NetworkProviderInfo> CREATOR;
@@ -10103,6 +10108,7 @@
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setConnectionStrength(@IntRange(from=0, to=3) int);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceName(@NonNull String);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceType(int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setModelName(@NonNull String);
}
@@ -10134,16 +10140,18 @@
public final class SharedConnectivitySettingsState implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.os.Bundle getExtras();
+ method @Nullable public android.app.PendingIntent getInstantTetherSettingsPendingIntent();
method public boolean isInstantTetherEnabled();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState> CREATOR;
}
public static final class SharedConnectivitySettingsState.Builder {
- ctor public SharedConnectivitySettingsState.Builder();
+ ctor public SharedConnectivitySettingsState.Builder(@NonNull android.content.Context);
method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState build();
method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.Builder setInstantTetherEnabled(boolean);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.Builder setInstantTetherSettingsPendingIntent(@NonNull android.content.Intent);
}
}
@@ -12993,20 +13001,20 @@
package android.service.voice {
public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
- method @Nullable public android.content.Intent createEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @Nullable public android.content.Intent createReEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @Nullable public android.content.Intent createUnEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+ method @Nullable public android.content.Intent createEnrollIntent();
+ method @Nullable public android.content.Intent createReEnrollIntent();
+ method @Nullable public android.content.Intent createUnEnrollIntent();
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int);
method public int getSupportedAudioCapabilities();
- method public int getSupportedRecognitionModes() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int, @NonNull byte[]) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+ method public int getSupportedRecognitionModes();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int, @NonNull byte[]);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
+ method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
+ method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
@@ -13185,10 +13193,10 @@
public interface HotwordDetector {
method public default void destroy();
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
+ method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
+ method public boolean stopRecognition();
+ method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
}
public static interface HotwordDetector.Callback {
@@ -13202,9 +13210,6 @@
method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
- public static class HotwordDetector.IllegalDetectorStateException extends android.util.AndroidException {
- }
-
public final class HotwordRejectedResult implements android.os.Parcelable {
method public int describeContents();
method public int getConfidenceLevel();
@@ -13272,9 +13277,9 @@
public class VisualQueryDetector {
method public void destroy();
- method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
- method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
+ method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean startRecognition();
+ method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean stopRecognition();
+ method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
}
public static interface VisualQueryDetector.Callback {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 445b957..6bfaa35 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -516,6 +516,10 @@
package android.app.admin {
+ public final class AccountTypePolicyKey extends android.app.admin.PolicyKey {
+ ctor public AccountTypePolicyKey(@NonNull String, @NonNull String);
+ }
+
public final class DeviceAdminAuthority extends android.app.admin.Authority {
field @NonNull public static final android.app.admin.DeviceAdminAuthority DEVICE_ADMIN_AUTHORITY;
}
@@ -612,6 +616,10 @@
field @NonNull public static final android.app.admin.FlagUnion FLAG_UNION;
}
+ public final class IntentFilterPolicyKey extends android.app.admin.PolicyKey {
+ ctor public IntentFilterPolicyKey(@NonNull String, @NonNull android.content.IntentFilter);
+ }
+
public final class MostRecent<V> extends android.app.admin.ResolutionMechanism<V> {
ctor public MostRecent();
method public int describeContents();
@@ -627,6 +635,14 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.MostRestrictive<?>> CREATOR;
}
+ public final class PackagePermissionPolicyKey extends android.app.admin.PolicyKey {
+ ctor public PackagePermissionPolicyKey(@NonNull String, @NonNull String, @NonNull String);
+ }
+
+ public final class PackagePolicyKey extends android.app.admin.PolicyKey {
+ ctor public PackagePolicyKey(@NonNull String, @NonNull String);
+ }
+
public final class PolicyState<V> implements android.os.Parcelable {
method @NonNull public android.app.admin.ResolutionMechanism<V> getResolutionMechanism();
}
@@ -676,6 +692,10 @@
method public int getOperation();
}
+ public final class UserRestrictionPolicyKey extends android.app.admin.PolicyKey {
+ ctor public UserRestrictionPolicyKey(@NonNull String, @NonNull String);
+ }
+
}
package android.app.assist {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2751b54..682fec8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5840,26 +5840,6 @@
r.activity.mChangingConfigurations = true;
- // If we are preserving the main window across relaunches we would also like to preserve
- // the children. However the client side view system does not support preserving
- // the child views so we notify the window manager to expect these windows to
- // be replaced and defer requests to destroy or hide them. This way we can achieve
- // visual continuity. It's important that we do this here prior to pause and destroy
- // as that is when we may hide or remove the child views.
- //
- // There is another scenario, if we have decided locally to relaunch the app from a
- // call to recreate, then none of the windows will be prepared for replacement or
- // preserved by the server, so we want to notify it that we are preparing to replace
- // everything
- try {
- if (r.mPreserveWindow) {
- WindowManagerGlobal.getWindowSession().prepareToReplaceWindows(
- r.token, true /* childrenOnly */);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6301ad7..999075d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2561,7 +2561,7 @@
public InstallSourceInfo getInstallSourceInfo(String packageName) throws NameNotFoundException {
final InstallSourceInfo installSourceInfo;
try {
- installSourceInfo = mPM.getInstallSourceInfo(packageName);
+ installSourceInfo = mPM.getInstallSourceInfo(packageName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e78fb17..0e89f57 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4634,9 +4634,9 @@
* Set whether this is an "ongoing" notification.
*
* Ongoing notifications cannot be dismissed by the user on locked devices, or by
- * notification listeners, and some notifications (device management, media) cannot be
- * dismissed on unlocked devices, so your application or service must take
- * care of canceling them.
+ * notification listeners, and some notifications (call, device management, media) cannot
+ * be dismissed on unlocked devices, so your application or service must take care of
+ * canceling them.
*
* They are typically used to indicate a background task that the user is actively engaged
* with (e.g., playing music) or is pending in some way and therefore occupying the device
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e72b141..f7d2afb 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -54,6 +54,9 @@
per-file Broadcast* = file:/BROADCASTS_OWNERS
per-file ReceiverInfo* = file:/BROADCASTS_OWNERS
+# KeyguardManager
+per-file KeyguardManager.java = file:/services/core/java/com/android/server/locksettings/OWNERS
+
# LocaleManager
per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS
@@ -94,7 +97,5 @@
per-file ConfigurationController.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file *ScreenCapture* = file:/services/core/java/com/android/server/wm/OWNERS
-# TODO(b/174932174): determine the ownership of KeyguardManager.java
-
# Zygote
per-file *Zygote* = file:/ZYGOTE_OWNERS
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 6417cd4..9e376a7 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -49,6 +50,7 @@
/**
* @hide
*/
+ @TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
mAccountType = Objects.requireNonNull((accountType));
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0241417..6bbbfe1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -57,6 +57,7 @@
import android.Manifest.permission;
import android.accounts.Account;
+import android.annotation.BroadcastBehavior;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.IntDef;
@@ -3998,6 +3999,27 @@
public static final String EXTRA_RESOURCE_IDS =
"android.app.extra.RESOURCE_IDS";
+ /**
+ * Broadcast Action: Broadcast sent to indicate that the device financing state has changed.
+ *
+ * <p>This occurs when, for example, a financing kiosk app has been added or removed.
+ *
+ * <p>To query the current device financing state see {@link #isDeviceFinanced}.
+ *
+ * <p>This will be delivered to the following apps if they include a receiver for this action
+ * in their manifest:
+ * <ul>
+ * <li>Device owner admins.
+ * <li>Organization-owned profile owner admins
+ * <li>The supervision app
+ * <li>The device management role holder
+ * </ul>
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ @BroadcastBehavior(explicitOnly = true, includeBackground = true)
+ public static final String ACTION_DEVICE_FINANCING_STATE_CHANGED =
+ "android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED";
+
/** Allow the user to choose whether to enable MTE on the device. */
public static final int MTE_NOT_CONTROLLED_BY_POLICY = 0;
diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java
index b0af4cd..30aad96 100644
--- a/core/java/android/app/admin/IntentFilterPolicyKey.java
+++ b/core/java/android/app/admin/IntentFilterPolicyKey.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Parcel;
@@ -49,6 +50,7 @@
/**
* @hide
*/
+ @TestApi
public IntentFilterPolicyKey(@NonNull String identifier, @NonNull IntentFilter filter) {
super(identifier);
mFilter = Objects.requireNonNull(filter);
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 08c4224..7fd514c 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -53,6 +54,7 @@
/**
* @hide
*/
+ @TestApi
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index b2a8d5d..2ab00bc 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -50,6 +51,7 @@
/**
* @hide
*/
+ @TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
mPackageName = Objects.requireNonNull((packageName));
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index 880b58b..aeb2380 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
@@ -40,6 +41,7 @@
/**
* @hide
*/
+ @TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
mRestriction = Objects.requireNonNull(restriction);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 132b9af..410994d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -247,7 +247,7 @@
@UnsupportedAppUsage
String getInstallerPackageName(in String packageName);
- InstallSourceInfo getInstallSourceInfo(in String packageName);
+ InstallSourceInfo getInstallSourceInfo(in String packageName, int userId);
void resetApplicationPreferences(int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 328b0ae..b9c671a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8666,7 +8666,7 @@
* requesting its own install information and is not an instant app.
*
* @param packageName The name of the package to query
- * @throws NameNotFoundException if the given package name is not installed
+ * @throws NameNotFoundException if the given package name is not available to the caller.
*/
@NonNull
public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName)
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 3341800..5e523c0 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -513,6 +513,19 @@
* This method executes within a transaction. If an exception is thrown, all changes
* will automatically be rolled back.
* </p>
+ * <p>
+ * <em>Important:</em> You should NOT modify an existing migration step from version X to X+1
+ * once a build has been released containing that migration step. If a migration step has an
+ * error and it runs on a device, the step will NOT re-run itself in the future if a fix is made
+ * to the migration step.</p>
+ * <p>For example, suppose a migration step renames a database column from {@code foo} to
+ * {@code bar} when the name should have been {@code baz}. If that migration step is released
+ * in a build and runs on a user's device, the column will be renamed to {@code bar}. If the
+ * developer subsequently edits this same migration step to change the name to {@code baz} as
+ * intended, the user devices which have already run this step will still have the name
+ * {@code bar}. Instead, a NEW migration step should be created to correct the error and rename
+ * {@code bar} to {@code baz}, ensuring the error is corrected on devices which have already run
+ * the migration step with the error.</p>
*
* @param db The database.
* @param oldVersion The old database version.
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
index cf20459..79a551a 100644
--- a/core/java/android/hardware/CameraSessionStats.java
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -54,6 +54,7 @@
private int mApiLevel;
private boolean mIsNdk;
private int mLatencyMs;
+ private long mLogId;
private int mSessionType;
private int mInternalReconfigure;
private long mRequestCount;
@@ -70,6 +71,7 @@
mApiLevel = -1;
mIsNdk = false;
mLatencyMs = -1;
+ mLogId = 0;
mMaxPreviewFps = 0;
mSessionType = -1;
mInternalReconfigure = -1;
@@ -82,7 +84,7 @@
public CameraSessionStats(String cameraId, int facing, int newCameraState,
String clientName, int apiLevel, boolean isNdk, int creationDuration,
- float maxPreviewFps, int sessionType, int internalReconfigure) {
+ float maxPreviewFps, int sessionType, int internalReconfigure, long logId) {
mCameraId = cameraId;
mFacing = facing;
mNewCameraState = newCameraState;
@@ -90,6 +92,7 @@
mApiLevel = apiLevel;
mIsNdk = isNdk;
mLatencyMs = creationDuration;
+ mLogId = logId;
mMaxPreviewFps = maxPreviewFps;
mSessionType = sessionType;
mInternalReconfigure = internalReconfigure;
@@ -127,6 +130,7 @@
dest.writeInt(mApiLevel);
dest.writeBoolean(mIsNdk);
dest.writeInt(mLatencyMs);
+ dest.writeLong(mLogId);
dest.writeFloat(mMaxPreviewFps);
dest.writeInt(mSessionType);
dest.writeInt(mInternalReconfigure);
@@ -146,6 +150,7 @@
mApiLevel = in.readInt();
mIsNdk = in.readBoolean();
mLatencyMs = in.readInt();
+ mLogId = in.readLong();
mMaxPreviewFps = in.readFloat();
mSessionType = in.readInt();
mInternalReconfigure = in.readInt();
@@ -189,6 +194,10 @@
return mLatencyMs;
}
+ public long getLogId() {
+ return mLogId;
+ }
+
public float getMaxPreviewFps() {
return mMaxPreviewFps;
}
diff --git a/core/java/android/service/voice/AbstractDetector.java b/core/java/android/service/voice/AbstractDetector.java
index 644a2bf..0f3e8d1 100644
--- a/core/java/android/service/voice/AbstractDetector.java
+++ b/core/java/android/service/voice/AbstractDetector.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.app.compat.CompatChanges;
import android.media.AudioFormat;
import android.media.permission.Identity;
import android.os.Binder;
@@ -100,7 +99,7 @@
public boolean startRecognition(
@NonNull ParcelFileDescriptor audioStream,
@NonNull AudioFormat audioFormat,
- @Nullable PersistableBundle options) throws IllegalDetectorStateException {
+ @Nullable PersistableBundle options) {
if (DEBUG) {
Slog.i(TAG, "#recognizeHotword");
}
@@ -132,18 +131,13 @@
* @param sharedMemory The unrestricted data blob to provide to the
* {@link VisualQueryDetectionService} and {@link HotwordDetectionService}. Use this to
* provide the hotword models data or other such data to the trusted process.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of
- * Android Tiramisu or above and attempts to start a recognition when the detector is
- * not able based on the state. Because the caller receives updates via an asynchronous
- * callback and the state of the detector can change without caller's knowledge, a
- * checked exception is thrown.
* @throws IllegalStateException if this {@link HotwordDetector} wasn't specified to use a
* {@link HotwordDetectionService} or {@link VisualQueryDetectionService} when it was
* created.
*/
@Override
public void updateState(@Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException {
+ @Nullable SharedMemory sharedMemory) {
if (DEBUG) {
Slog.d(TAG, "updateState()");
}
@@ -199,13 +193,9 @@
}
}
- protected void throwIfDetectorIsNoLongerActive() throws IllegalDetectorStateException {
+ protected void throwIfDetectorIsNoLongerActive() {
if (!mIsDetectorActive.get()) {
Slog.e(TAG, "attempting to use a destroyed detector which is no longer active");
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "attempting to use a destroyed detector which is no longer active");
- }
throw new IllegalStateException(
"attempting to use a destroyed detector which is no longer active");
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 2830fb7..ffa15f0 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -827,28 +827,19 @@
/**
* {@inheritDoc}
*
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and this AlwaysOnHotwordDetector wasn't specified to use a
- * {@link HotwordDetectionService} when it was created. In addition, the exception can
- * be thrown if this AlwaysOnHotwordDetector is in an invalid or error state.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if
- * this AlwaysOnHotwordDetector wasn't specified to use a
- * {@link HotwordDetectionService} when it was created. In addition, the exception can
- * be thrown if this AlwaysOnHotwordDetector is in an invalid or error state.
+ * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
+ * {@link HotwordDetectionService} when it was created. In addition, if this
+ * AlwaysOnHotwordDetector is in an invalid or error state.
*/
@Override
public final void updateState(@Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException {
+ @Nullable SharedMemory sharedMemory) {
synchronized (mLock) {
if (!mSupportSandboxedDetectionService) {
throw new IllegalStateException(
"updateState called, but it doesn't support hotword detection service");
}
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "updateState called on an invalid detector or error state");
- }
throw new IllegalStateException(
"updateState called on an invalid detector or error state");
}
@@ -869,17 +860,16 @@
@TestApi
public void overrideAvailability(int availability) {
synchronized (mLock) {
+ mAvailability = availability;
+ mIsAvailabilityOverriddenByTestApi = true;
// ENROLLED state requires there to be metadata about the sound model so a fake one
// is created.
- if (mKeyphraseMetadata == null && availability == STATE_KEYPHRASE_ENROLLED) {
+ if (mKeyphraseMetadata == null && mAvailability == STATE_KEYPHRASE_ENROLLED) {
Set<Locale> fakeSupportedLocales = new HashSet<>();
fakeSupportedLocales.add(mLocale);
mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
}
-
- mAvailability = availability;
- mIsAvailabilityOverriddenByTestApi = true;
notifyStateChangedLocked();
}
}
@@ -935,23 +925,14 @@
* @see #RECOGNITION_MODE_USER_IDENTIFICATION
* @see #RECOGNITION_MODE_VOICE_TRIGGER
*
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above. Because the caller receives availability updates via an asynchronous
- * callback, it may be due to the availability changing while this call is performed.
- * - Throws if the detector is in an invalid or error state.
- * This may happen if another detector has been instantiated or the
- * {@link VoiceInteractionService} hosting this detector has been shut down.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 Android if the recognition isn't supported. Callers should only call this method
- * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
- * avoid this exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below Android API level
- * 33 if the detector is in an invalid or error state. This may happen if another
- * detector has been instantiated or the {@link VoiceInteractionService} hosting this
- * detector has been shut down.
+ * @throws UnsupportedOperationException if the keyphrase itself isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
- public @RecognitionModes
- int getSupportedRecognitionModes() throws IllegalDetectorStateException {
+ public @RecognitionModes int getSupportedRecognitionModes() {
if (DBG) Slog.d(TAG, "getSupportedRecognitionModes()");
synchronized (mLock) {
return getSupportedRecognitionModesLocked();
@@ -959,22 +940,14 @@
}
@GuardedBy("mLock")
- private int getSupportedRecognitionModesLocked() throws IllegalDetectorStateException {
+ private int getSupportedRecognitionModesLocked() {
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException("getSupportedRecognitionModes called on an"
- + " invalid detector or error state");
- }
throw new IllegalStateException(
"getSupportedRecognitionModes called on an invalid detector or error state");
}
// This method only makes sense if we can actually support a recognition.
if (mAvailability != STATE_KEYPHRASE_ENROLLED || mKeyphraseMetadata == null) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException("Getting supported recognition modes for"
- + " the keyphrase is not supported");
- }
throw new UnsupportedOperationException(
"Getting supported recognition modes for the keyphrase is not supported");
}
@@ -1029,30 +1002,15 @@
* startRecognition request. This data is intended to provide additional parameters
* when starting the opaque sound model.
* @return Indicates whether the call succeeded or not.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and attempts to start a recognition when the detector is not able based on
- * the availability state. This can be thrown even if the state has been checked before
- * calling this method because the caller receives availability updates via an
- * asynchronous callback, it may be due to the availability changing while this call is
- * performed.
- * - Throws if the recognition isn't supported.
- * Callers should only call this method after a supported state callback on
- * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
- * - Also throws if the detector is in an invalid or error state.
- * This may happen if another detector has been instantiated or the
- * {@link VoiceInteractionService} hosting this detector has been shut down.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 Android if the recognition isn't supported. Callers should only call this method
- * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
- * avoid this exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below Android API level
- * 33 if the detector is in an invalid or error state. This may happen if another
- * detector has been instantiated or the {@link VoiceInteractionService} hosting this
- * detector has been shut down.
+ * @throws UnsupportedOperationException if the recognition isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
- public boolean startRecognition(@RecognitionFlags int recognitionFlags, @NonNull byte[] data)
- throws IllegalDetectorStateException {
+ public boolean startRecognition(@RecognitionFlags int recognitionFlags, @NonNull byte[] data) {
synchronized (mLock) {
return startRecognitionLocked(recognitionFlags, data)
== STATUS_OK;
@@ -1069,30 +1027,15 @@
*
* @param recognitionFlags The flags to control the recognition properties.
* @return Indicates whether the call succeeded or not.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and attempts to start a recognition when the detector is not able based on
- * the availability state. This can be thrown even if the state has been checked before
- * calling this method because the caller receives availability updates via an
- * asynchronous callback, it may be due to the availability changing while this call is
- * performed.
- * - Throws if the recognition isn't supported.
- * Callers should only call this method after a supported state callback on
- * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
- * - Also throws if the detector is in an invalid or error state.
- * This may happen if another detector has been instantiated or the
- * {@link VoiceInteractionService} hosting this detector has been shut down.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 if the recognition isn't supported. Callers should only call this method after a
- * supported state callback on {@link Callback#onAvailabilityChanged(int)} to avoid this
- * exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
- * detector is in an invalid or error state. This may happen if another detector has
- * been instantiated or the {@link VoiceInteractionService} hosting this detector has
- * been shut down.
+ * @throws UnsupportedOperationException if the recognition isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
- public boolean startRecognition(@RecognitionFlags int recognitionFlags)
- throws IllegalDetectorStateException {
+ public boolean startRecognition(@RecognitionFlags int recognitionFlags) {
if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")");
synchronized (mLock) {
return startRecognitionLocked(recognitionFlags, null /* data */) == STATUS_OK;
@@ -1106,8 +1049,7 @@
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
@Override
- public boolean startRecognition()
- throws IllegalDetectorStateException {
+ public boolean startRecognition() {
return startRecognition(0);
}
@@ -1117,44 +1059,28 @@
* Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @return Indicates whether the call succeeded or not.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of
- * API level 33 or above and attempts to stop a recognition when the detector is
- * not able based on the state. This can be thrown even if the state has been checked
- * before calling this method because the caller receives availability updates via an
- * asynchronous callback, it may be due to the availability changing while this call is
- * performed.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 if the recognition isn't supported. Callers should only call this method after a
- * supported state callback on {@link Callback#onAvailabilityChanged(int)} to avoid this
- * exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
- * detector is in an invalid or error state. This may happen if another detector has
- * been instantiated or the {@link VoiceInteractionService} hosting this detector has
- * been shut down.
+ * @throws UnsupportedOperationException if the recognition isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
// TODO: Remove this RequiresPermission since it isn't actually enforced. Also fix the javadoc
// about permissions enforcement (when it throws vs when it just returns false) for other
// methods in this class.
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
@Override
- public boolean stopRecognition() throws IllegalDetectorStateException {
+ public boolean stopRecognition() {
if (DBG) Slog.d(TAG, "stopRecognition()");
synchronized (mLock) {
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "stopRecognition called on an invalid detector or error state");
- }
throw new IllegalStateException(
"stopRecognition called on an invalid detector or error state");
}
// Check if we can start/stop a recognition.
if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "Recognition for the given keyphrase is not supported");
- }
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
@@ -1179,28 +1105,18 @@
* - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
* if API is not supported by HAL
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * if the detector is in an invalid or error state. This may happen if another detector
- * has been instantiated or the {@link VoiceInteractionService} hosting this detector
- * has been shut down.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
- * detector is in an invalid or error state. This may happen if another detector has
- * been instantiated or the {@link VoiceInteractionService} hosting this detector has
- * been shut down.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
- public int setParameter(@ModelParams int modelParam, int value)
- throws IllegalDetectorStateException {
+ public int setParameter(@ModelParams int modelParam, int value) {
if (DBG) {
Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")");
}
synchronized (mLock) {
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "setParameter called on an invalid detector or error state");
- }
throw new IllegalStateException(
"setParameter called on an invalid detector or error state");
}
@@ -1221,27 +1137,18 @@
*
* @param modelParam {@link ModelParams}
* @return value of parameter
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * if the detector is in an invalid or error state. This may happen if another detector
- * has been instantiated or the {@link VoiceInteractionService} hosting this detector
- * has been shut down.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if
- * the detector is in an invalid or error state. This may happen if another detector has
- * been instantiated or the {@link VoiceInteractionService} hosting this detector has
- * been shut down.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
- public int getParameter(@ModelParams int modelParam) throws IllegalDetectorStateException {
+ public int getParameter(@ModelParams int modelParam) {
if (DBG) {
Slog.d(TAG, "getParameter(" + modelParam + ")");
}
synchronized (mLock) {
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "getParameter called on an invalid detector or error state");
- }
throw new IllegalStateException(
"getParameter called on an invalid detector or error state");
}
@@ -1259,29 +1166,19 @@
*
* @param modelParam {@link ModelParams}
* @return supported range of parameter, null if not supported
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * if the detector is in an invalid or error state. This may happen if another detector
- * has been instantiated or the {@link VoiceInteractionService} hosting this detector
- * has been shut down.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if
- * the detector is in an invalid or error state. This may happen if another detector has
- * been instantiated or the {@link VoiceInteractionService} hosting this detector has
- * been shut down.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
@Nullable
- public ModelParamRange queryParameter(@ModelParams int modelParam)
- throws IllegalDetectorStateException {
+ public ModelParamRange queryParameter(@ModelParams int modelParam) {
if (DBG) {
Slog.d(TAG, "queryParameter(" + modelParam + ")");
}
synchronized (mLock) {
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "queryParameter called on an invalid detector or error state");
- }
throw new IllegalStateException(
"queryParameter called on an invalid detector or error state");
}
@@ -1298,25 +1195,15 @@
* otherwise {@link #createReEnrollIntent()} should be preferred.
*
* @return An {@link Intent} to start enrollment for the given keyphrase.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above.
- * - Thrown if managing they keyphrase isn't supported. Callers should only call this
- * method after a supported state callback on
- * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
- * - Thrown if the detector is in an invalid state. This may happen if another detector
- * has been instantiated or the {@link VoiceInteractionService} hosting this detector
- * has been shut down.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 if managing they keyphrase isn't supported. Callers should only call this method
- * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
- * avoid this exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
- * detector is in an invalid state. This may happen if another detector has been
- * instantiated or the {@link VoiceInteractionService} hosting this detector has been
- * shut down.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@Nullable
- public Intent createEnrollIntent() throws IllegalDetectorStateException {
+ public Intent createEnrollIntent() {
if (DBG) Slog.d(TAG, "createEnrollIntent");
synchronized (mLock) {
return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_ENROLL);
@@ -1330,25 +1217,15 @@
* i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
*
* @return An {@link Intent} to start un-enrollment for the given keyphrase.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above.
- * - Thrown if managing they keyphrase isn't supported. Callers should only call this
- * method after a supported state callback on
- * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
- * - Thrown if the detector is in an invalid state. This may happen if another detector
- * has been instantiated or the {@link VoiceInteractionService} hosting this detector
- * has been shut down.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 if managing they keyphrase isn't supported. Callers should only call this method
- * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
- * avoid this exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
- * detector is in an invalid state. This may happen if another detector has been
- * instantiated or the {@link VoiceInteractionService} hosting this detector has been
- * shut down.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@Nullable
- public Intent createUnEnrollIntent() throws IllegalDetectorStateException {
+ public Intent createUnEnrollIntent() {
if (DBG) Slog.d(TAG, "createUnEnrollIntent");
synchronized (mLock) {
return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_UN_ENROLL);
@@ -1362,25 +1239,15 @@
* i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
*
* @return An {@link Intent} to start re-enrollment for the given keyphrase.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above.
- * - Thrown if managing they keyphrase isn't supported. Callers should only call this
- * method after a supported state callback on
- * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
- * - Thrown if the detector is in an invalid state. This may happen if another detector
- * has been instantiated or the {@link VoiceInteractionService} hosting this detector
- * has been shut down.
- * @throws UnsupportedOperationException Thrown when a caller has a target SDK below API level
- * 33 if managing they keyphrase isn't supported. Callers should only call this method
- * after a supported state callback on {@link Callback#onAvailabilityChanged(int)} to
- * avoid this exception.
- * @throws IllegalStateException Thrown when a caller has a target SDK below API level 33 if the
- * detector is in an invalid state. This may happen if another detector has been
- * instantiated or the {@link VoiceInteractionService} hosting this detector has been
- * shut down.
+ * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+ * @throws IllegalStateException if the detector is in an invalid or error state.
+ * This may happen if another detector has been instantiated or the
+ * {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@Nullable
- public Intent createReEnrollIntent() throws IllegalDetectorStateException {
+ public Intent createReEnrollIntent() {
if (DBG) Slog.d(TAG, "createReEnrollIntent");
synchronized (mLock) {
return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_RE_ENROLL);
@@ -1388,24 +1255,15 @@
}
@GuardedBy("mLock")
- private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action)
- throws IllegalDetectorStateException {
+ private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) {
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "getManageIntent called on an invalid detector or error state");
- }
throw new IllegalStateException(
- "getManageIntent called on an invalid detector or error state");
+ "getManageIntent called on an invalid detector or error state");
}
// This method only makes sense if we can actually support a recognition.
if (mAvailability != STATE_KEYPHRASE_ENROLLED
&& mAvailability != STATE_KEYPHRASE_UNENROLLED) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "Managing the given keyphrase is not supported");
- }
throw new UnsupportedOperationException(
"Managing the given keyphrase is not supported");
}
@@ -1489,27 +1347,19 @@
@GuardedBy("mLock")
private int startRecognitionLocked(int recognitionFlags,
- @Nullable byte[] data) throws IllegalDetectorStateException {
+ @Nullable byte[] data) {
if (DBG) {
Slog.d(TAG, "startRecognition("
+ recognitionFlags
+ ", " + Arrays.toString(data) + ")");
}
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "startRecognition called on an invalid detector or error state");
- }
throw new IllegalStateException(
"startRecognition called on an invalid detector or error state");
}
// Check if we can start/stop a recognition.
if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
- if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
- throw new IllegalDetectorStateException(
- "Recognition for the given keyphrase is not supported");
- }
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 93fcec1..0c8fd48 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -23,14 +23,10 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
import android.media.AudioFormat;
-import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.SharedMemory;
-import android.util.AndroidException;
import java.io.PrintWriter;
@@ -44,23 +40,6 @@
public interface HotwordDetector {
/**
- * Prior to API level 33, API calls of {@link android.service.voice.HotwordDetector} could
- * return both {@link java.lang.IllegalStateException} or
- * {@link java.lang.UnsupportedOperationException} depending on the detector's underlying state.
- * This lead to confusing behavior as the underlying state of the detector can be modified
- * without the knowledge of the caller via system service layer updates.
- *
- * This change ID, when enabled, changes the API calls to only throw checked exception
- * {@link android.service.voice.HotwordDetector.IllegalDetectorStateException} when checking
- * against state information modified by both the caller and the system services.
- *
- * @hide
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
- long HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION = 226355112L;
-
- /**
* Indicates that it is a non-trusted hotword detector.
*
* @hide
@@ -109,26 +88,16 @@
* Calling this again while recognition is active does nothing.
*
* @return {@code true} if the request to start recognition succeeded
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and attempts to start a recognition when the detector is not able based on
- * the state. This can be thrown even if the state has been checked before calling this
- * method because the caller receives updates via an asynchronous callback, and the
- * state of the detector can change concurrently to the caller calling this method.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
- boolean startRecognition() throws IllegalDetectorStateException;
+ boolean startRecognition();
/**
* Stops sandboxed detection recognition.
*
* @return {@code true} if the request to stop recognition succeeded
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and attempts to stop a recognition when the detector is not able based on
- * the state. This can be thrown even if the state has been checked before calling this
- * method because the caller receives updates via an asynchronous callback, and the
- * state of the detector can change concurrently to the caller calling this method.
*/
- boolean stopRecognition() throws IllegalDetectorStateException;
+ boolean stopRecognition();
/**
* Starts hotword recognition on audio coming from an external connected microphone.
@@ -142,16 +111,11 @@
* PersistableBundle does not allow any remotable objects or other contents that can be
* used to communicate with other processes.
* @return {@code true} if the request to start recognition succeeded
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and attempts to start a recognition when the detector is not able based on
- * the state. This can be thrown even if the state has been checked before calling this
- * method because the caller receives updates via an asynchronous callback, and the
- * state of the detector can change concurrently to the caller calling this method.
*/
boolean startRecognition(
@NonNull ParcelFileDescriptor audioStream,
@NonNull AudioFormat audioFormat,
- @Nullable PersistableBundle options) throws IllegalDetectorStateException;
+ @Nullable PersistableBundle options);
/**
* Set configuration and pass read-only data to sandboxed detection service.
@@ -161,17 +125,10 @@
* communicate with other processes.
* @param sharedMemory The unrestricted data blob to provide to sandboxed detection services.
* Use this to provide model data or other such data to the trusted process.
- * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
- * or above and the detector is not able to perform the operation based on the
- * underlying state. This can be thrown even if the state has been checked before
- * calling this method because the caller receives updates via an asynchronous callback,
- * and the state of the detector can change concurrently to the caller calling this
- * method.
* @throws IllegalStateException if this HotwordDetector wasn't specified to use a
* sandboxed detection service when it was created.
*/
- void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory)
- throws IllegalDetectorStateException;
+ void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory);
/**
* Invalidates this detector so that any future calls to this result
@@ -298,14 +255,4 @@
*/
void onHotwordDetectionServiceRestarted();
}
-
- /**
- * {@link HotwordDetector} specific exception thrown when the underlying state of the detector
- * is invalid for the given action.
- */
- class IllegalDetectorStateException extends AndroidException {
- IllegalDetectorStateException(String message) {
- super(message);
- }
- }
}
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 767fe37..77900d7 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -86,7 +86,7 @@
@RequiresPermission(RECORD_AUDIO)
@Override
- public boolean startRecognition() throws IllegalDetectorStateException {
+ public boolean startRecognition() {
if (DEBUG) {
Slog.i(TAG, "#startRecognition");
}
@@ -109,7 +109,7 @@
/** TODO: stopRecognition */
@RequiresPermission(RECORD_AUDIO)
@Override
- public boolean stopRecognition() throws IllegalDetectorStateException {
+ public boolean stopRecognition() {
if (DEBUG) {
Slog.i(TAG, "#stopRecognition");
}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index 7dc0687..d7bf074 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -84,8 +84,7 @@
* @see HotwordDetector#updateState(PersistableBundle, SharedMemory)
*/
public void updateState(@Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) throws
- HotwordDetector.IllegalDetectorStateException {
+ @Nullable SharedMemory sharedMemory) {
mInitializationDelegate.updateState(options, sharedMemory);
}
@@ -104,7 +103,7 @@
* @see HotwordDetector#startRecognition()
*/
@RequiresPermission(allOf = {CAMERA, RECORD_AUDIO})
- public boolean startRecognition() throws HotwordDetector.IllegalDetectorStateException {
+ public boolean startRecognition() {
if (DEBUG) {
Slog.i(TAG, "#startRecognition");
}
@@ -128,7 +127,7 @@
* @see HotwordDetector#stopRecognition()
*/
@RequiresPermission(allOf = {CAMERA, RECORD_AUDIO})
- public boolean stopRecognition() throws HotwordDetector.IllegalDetectorStateException {
+ public boolean stopRecognition() {
if (DEBUG) {
Slog.i(TAG, "#stopRecognition");
}
@@ -236,13 +235,13 @@
}
@Override
- public boolean stopRecognition() throws IllegalDetectorStateException {
+ public boolean stopRecognition() {
throwIfDetectorIsNoLongerActive();
return true;
}
@Override
- public boolean startRecognition() throws IllegalDetectorStateException {
+ public boolean startRecognition() {
throwIfDetectorIsNoLongerActive();
return true;
}
@@ -251,7 +250,7 @@
public final boolean startRecognition(
@NonNull ParcelFileDescriptor audioStream,
@NonNull AudioFormat audioFormat,
- @Nullable PersistableBundle options) throws IllegalDetectorStateException {
+ @Nullable PersistableBundle options) {
//No-op, not supported by VisualQueryDetector as it should be trusted.
return false;
}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 8c4e90c..c92b1b8 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -195,7 +195,7 @@
private boolean mDebugPrintNextFrameTimeDelta;
private int mFPSDivisor = 1;
- private DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
+ private final DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
new DisplayEventReceiver.VsyncEventData();
private final FrameData mFrameData = new FrameData();
@@ -857,7 +857,7 @@
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
- mLastVsyncEventData = vsyncEventData;
+ mLastVsyncEventData.copyFrom(vsyncEventData);
}
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
@@ -1247,7 +1247,7 @@
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
- private VsyncEventData mLastVsyncEventData = new VsyncEventData();
+ private final VsyncEventData mLastVsyncEventData = new VsyncEventData();
FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) {
super(looper, vsyncSource, /* eventRegistration */ 0, layerHandle);
@@ -1287,7 +1287,7 @@
mTimestampNanos = timestampNanos;
mFrame = frame;
- mLastVsyncEventData = vsyncEventData;
+ mLastVsyncEventData.copyFrom(vsyncEventData);
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index b4675e0..54db34e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -146,7 +146,12 @@
mMessageQueue = null;
}
- static final class VsyncEventData {
+ /**
+ * Class to capture all inputs required for syncing events data.
+ *
+ * @hide
+ */
+ public static final class VsyncEventData {
// The amount of frame timeline choices.
// Must be in sync with VsyncEventData::kFrameTimelinesLength in
// frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime
@@ -164,6 +169,12 @@
this.deadline = deadline;
}
+ void copyFrom(FrameTimeline other) {
+ vsyncId = other.vsyncId;
+ expectedPresentationTime = other.expectedPresentationTime;
+ deadline = other.deadline;
+ }
+
// The frame timeline vsync id, used to correlate a frame
// produced by HWUI with the timeline data stored in Surface Flinger.
public long vsyncId = FrameInfo.INVALID_VSYNC_ID;
@@ -203,6 +214,14 @@
this.frameInterval = frameInterval;
}
+ void copyFrom(VsyncEventData other) {
+ preferredFrameTimelineIndex = other.preferredFrameTimelineIndex;
+ frameInterval = other.frameInterval;
+ for (int i = 0; i < frameTimelines.length; i++) {
+ frameTimelines[i].copyFrom(other.frameTimelines[i]);
+ }
+ }
+
public FrameTimeline preferredFrameTimeline() {
return frameTimelines[preferredFrameTimelineIndex];
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 5810642..83de2a0 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -110,16 +110,6 @@
int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
int lastSyncSeqId);
- /*
- * Notify the window manager that an application is relaunching and
- * windows should be prepared for replacement.
- *
- * @param appToken The application
- * @param childrenOnly Whether to only prepare child windows for replacement
- * (for example when main windows are being reused via preservation).
- */
- oneway void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly);
-
/**
* Called by a client to report that it ran out of graphics memory.
*/
@@ -304,7 +294,7 @@
* an input channel where the client can receive input.
*/
void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window,
- in IBinder hostInputToken, int flags, int privateFlags, int type,
+ in IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type,
in IBinder windowToken, in IBinder focusGrantToken, String inputHandleName,
out InputChannel outInputChannel);
@@ -312,7 +302,8 @@
* Update the flags on an input channel associated with a particular surface.
*/
oneway void updateInputChannel(in IBinder channelToken, int displayId,
- in SurfaceControl surface, int flags, int privateFlags, in Region region);
+ in SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
+ in Region region);
/**
* Transfer window focus to an embedded window if the calling window has focus.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 35ed88f..cc846e3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -863,10 +863,8 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION =
"android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
@@ -899,8 +897,6 @@
* android:value="false"/>
* </application>
* </pre>
- *
- * @hide
*/
// TODO(b/263984287): Make this public API.
String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS =
@@ -937,10 +933,8 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS";
/**
@@ -976,10 +970,8 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION =
"android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION";
@@ -1023,10 +1015,8 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH =
"android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
@@ -1073,17 +1063,28 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE =
"android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
/**
* Application level {@link android.content.pm.PackageManager.Property PackageManager
* .Property} for an app to inform the system that the app should be excluded from the
- * compatibility override for orientation set by the device manufacturer.
+ * compatibility override for orientation set by the device manufacturer. When the orientation
+ * override is applied it can:
+ * <ul>
+ * <li>Replace the specific orientation requested by the app with another selected by the
+ device manufacturer, e.g. replace undefined requested by the app with portrait.
+ * <li>Always use an orientation selected by the device manufacturer.
+ * <li>Do one of the above but only when camera connection is open.
+ * </ul>
+ *
+ * <p>This property is different from {@link PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION}
+ * (which is used to avoid orientation loops caused by the incorrect use of {@link
+ * android.app.Activity#setRequestedOrientation}) because this property overrides the app to an
+ * orientation selected by the device manufacturer rather than ignoring one of orientation
+ * requests coming from the app while respecting the previous one.
*
* <p>With this property set to {@code true} or unset, device manufacturers can override
* orientation for the app using their discretion to improve display compatibility.
@@ -1099,10 +1100,8 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE =
"android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
@@ -1144,10 +1143,8 @@
* android:value="true|false"/>
* </application>
* </pre>
- *
- * @hide
*/
- // TODO(b/263984287): Make this public API.
+ // TODO(b/263984287): Add CTS tests.
String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE =
"android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
@@ -2815,7 +2812,7 @@
*
* @hide
*/
- public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002;
+ public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 1 << 1;
/**
* By default, wallpapers are sent new offsets when the wallpaper is scrolled. Wallpapers
@@ -2826,7 +2823,7 @@
*
* @hide
*/
- public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;
+ public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 1 << 2;
/**
* When set {@link LayoutParams#TYPE_APPLICATION_OVERLAY} windows will stay visible, even if
@@ -2835,7 +2832,7 @@
* @hide
*/
@RequiresPermission(permission.SYSTEM_APPLICATION_OVERLAY)
- public static final int PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY = 0x00000008;
+ public static final int PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY = 1 << 3;
/** In a multiuser system if this flag is set and the owner is a system process then this
* window will appear on all user screens. This overrides the default behavior of window
@@ -2845,7 +2842,7 @@
* {@hide} */
@SystemApi
@RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW)
- public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
+ public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 1 << 4;
/**
* Flag to allow this window to have unrestricted gesture exclusion.
@@ -2853,7 +2850,7 @@
* @see View#setSystemGestureExclusionRects(List)
* @hide
*/
- public static final int PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION = 0x00000020;
+ public static final int PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION = 1 << 5;
/**
* Never animate position changes of the window.
@@ -2862,20 +2859,20 @@
* {@hide}
*/
@UnsupportedAppUsage
- public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 0x00000040;
+ public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 1 << 6;
/** Window flag: special flag to limit the size of the window to be
* original size ([320x480] x density). Used to create window for applications
* running under compatibility mode.
*
* {@hide} */
- public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 0x00000080;
+ public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 1 << 7;
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
* it is created.
* {@hide} */
- public static final int PRIVATE_FLAG_SYSTEM_ERROR = 0x00000100;
+ public static final int PRIVATE_FLAG_SYSTEM_ERROR = 1 << 8;
/**
* Flag to indicate that the view hierarchy of the window can only be measured when
@@ -2884,14 +2881,14 @@
* views. This reduces the chances to perform measure.
* {@hide}
*/
- public static final int PRIVATE_FLAG_OPTIMIZE_MEASURE = 0x00000200;
+ public static final int PRIVATE_FLAG_OPTIMIZE_MEASURE = 1 << 9;
/**
* Flag that prevents the wallpaper behind the current window from receiving touch events.
*
* {@hide}
*/
- public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800;
+ public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 1 << 10;
/**
* Flag to force the status bar window to be visible all the time. If the bar is hidden when
@@ -2900,7 +2897,7 @@
*
* {@hide}
*/
- public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 0x00001000;
+ public static final int PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR = 1 << 11;
/**
* Flag to indicate that the window frame should be the requested frame adding the display
@@ -2910,7 +2907,7 @@
*
* {@hide}
*/
- public static final int PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT = 0x00002000;
+ public static final int PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT = 1 << 12;
/**
* Flag that will make window ignore app visibility and instead depend purely on the decor
@@ -2918,39 +2915,28 @@
* drawing after it launches an app.
* @hide
*/
- public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 0x00004000;
-
- /**
- * Flag to indicate that this window is not expected to be replaced across
- * configuration change triggered activity relaunches. In general the WindowManager
- * expects Windows to be replaced after relaunch, and thus it will preserve their surfaces
- * until the replacement is ready to show in order to prevent visual glitch. However
- * some windows, such as PopupWindows expect to be cleared across configuration change,
- * and thus should hint to the WindowManager that it should not wait for a replacement.
- * @hide
- */
- public static final int PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH = 0x00008000;
+ public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 1 << 13;
/**
* Flag to indicate that this child window should always be laid-out in the parent
* frame regardless of the current windowing mode configuration.
* @hide
*/
- public static final int PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME = 0x00010000;
+ public static final int PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME = 1 << 14;
/**
* Flag to indicate that this window is always drawing the status bar background, no matter
* what the other flags are.
* @hide
*/
- public static final int PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS = 0x00020000;
+ public static final int PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS = 1 << 15;
/**
* Flag to indicate that this window needs Sustained Performance Mode if
* the device supports it.
* @hide
*/
- public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 0x00040000;
+ public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 1 << 16;
/**
* Flag to indicate that any window added by an application process that is of type
@@ -2961,7 +2947,7 @@
*/
@SystemApi
@RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
- public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
+ public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 1 << 19;
/**
* Indicates that this window is the rounded corners overlay present on some
@@ -2969,7 +2955,7 @@
* screen magnification, and mirroring.
* @hide
*/
- public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
+ public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 1 << 20;
/**
* Flag to indicate that this window will be excluded while computing the magnifiable region
@@ -2983,7 +2969,7 @@
* </p><p>
* @hide
*/
- public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 0x00200000;
+ public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 1 << 21;
/**
* Flag to prevent the window from being magnified by the accessibility magnifier.
@@ -2991,7 +2977,7 @@
* TODO(b/190623172): This is a temporary solution and need to find out another way instead.
* @hide
*/
- public static final int PRIVATE_FLAG_NOT_MAGNIFIABLE = 0x00400000;
+ public static final int PRIVATE_FLAG_NOT_MAGNIFIABLE = 1 << 22;
/**
* Flag to indicate that the status bar window is in a state such that it forces showing
@@ -3000,54 +2986,54 @@
* It only takes effects if this is set by {@link LayoutParams#TYPE_STATUS_BAR}.
* @hide
*/
- public static final int PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION = 0x00800000;
+ public static final int PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION = 1 << 23;
/**
* Flag to indicate that the window is color space agnostic, and the color can be
* interpreted to any color space.
* @hide
*/
- public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 0x01000000;
+ public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 1 << 24;
/**
* Flag to request creation of a BLAST (Buffer as LayerState) Layer.
* If not specified the client will receive a BufferQueue layer.
* @hide
*/
- public static final int PRIVATE_FLAG_USE_BLAST = 0x02000000;
+ public static final int PRIVATE_FLAG_USE_BLAST = 1 << 25;
/**
* Flag to indicate that the window is controlling the appearance of system bars. So we
* don't need to adjust it by reading its system UI flags for compatibility.
* @hide
*/
- public static final int PRIVATE_FLAG_APPEARANCE_CONTROLLED = 0x04000000;
+ public static final int PRIVATE_FLAG_APPEARANCE_CONTROLLED = 1 << 26;
/**
* Flag to indicate that the window is controlling the behavior of system bars. So we don't
* need to adjust it by reading its window flags or system UI flags for compatibility.
* @hide
*/
- public static final int PRIVATE_FLAG_BEHAVIOR_CONTROLLED = 0x08000000;
+ public static final int PRIVATE_FLAG_BEHAVIOR_CONTROLLED = 1 << 27;
/**
* Flag to indicate that the window is controlling how it fits window insets on its own.
* So we don't need to adjust its attributes for fitting window insets.
* @hide
*/
- public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 0x10000000;
+ public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 1 << 28;
/**
* Flag to indicate that the window is a trusted overlay.
* @hide
*/
- public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 0x20000000;
+ public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 1 << 29;
/**
* Flag to indicate that the parent frame of a window should be inset by IME.
* @hide
*/
- public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000;
+ public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 1 << 30;
/**
* Flag to indicate that we want to intercept and handle global drag and drop for all users.
@@ -3062,7 +3048,7 @@
* @hide
*/
@RequiresPermission(permission.MANAGE_ACTIVITY_TASKS)
- public static final int PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP = 0x80000000;
+ public static final int PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP = 1 << 31;
/**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
@@ -3093,7 +3079,6 @@
PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR,
PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
- PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS,
PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
@@ -3169,10 +3154,6 @@
equals = PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
name = "FORCE_DECOR_VIEW_VISIBILITY"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
- equals = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
- name = "WILL_NOT_REPLACE_ON_RELAUNCH"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
equals = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
name = "LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"),
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b157ea0..0560caf 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -139,7 +139,7 @@
try {
mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags,
- state.mInputRegion);
+ state.mParams.inputFeatures, state.mInputRegion);
} catch (RemoteException e) {
Log.e(TAG, "Failed to update surface input channel: ", e);
}
@@ -189,12 +189,13 @@
mRealWm.grantInputChannel(displayId,
new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
window, mHostInputToken,
- attrs.flags, attrs.privateFlags, attrs.type, attrs.token,
- mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
+ attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type,
+ attrs.token, mFocusGrantToken, attrs.getTitle().toString(),
+ outInputChannel);
} else {
mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
- attrs.privateFlags, attrs.type, attrs.token, mFocusGrantToken,
- attrs.getTitle().toString(), outInputChannel);
+ attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
+ mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to grant input to surface: ", e);
@@ -381,16 +382,19 @@
outMergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
}
- if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0
- && state.mInputChannelToken != null) {
+ final int inputChangeMask = WindowManager.LayoutParams.FLAGS_CHANGED
+ | WindowManager.LayoutParams.INPUT_FEATURES_CHANGED;
+ if ((attrChanges & inputChangeMask) != 0 && state.mInputChannelToken != null) {
try {
- if(mRealWm instanceof IWindowSession.Stub) {
+ if (mRealWm instanceof IWindowSession.Stub) {
mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
new SurfaceControl(sc, "WindowlessWindowManager.relayout"),
- attrs.flags, attrs.privateFlags, state.mInputRegion);
+ attrs.flags, attrs.privateFlags, attrs.inputFeatures,
+ state.mInputRegion);
} else {
mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
- attrs.flags, attrs.privateFlags, state.mInputRegion);
+ attrs.flags, attrs.privateFlags, attrs.inputFeatures,
+ state.mInputRegion);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to update surface input channel: ", e);
@@ -415,10 +419,6 @@
}
@Override
- public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) {
- }
-
- @Override
public boolean outOfMemory(android.view.IWindow window) {
return false;
}
@@ -564,14 +564,14 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
- IBinder hostInputToken, int flags, int privateFlags, int type,
+ IBinder hostInputToken, int flags, int privateFlags, int inputFeatures, int type,
IBinder windowToken, IBinder focusGrantToken, String inputHandleName,
InputChannel outInputChannel) {
}
@Override
public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
- int flags, int privateFlags, Region region) {
+ int flags, int privateFlags, int inputFeatures, Region region) {
}
@Override
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index c1ec168..d54addb 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -19,7 +19,6 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1644,8 +1643,7 @@
p.width = mLastWidth = mWidth;
}
- p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
- | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+ p.privateFlags = PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
// Used for debugging.
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 076e4e1..1505ccc 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -185,8 +185,13 @@
private static void preloadSharedLibraries() {
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
- System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
+
+ // TODO(b/206676167): This library is only used for renderscript today. When renderscript is
+ // removed, this load can be removed as well.
+ if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
+ System.loadLibrary("compiler_rt");
+ }
}
native private static void nativePreloadAppProcessHALs();
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 739055e..0c3ff6c 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -169,21 +169,25 @@
gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval,
vsyncEventData.frameInterval);
- jobjectArray frameTimelinesObj = reinterpret_cast<jobjectArray>(
- env->GetObjectField(vsyncEventDataObj.get(),
- gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
- .frameTimelines));
+ ScopedLocalRef<jobjectArray>
+ frameTimelinesObj(env,
+ reinterpret_cast<jobjectArray>(
+ env->GetObjectField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo
+ .vsyncEventDataClassInfo
+ .frameTimelines)));
for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
- jobject frameTimelineObj = env->GetObjectArrayElement(frameTimelinesObj, i);
- env->SetLongField(frameTimelineObj,
+ ScopedLocalRef<jobject>
+ frameTimelineObj(env, env->GetObjectArrayElement(frameTimelinesObj.get(), i));
+ env->SetLongField(frameTimelineObj.get(),
gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId,
frameTimeline.vsyncId);
- env->SetLongField(frameTimelineObj,
+ env->SetLongField(frameTimelineObj.get(),
gDisplayEventReceiverClassInfo.frameTimelineClassInfo
.expectedPresentationTime,
frameTimeline.expectedPresentationTime);
- env->SetLongField(frameTimelineObj,
+ env->SetLongField(frameTimelineObj.get(),
gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline,
frameTimeline.deadlineTimestamp);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 479ea4e..06e91c3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -824,6 +824,9 @@
<protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" />
<protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW" />
<protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW" />
+ <protected-broadcast android:name="android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.admin.action.DEVICE_POLICY_SET_RESULT" />
+ <protected-broadcast android:name="android.app.admin.action.DEVICE_POLICY_CHANGED" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -5303,12 +5306,12 @@
{@link android.Manifest.permission#USE_EXACT_ALARM} once it targets API
{@link android.os.Build.VERSION_CODES#TIRAMISU}. All apps using exact alarms for secondary
features (which should still be user facing) should continue using this permission.
- <p>Protection level: appop
+ <p>Protection level: signature|privileged|appop
-->
<permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
android:label="@string/permlab_schedule_exact_alarm"
android:description="@string/permdesc_schedule_exact_alarm"
- android:protectionLevel="normal|appop"/>
+ android:protectionLevel="signature|privileged|appop"/>
<!-- Allows apps to use exact alarms just like with {@link
android.Manifest.permission#SCHEDULE_EXACT_ALARM} but without needing to request this
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
index e752431..8eae064 100644
--- a/core/res/res/layout/notification_expand_button.xml
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -19,23 +19,28 @@
android:id="@+id/expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_header_height"
android:layout_gravity="top|end"
android:contentDescription="@string/expand_button_content_description_collapsed"
- android:padding="16dp"
+ android:paddingHorizontal="16dp"
>
<LinearLayout
android:id="@+id/expand_button_pill"
android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_expand_button_pill_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_expand_button_pill_height"
android:orientation="horizontal"
android:background="@drawable/expand_button_pill_bg"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
>
<TextView
android:id="@+id/expand_button_number"
android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_expand_button_pill_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_expand_button_pill_height"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:gravity="center_vertical"
android:paddingStart="8dp"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index fd787f6..16a8bb7 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -79,7 +79,8 @@
<NotificationTopLineView
android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_headerless_line_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
android:clipChildren="false"
android:theme="@style/Theme.DeviceDefault.Notification"
>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
index 1b3bd26..76bcc96 100644
--- a/core/res/res/layout/notification_template_material_call.xml
+++ b/core/res/res/layout/notification_template_material_call.xml
@@ -29,7 +29,8 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="88dp"
+ android:layout_height="wrap_content"
+ android:minHeight="88dp"
android:orientation="horizontal"
>
@@ -41,6 +42,7 @@
android:layout_marginStart="@dimen/conversation_content_start"
android:orientation="vertical"
android:minHeight="68dp"
+ android:paddingBottom="@dimen/notification_headerless_margin_twoline"
>
<include
@@ -49,7 +51,10 @@
android:layout_height="wrap_content"
/>
- <include layout="@layout/notification_template_text" />
+ <include layout="@layout/notification_template_text"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_text_height"
+ />
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 95ddc2e..df32d30 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -19,7 +19,8 @@
android:id="@+id/status_bar_latest_event_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_min_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_min_height"
android:tag="media"
>
@@ -77,7 +78,8 @@
<NotificationTopLineView
android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_headerless_line_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
android:clipChildren="false"
android:theme="@style/Theme.DeviceDefault.Notification"
>
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index bef1d0b..3e82bd1 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -102,7 +102,8 @@
<NotificationTopLineView
android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_headerless_line_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
android:layout_marginStart="@dimen/notification_content_margin_start"
android:clipChildren="false"
android:theme="@style/Theme.DeviceDefault.Notification"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index b35481d..97e753e 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -227,6 +227,12 @@
<string-array name="device_state_notification_thermal_contents">
<item>@string/concurrent_display_notification_thermal_content</item>
</string-array>
+ <string-array name="device_state_notification_power_save_titles">
+ <item>@string/concurrent_display_notification_power_save_title</item>
+ </string-array>
+ <string-array name="device_state_notification_power_save_contents">
+ <item>@string/concurrent_display_notification_power_save_content</item>
+ </string-array>
<!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner of the
demo device provisioning permissions. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 5bb86dc..80bf795 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -356,7 +356,7 @@
<dimen name="notification_headerless_margin_twoline">20dp</dimen>
<!-- The height of each of the 1 or 2 lines in the headerless notification template -->
- <dimen name="notification_headerless_line_height">24dp</dimen>
+ <dimen name="notification_headerless_line_height">24sp</dimen>
<!-- vertical margin for the headerless notification content -->
<dimen name="notification_headerless_min_height">56dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d8e69d7..6afdae5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6267,6 +6267,12 @@
<string name="concurrent_display_notification_thermal_title">Device is too warm</string>
<!-- Content of concurrent display thermal notification. [CHAR LIMIT=NONE] -->
<string name="concurrent_display_notification_thermal_content">Dual Screen is unavailable because your phone is getting too warm</string>
+ <!-- Title of concurrent display power saver notification. [CHAR LIMIT=NONE] -->
+ <string name="concurrent_display_notification_power_save_title">Dual Screen is unavailable</string>
+ <!-- Content of concurrent display power saver notification. [CHAR LIMIT=NONE] -->
+ <string name="concurrent_display_notification_power_save_content">Dual Screen is unavailable because Battery Saver is on. You can turn this off in Settings.</string>
+ <!-- Text of power saver notification settings button. [CHAR LIMIT=NONE] -->
+ <string name="device_state_notification_settings_button">Go to Settings</string>
<!-- Text of device state notification turn off button. [CHAR LIMIT=NONE] -->
<string name="device_state_notification_turn_off_button">Turn off</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c10612e..1cb56e0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4931,12 +4931,17 @@
<java-symbol type="array" name="device_state_notification_active_contents"/>
<java-symbol type="array" name="device_state_notification_thermal_titles"/>
<java-symbol type="array" name="device_state_notification_thermal_contents"/>
+ <java-symbol type="array" name="device_state_notification_power_save_titles"/>
+ <java-symbol type="array" name="device_state_notification_power_save_contents"/>
<java-symbol type="string" name="concurrent_display_notification_name"/>
<java-symbol type="string" name="concurrent_display_notification_active_title"/>
<java-symbol type="string" name="concurrent_display_notification_active_content"/>
<java-symbol type="string" name="concurrent_display_notification_thermal_title"/>
<java-symbol type="string" name="concurrent_display_notification_thermal_content"/>
+ <java-symbol type="string" name="concurrent_display_notification_power_save_title"/>
+ <java-symbol type="string" name="concurrent_display_notification_power_save_content"/>
<java-symbol type="string" name="device_state_notification_turn_off_button"/>
+ <java-symbol type="string" name="device_state_notification_settings_button"/>
<java-symbol type="bool" name="config_independentLockscreenLiveWallpaper"/>
<java-symbol type="integer" name="config_deviceStateConcurrentRearDisplay" />
<java-symbol type="string" name="config_rearDisplayPhysicalAddress" />
diff --git a/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java
new file mode 100644
index 0000000..d7b911d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/DeleteRangeGestureTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.RectF;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@ApiTest(apis = {"android.view.inputmethod.DeleteRangeGesture.Builder#setGranularity",
+ "android.view.inputmethod.DeleteRangeGesture.Builder#setDeletionStartArea",
+ "android.view.inputmethod.DeleteRangeGesture.Builder#setDeletionEndArea",
+ "android.view.inputmethod.DeleteRangeGesture.Builder#setFallbackText",
+ "android.view.inputmethod.DeleteRangeGesture.Builder#build"})
+public class DeleteRangeGestureTest {
+ private static final RectF DELETION_START_RECTANGLE = new RectF(1, 2, 3, 4);
+ private static final RectF DELETION_END_RECTANGLE = new RectF(0, 2, 3, 4);
+ private static final String FALLBACK_TEXT = "fallback_test";
+
+ @Test
+ public void testBuilder() {
+ DeleteRangeGesture.Builder builder = new DeleteRangeGesture.Builder();
+ DeleteRangeGesture gesture = builder.setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .setDeletionStartArea(DELETION_START_RECTANGLE)
+ .setDeletionEndArea(DELETION_END_RECTANGLE)
+ .setFallbackText(FALLBACK_TEXT).build();
+ assertNotNull(gesture);
+ assertEquals(HandwritingGesture.GRANULARITY_WORD, gesture.getGranularity());
+ assertEquals(DELETION_START_RECTANGLE, gesture.getDeletionStartArea());
+ assertEquals(DELETION_END_RECTANGLE, gesture.getDeletionEndArea());
+ assertEquals(FALLBACK_TEXT, gesture.getFallbackText());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java
new file mode 100644
index 0000000..47a724d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/InsertGestureTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.PointF;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@ApiTest(apis = {"android.view.inputmethod.InsertGesture.Builder#setInsertionPoint",
+ "android.view.inputmethod.InsertGesture.Builder#setTextToInsert",
+ "android.view.inputmethod.InsertGesture.Builder#setFallbackText",
+ "android.view.inputmethod.InsertGesture.Builder#build"})
+public class InsertGestureTest {
+ private static final PointF INSERTION_POINT = new PointF(1, 2);
+ private static final String FALLBACK_TEXT = "fallback_text";
+ private static final String TEXT_TO_INSERT = "text";
+
+ @Test
+ public void testBuilder() {
+ InsertGesture.Builder builder = new InsertGesture.Builder();
+ InsertGesture gesture = builder.setInsertionPoint(INSERTION_POINT)
+ .setTextToInsert(TEXT_TO_INSERT)
+ .setFallbackText(FALLBACK_TEXT).build();
+ assertNotNull(gesture);
+ assertEquals(INSERTION_POINT, gesture.getInsertionPoint());
+ assertEquals(FALLBACK_TEXT, gesture.getFallbackText());
+ assertEquals(TEXT_TO_INSERT, gesture.getTextToInsert());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java
new file mode 100644
index 0000000..11ddba1
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/InsertModeGestureTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.PointF;
+import android.os.CancellationSignal;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@ApiTest(apis = {"android.view.inputmethod.InsertModeGesture.Builder#setInsertionPoint",
+ "android.view.inputmethod.InsertModeGesture.Builder#setCancellationSignal",
+ "android.view.inputmethod.InsertModeGesture.Builder#setFallbackText",
+ "android.view.inputmethod.InsertModeGesture.Builder#build"})
+public class InsertModeGestureTest {
+ private static final PointF INSERTION_POINT = new PointF(1, 2);
+ private static final String FALLBACK_TEXT = "fallback_text";
+ private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal();
+
+ @Test
+ public void testBuilder() {
+ InsertModeGesture.Builder builder = new InsertModeGesture.Builder();
+ InsertModeGesture gesture = builder.setInsertionPoint(INSERTION_POINT)
+ .setCancellationSignal(CANCELLATION_SIGNAL)
+ .setFallbackText(FALLBACK_TEXT).build();
+ assertNotNull(gesture);
+ assertEquals(INSERTION_POINT, gesture.getInsertionPoint());
+ assertEquals(FALLBACK_TEXT, gesture.getFallbackText());
+ assertEquals(CANCELLATION_SIGNAL, gesture.getCancellationSignal());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java
new file mode 100644
index 0000000..b2eb07c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/SelectGestureTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.RectF;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@ApiTest(apis = {"android.view.inputmethod.SelectGesture.Builder#setGranularity",
+ "android.view.inputmethod.SelectGesture.Builder#setSelectionArea",
+ "android.view.inputmethod.SelectGesture.Builder#setFallbackText",
+ "android.view.inputmethod.SelectGesture.Builder#build"})
+public class SelectGestureTest {
+ private static final RectF SELECTION_RECTANGLE = new RectF(1, 2, 3, 4);
+ private static final String FALLBACK_TEXT = "fallback_text";
+
+ @Test
+ public void testBuilder() {
+ SelectGesture.Builder builder = new SelectGesture.Builder();
+ SelectGesture gesture = builder.setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .setSelectionArea(SELECTION_RECTANGLE)
+ .setFallbackText(FALLBACK_TEXT).build();
+ assertNotNull(gesture);
+ assertEquals(HandwritingGesture.GRANULARITY_WORD, gesture.getGranularity());
+ assertEquals(SELECTION_RECTANGLE, gesture.getSelectionArea());
+ assertEquals(FALLBACK_TEXT, gesture.getFallbackText());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java
new file mode 100644
index 0000000..df63a4a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/SelectRangeGestureTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.RectF;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@ApiTest(apis = {"android.view.inputmethod.SelectRangeGesture.Builder#setGranularity",
+ "android.view.inputmethod.SelectRangeGesture.Builder#setSelectionStartArea",
+ "android.view.inputmethod.SelectRangeGesture.Builder#setSelectionEndArea",
+ "android.view.inputmethod.SelectRangeGesture.Builder#setFallbackText",
+ "android.view.inputmethod.SelectRangeGesture.Builder#build"})
+public class SelectRangeGestureTest {
+ private static final RectF SELECTION_START_RECTANGLE = new RectF(1, 2, 3, 4);
+ private static final RectF SELECTION_END_RECTANGLE = new RectF(0, 2, 3, 4);
+ private static final String FALLBACK_TEXT = "fallback_text";
+
+ @Test
+ public void testBuilder() {
+ SelectRangeGesture.Builder builder = new SelectRangeGesture.Builder();
+ SelectRangeGesture gesture = builder.setGranularity(HandwritingGesture.GRANULARITY_WORD)
+ .setSelectionStartArea(SELECTION_START_RECTANGLE)
+ .setSelectionEndArea(SELECTION_END_RECTANGLE)
+ .setFallbackText(FALLBACK_TEXT).build();
+ assertNotNull(gesture);
+ assertEquals(HandwritingGesture.GRANULARITY_WORD, gesture.getGranularity());
+ assertEquals(SELECTION_START_RECTANGLE, gesture.getSelectionStartArea());
+ assertEquals(SELECTION_END_RECTANGLE, gesture.getSelectionEndArea());
+ assertEquals(FALLBACK_TEXT, gesture.getFallbackText());
+ }
+}
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
index 2d6ae2e..19c52a6 100644
--- a/data/etc/com.android.emergency.xml
+++ b/data/etc/com.android.emergency.xml
@@ -20,6 +20,7 @@
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.SCHEDULE_EXACT_ALARM"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<!-- Required to update emergency gesture settings -->
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 5d303cf..0faf62e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -321,6 +321,7 @@
<permission name="android.permission.REGISTER_CONNECTION_MANAGER"/>
<permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
<permission name="android.permission.RETRIEVE_WINDOW_CONTENT"/>
+ <permission name="android.permission.SCHEDULE_EXACT_ALARM"/>
<permission name="android.permission.SET_ALWAYS_FINISH"/>
<permission name="android.permission.SET_ANIMATION_SCALE"/>
<permission name="android.permission.SET_DEBUG_APP"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ffc5ff2..5549f88 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -259,12 +259,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1878839956": {
- "message": "Marking app token %s with replacing windows.",
- "level": "DEBUG",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1872288685": {
"message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s",
"level": "VERBOSE",
@@ -463,12 +457,6 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/Task.java"
},
- "-1698815688": {
- "message": "Resetting app token %s of replacing window marks.",
- "level": "DEBUG",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1679411993": {
"message": "setVr2dDisplayId called for: %d",
"level": "DEBUG",
@@ -481,12 +469,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1661704580": {
- "message": "Attempted to set replacing window on non-existing app token %s",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1647332198": {
"message": "remove RecentTask %s when finishing user %d",
"level": "INFO",
@@ -613,12 +595,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1515151503": {
- "message": ">>> OPEN TRANSACTION removeReplacedWindows",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/RootWindowContainer.java"
- },
"-1501564055": {
"message": "Organized TaskFragment is not ready= %s",
"level": "VERBOSE",
@@ -691,12 +667,6 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
- "-1471946192": {
- "message": "Marking app token %s with replacing child windows.",
- "level": "DEBUG",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1471518109": {
"message": "Set animatingExit: reason=onAppVisibilityChanged win=%s",
"level": "DEBUG",
@@ -919,12 +889,6 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
- "-1270731689": {
- "message": "Attempted to set replacing window on app token with no content %s",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1263316010": {
"message": "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and oldRotation=%s (%d)",
"level": "VERBOSE",
@@ -1417,12 +1381,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
},
- "-799003045": {
- "message": "Set animatingExit: reason=remove\/replaceWindow win=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ANIM",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"-787664727": {
"message": "Cannot launch dream activity due to invalid state. dream component: %s packageName: %s",
"level": "ERROR",
@@ -1453,6 +1411,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-778347463": {
+ "message": "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b mDisplayFrozen=%b callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-775004869": {
"message": "Not a match: %s",
"level": "DEBUG",
@@ -1963,12 +1927,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-320419645": {
- "message": "Removing replaced window: %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"-319689203": {
"message": "Reparenting to original parent: %s for %s",
"level": "INFO",
@@ -2335,12 +2293,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "38267433": {
- "message": "Attempted to reset replacing window on non-existing app token %s",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"45285419": {
"message": "startingWindow was set but startingSurface==null, couldn't remove",
"level": "VERBOSE",
@@ -2989,12 +2941,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "594260654": {
- "message": "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b mWillReplaceWindow=%b mDisplayFrozen=%b callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"600140673": {
"message": "checkBootAnimationComplete: Waiting for anim complete",
"level": "INFO",
@@ -3829,12 +3775,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1423592961": {
- "message": "<<< CLOSE TRANSACTION removeReplacedWindows",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/RootWindowContainer.java"
- },
"1430336882": {
"message": "findFocusedWindow: focusedApp windows not focusable using new focus @ %s",
"level": "VERBOSE",
@@ -3901,12 +3841,6 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "1515161239": {
- "message": "removeDeadWindows: %s",
- "level": "WARN",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1518495446": {
"message": "removeWindowToken: Attempted to remove non-existing token: %s",
"level": "WARN",
@@ -4273,12 +4207,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
- "1921821199": {
- "message": "Preserving %s until the new one is added",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"1928325128": {
"message": "Run showImeRunner",
"level": "DEBUG",
@@ -4489,12 +4417,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
- "2114149926": {
- "message": "Not removing %s because app died while it's visible",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"2117696413": {
"message": "moveTaskToFront: moving taskId=%d",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 298ad30..8d1da0f7 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -63,11 +63,11 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/bubble_manage_menu_dont_bubble_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
- android:text="@string/bubbles_dont_bubble_conversation" />
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 3082962..9f6cf79 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -146,6 +146,8 @@
<string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
<!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
<string name="bubble_dismiss_text">Dismiss bubble</string>
+ <!-- Button text to stop an app from bubbling [CHAR LIMIT=60]-->
+ <string name="bubbles_dont_bubble">Don\u2019t bubble</string>
<!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]-->
<string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string>
<!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 0b947c8..deb4fd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -844,6 +844,8 @@
private DismissView mDismissView;
private ViewGroup mManageMenu;
+ private TextView mManageDontBubbleText;
+ private ViewGroup mManageSettingsView;
private ImageView mManageSettingsIcon;
private TextView mManageSettingsText;
private boolean mShowingManage = false;
@@ -1217,7 +1219,11 @@
mUnbubbleConversationCallback.accept(mBubbleData.getSelectedBubble().getKey());
});
- mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener(
+ mManageDontBubbleText = mManageMenu
+ .findViewById(R.id.bubble_manage_menu_dont_bubble_text);
+
+ mManageSettingsView = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container);
+ mManageSettingsView.setOnClickListener(
view -> {
showManageMenu(false /* show */);
final BubbleViewProvider bubble = mBubbleData.getSelectedBubble();
@@ -2868,10 +2874,19 @@
// name and icon.
if (show) {
final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
- if (bubble != null) {
+ if (bubble != null && !bubble.isAppBubble()) {
+ // Setup options for non app bubbles
+ mManageDontBubbleText.setText(R.string.bubbles_dont_bubble_conversation);
mManageSettingsIcon.setImageBitmap(bubble.getRawAppBadge());
mManageSettingsText.setText(getResources().getString(
R.string.bubbles_app_settings, bubble.getAppName()));
+ mManageSettingsView.setVisibility(VISIBLE);
+ } else {
+ // Setup options for app bubbles
+ mManageDontBubbleText.setText(R.string.bubbles_dont_bubble);
+ // App bubbles are not notification based
+ // so we don't show the option to go to notification settings
+ mManageSettingsView.setVisibility(GONE);
}
}
@@ -2936,6 +2951,15 @@
}
}
+ /**
+ * Checks whether manage menu notification settings action is available and visible
+ * Used for testing
+ */
+ @VisibleForTesting
+ public boolean isManageMenuSettingsVisible() {
+ return mManageSettingsView != null && mManageSettingsView.getVisibility() == VISIBLE;
+ }
+
private void updateExpandedBubble() {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "updateExpandedBubble()");
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 be9b529..ea2559a 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
@@ -104,7 +104,6 @@
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -119,6 +118,8 @@
UserChangeListener {
private static final String TAG = "PipController";
+ private static final String LAUNCHER_KEEP_CLEAR_AREA_TAG = "hotseat";
+
private static final long PIP_KEEP_CLEAR_AREAS_DELAY =
SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200);
@@ -934,12 +935,10 @@
0, mPipBoundsState.getDisplayBounds().bottom - height,
mPipBoundsState.getDisplayBounds().right,
mPipBoundsState.getDisplayBounds().bottom);
- Set<Rect> restrictedKeepClearAreas = new HashSet<>(
- mPipBoundsState.getRestrictedKeepClearAreas());
- restrictedKeepClearAreas.add(rect);
- mPipBoundsState.setKeepClearAreas(restrictedKeepClearAreas,
- mPipBoundsState.getUnrestrictedKeepClearAreas());
+ mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, rect);
updatePipPositionForKeepClearAreas();
+ } else {
+ mPipBoundsState.removeNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a5546e5..a1eaf85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -259,37 +259,6 @@
}
};
- private final SplitScreenTransitions.TransitionFinishedCallback
- mRecentTransitionFinishedCallback =
- new SplitScreenTransitions.TransitionFinishedCallback() {
- @Override
- public void onFinished(WindowContainerTransaction finishWct,
- SurfaceControl.Transaction finishT) {
- // Check if the recent transition is finished by returning to the current
- // split, so we
- // can restore the divider bar.
- for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
- final WindowContainerTransaction.HierarchyOp op =
- finishWct.getHierarchyOps().get(i);
- final IBinder container = op.getContainer();
- if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
- && (mMainStage.containsContainer(container)
- || mSideStage.containsContainer(container))) {
- updateSurfaceBounds(mSplitLayout, finishT,
- false /* applyResizingOffset */);
- setDividerVisibility(true, finishT);
- return;
- }
- }
-
- // Dismiss the split screen if it's not returning to split.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
- setSplitsVisible(false);
- setDividerVisibility(false, finishT);
- logExit(EXIT_REASON_UNKNOWN);
- }
- };
-
protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
@@ -388,6 +357,11 @@
return mMainStage.isActive();
}
+ /** Checks if `transition` is a pending enter-split transition. */
+ public boolean isPendingEnter(IBinder transition) {
+ return mSplitTransitions.isPendingEnter(transition);
+ }
+
@StageType
int getStageOfTask(int taskId) {
if (mMainStage.containsTask(taskId)) {
@@ -2264,11 +2238,16 @@
}
} else if (isOpening && inFullscreen) {
final int activityType = triggerTask.getActivityType();
- if (activityType == ACTIVITY_TYPE_HOME
- || activityType == ACTIVITY_TYPE_RECENTS) {
- // Enter overview panel, so start recent transition.
- mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
- mRecentTransitionFinishedCallback);
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
+ if (request.getRemoteTransition() != null) {
+ // starting recents/home, so don't handle this and let it fall-through to
+ // the remote handler.
+ return null;
+ }
+ // Need to use the old stuff for non-remote animations, otherwise we don't
+ // exit split-screen.
+ mSplitTransitions.setRecentTransition(transition, null /* remote */,
+ this::onRecentsInSplitAnimationFinish);
}
}
} else {
@@ -2398,7 +2377,7 @@
shouldAnimate = startPendingEnterAnimation(
transition, info, startTransaction, finishTransaction);
} else if (mSplitTransitions.isPendingRecent(transition)) {
- shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
+ onRecentsInSplitAnimationStart(startTransaction);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
shouldAnimate = startPendingDismissAnimation(
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
@@ -2653,10 +2632,35 @@
return true;
}
- private boolean startPendingRecentAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ /** Call this when starting the open-recents animation while split-screen is active. */
+ public void onRecentsInSplitAnimationStart(@NonNull SurfaceControl.Transaction t) {
setDividerVisibility(false, t);
- return true;
+ }
+
+ /** Call this when the recents animation during split-screen finishes. */
+ public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
+ SurfaceControl.Transaction finishT) {
+ // Check if the recent transition is finished by returning to the current
+ // split, so we can restore the divider bar.
+ for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp op =
+ finishWct.getHierarchyOps().get(i);
+ final IBinder container = op.getContainer();
+ if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+ && (mMainStage.containsContainer(container)
+ || mSideStage.containsContainer(container))) {
+ updateSurfaceBounds(mSplitLayout, finishT,
+ false /* applyResizingOffset */);
+ setDividerVisibility(true, finishT);
+ return;
+ }
+ }
+
+ // Dismiss the split screen if it's not returning to split.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
+ setSplitsVisible(false);
+ setDividerVisibility(false, finishT);
+ logExit(EXIT_REASON_UNKNOWN);
}
private void addDividerBarToTransition(@NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 2e86448..d094892 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -25,6 +26,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
+import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -68,14 +70,20 @@
/** Pip was entered while handling an intent with its own remoteTransition. */
static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3;
+ /** Recents transition while split-screen active. */
+ static final int TYPE_RECENTS_DURING_SPLIT = 4;
+
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
/** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */
static final int ANIM_TYPE_GOING_HOME = 1;
+ /** For RECENTS_DURING_SPLIT, is set when this turns into a pair->pair task switch. */
+ static final int ANIM_TYPE_PAIR_TO_PAIR = 1;
+
final int mType;
- int mAnimType = 0;
+ int mAnimType = ANIM_TYPE_DEFAULT;
final IBinder mTransition;
Transitions.TransitionHandler mLeftoversHandler = null;
@@ -167,6 +175,27 @@
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
return handler.second;
+ } else if (mSplitHandler.isSplitActive()
+ && isOpeningType(request.getType())
+ && request.getTriggerTask() != null
+ && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && (request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME
+ || request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_RECENTS)
+ && request.getRemoteTransition() != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ + "Split-Screen is active, so treat it as Mixed.");
+ Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler =
+ mPlayer.dispatchRequest(transition, request, this);
+ if (handler == null) {
+ android.util.Log.e(Transitions.TAG, " No handler for remote? This is unexpected"
+ + ", there should at-least be RemoteHandler.");
+ return null;
+ }
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
+ mixed.mLeftoversHandler = handler.first;
+ mActiveTransitions.add(mixed);
+ return handler.second;
}
return null;
}
@@ -216,6 +245,9 @@
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
finishTransaction, finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
+ return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
+ finishCallback);
} else {
mActiveTransitions.remove(mixed);
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -441,12 +473,40 @@
return true;
}
+ private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // Split-screen is only interested in the recents transition finishing (and merging), so
+ // just wrap finish and start recents animation directly.
+ Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+ mixed.mInFlightSubAnimations = 0;
+ mActiveTransitions.remove(mixed);
+ // If pair-to-pair switching, the post-recents clean-up isn't needed.
+ if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
+ wct = wct != null ? wct : new WindowContainerTransaction();
+ mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
+ }
+ mSplitHandler.onTransitionAnimationComplete();
+ finishCallback.onTransitionFinished(wct, wctCB);
+ };
+ mixed.mInFlightSubAnimations = 1;
+ mSplitHandler.onRecentsInSplitAnimationStart(startTransaction);
+ final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
+ startTransaction, finishTransaction, finishCB);
+ if (!handled) {
+ mActiveTransitions.remove(mixed);
+ }
+ return handled;
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
for (int i = 0; i < mActiveTransitions.size(); ++i) {
- if (mActiveTransitions.get(i) != mergeTarget) continue;
+ if (mActiveTransitions.get(i).mTransition != mergeTarget) continue;
MixedTransition mixed = mActiveTransitions.get(i);
if (mixed.mInFlightSubAnimations <= 0) {
// Already done, so no need to end it.
@@ -474,6 +534,14 @@
mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
finishCallback);
}
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
+ if (mSplitHandler.isPendingEnter(transition)) {
+ // Recents -> enter-split means that we are switching from one pair to
+ // another pair.
+ mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
+ }
+ mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
} else {
throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ mixed.mType);
@@ -493,6 +561,8 @@
if (mixed == null) return;
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
+ mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 0a9c331..8cb575c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -103,6 +103,7 @@
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
+ 0 /* inputFeatures */,
TYPE_APPLICATION,
null /* windowToken */,
mFocusGrantToken,
@@ -208,6 +209,7 @@
mDecorationSurface,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
+ 0 /* inputFeatures */,
touchRegion);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 3901dab..df78d92 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -35,6 +35,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -249,7 +250,7 @@
@Test
@UiThreadTest
- public void testEnterRecents() {
+ public void testEnterRecentsAndCommit() {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -258,27 +259,65 @@
.build();
// Create a request to bring home forward
- TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
+ TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask,
+ mock(RemoteTransition.class));
IBinder transition = mock(IBinder.class);
WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
-
- assertTrue(result.isEmpty());
+ // Don't handle recents opening
+ assertNull(result);
// make sure we haven't made any local changes yet (need to wait until transition is ready)
assertTrue(mStageCoordinator.isSplitScreenVisible());
- // simulate the transition
- TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, 0)
- .addChange(TRANSIT_TO_FRONT, homeTask)
- .addChange(TRANSIT_TO_BACK, mMainChild)
- .addChange(TRANSIT_TO_BACK, mSideChild)
- .build();
+ // simulate the start of recents transition
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
- mStageCoordinator.startAnimation(transition, info,
- mock(SurfaceControl.Transaction.class),
- mock(SurfaceControl.Transaction.class),
- mock(Transitions.TransitionFinishCallback.class));
+ mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // Make sure it cleans-up if recents doesn't restore
+ WindowContainerTransaction commitWCT = new WindowContainerTransaction();
+ mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
+ mock(SurfaceControl.Transaction.class));
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testEnterRecentsAndRestore() {
+ enterSplit();
+
+ ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .build();
+
+ // Create a request to bring home forward
+ TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask,
+ mock(RemoteTransition.class));
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
+ // Don't handle recents opening
+ assertNull(result);
+
+ // make sure we haven't made any local changes yet (need to wait until transition is ready)
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // simulate the start of recents transition
+ mMainStage.onTaskVanished(mMainChild);
+ mSideStage.onTaskVanished(mSideChild);
+ mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // Make sure we remain in split after recents restores.
+ WindowContainerTransaction restoreWCT = new WindowContainerTransaction();
+ restoreWCT.reorder(mMainChild.token, true /* toTop */);
+ restoreWCT.reorder(mSideChild.token, true /* toTop */);
+ // simulate the restoreWCT being applied:
+ mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
+ mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
+ mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
+ mock(SurfaceControl.Transaction.class));
assertTrue(mStageCoordinator.isSplitScreenVisible());
}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b0896da..9df6822 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -91,6 +91,8 @@
bool Properties::isLowRam = false;
bool Properties::isSystemOrPersistent = false;
+float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number
+
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
@@ -150,6 +152,11 @@
enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, true);
+ auto hdrHeadroom = (float)atof(base::GetProperty(PROPERTY_8BIT_HDR_HEADROOM, "").c_str());
+ if (hdrHeadroom >= 1.f) {
+ maxHdrHeadroomOn8bit = std::min(hdrHeadroom, 100.f);
+ }
+
// call isDrawingEnabled to force loading of the property
isDrawingEnabled();
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index ed7175e..24e206b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -218,6 +218,8 @@
#define PROPERTY_MEMORY_POLICY "debug.hwui.app_memory_policy"
+#define PROPERTY_8BIT_HDR_HEADROOM "debug.hwui.8bit_hdr_headroom"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -321,6 +323,8 @@
static bool isLowRam;
static bool isSystemOrPersistent;
+ static float maxHdrHeadroomOn8bit;
+
static StretchEffectBehavior getStretchEffectBehavior() {
return stretchEffectBehavior;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f10b2b2..dd781bb 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -311,7 +311,7 @@
}
switch (mColorMode) {
case ColorMode::Hdr:
- return 3.f; // TODO: Refine this number
+ return Properties::maxHdrHeadroomOn8bit;
case ColorMode::Hdr10:
return 10.f;
default:
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index c398405..c3ad767 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -53,8 +53,6 @@
mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
mLocked.resourcesLoaded = false;
-
- mLocked.buttonState = 0;
}
MouseCursorController::~MouseCursorController() {
@@ -95,22 +93,6 @@
setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
}
-void MouseCursorController::setButtonState(int32_t buttonState) {
-#if DEBUG_MOUSE_CURSOR_UPDATES
- ALOGD("Set button state 0x%08x", buttonState);
-#endif
- std::scoped_lock lock(mLock);
-
- if (mLocked.buttonState != buttonState) {
- mLocked.buttonState = buttonState;
- }
-}
-
-int32_t MouseCursorController::getButtonState() const {
- std::scoped_lock lock(mLock);
- return mLocked.buttonState;
-}
-
void MouseCursorController::setPosition(float x, float y) {
#if DEBUG_MOUSE_CURSOR_UPDATES
ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 26be2a8..00dc085 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -45,8 +45,6 @@
std::optional<FloatRect> getBounds() const;
void move(float deltaX, float deltaY);
- void setButtonState(int32_t buttonState);
- int32_t getButtonState() const;
void setPosition(float x, float y);
FloatPoint getPosition() const;
int32_t getDisplayId() const;
@@ -96,8 +94,6 @@
PointerIconStyle requestedPointerType;
PointerIconStyle resolvedPointerType;
- int32_t buttonState;
-
bool animating{false};
} mLocked GUARDED_BY(mLock);
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 544edc2..88e3519 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -136,14 +136,6 @@
mCursorController.move(transformed.x, transformed.y);
}
-void PointerController::setButtonState(int32_t buttonState) {
- mCursorController.setButtonState(buttonState);
-}
-
-int32_t PointerController::getButtonState() const {
- return mCursorController.getButtonState();
-}
-
void PointerController::setPosition(float x, float y) {
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 6d3557c..ca14b6e 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -50,21 +50,19 @@
~PointerController() override;
- virtual std::optional<FloatRect> getBounds() const;
- virtual void move(float deltaX, float deltaY);
- virtual void setButtonState(int32_t buttonState);
- virtual int32_t getButtonState() const;
- virtual void setPosition(float x, float y);
- virtual FloatPoint getPosition() const;
- virtual int32_t getDisplayId() const;
- virtual void fade(Transition transition);
- virtual void unfade(Transition transition);
- virtual void setDisplayViewport(const DisplayViewport& viewport);
+ std::optional<FloatRect> getBounds() const override;
+ void move(float deltaX, float deltaY) override;
+ void setPosition(float x, float y) override;
+ FloatPoint getPosition() const override;
+ int32_t getDisplayId() const override;
+ void fade(Transition transition) override;
+ void unfade(Transition transition) override;
+ void setDisplayViewport(const DisplayViewport& viewport) override;
- virtual void setPresentation(Presentation presentation);
- virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId);
- virtual void clearSpots();
+ void setPresentation(Presentation presentation) override;
+ void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+ BitSet32 spotIdBits, int32_t displayId) override;
+ void clearSpots() override;
void updatePointerIcon(PointerIconStyle iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 7946baee..f939f52 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -280,7 +280,7 @@
}
case DO_NOTIFY_TV_MESSAGE: {
SomeArgs args = (SomeArgs) msg.obj;
- mTvInputSessionImpl.onTvMessageReceived((String) args.arg1, (Bundle) args.arg2);
+ mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2);
break;
}
default: {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 4e380c4..0d283fa 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1512,7 +1512,7 @@
* See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
* how to parse this data.
*/
- public void onTvMessage(@NonNull @TvInputManager.TvMessageType String type,
+ public void onTvMessage(@TvInputManager.TvMessageType int type,
@NonNull Bundle data) {
}
@@ -2065,7 +2065,7 @@
onAdBufferReady(buffer);
}
- void onTvMessageReceived(String type, Bundle data) {
+ void onTvMessageReceived(int type, Bundle data) {
onTvMessage(type, data);
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 7f4c03b..6f67d68 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -617,8 +617,6 @@
void FilterClientCallbackImpl::getSectionEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
const DemuxFilterSectionEvent §ionEvent = event.get<DemuxFilterEvent::Tag::section>();
jint tableId = sectionEvent.tableId;
@@ -626,23 +624,15 @@
jint sectionNum = sectionEvent.sectionNum;
jlong dataLength = sectionEvent.dataLength;
- jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
+ jobject obj = env->NewObject(mSectionEventClass, mSectionEventInitID, tableId, version,
+ sectionNum, dataLength);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
- jmethodID eventInit = env->GetMethodID(
- eventClazz,
- "<init>",
- "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
- "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;"
- "Ljava/util/List;)V");
- jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
jobject audioDescriptor = nullptr;
@@ -650,8 +640,6 @@
jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
switch (mediaEvent.extraMetaData.getTag()) {
case DemuxFilterMediaEventExtraMetaData::Tag::audio: {
- jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
- jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
const AudioExtraMetaData &ad =
mediaEvent.extraMetaData.get<DemuxFilterMediaEventExtraMetaData::Tag::audio>();
@@ -662,9 +650,9 @@
jbyte adGainFront = ad.adGainFront;
jbyte adGainSurround = ad.adGainSurround;
- audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
- adGainCenter, adGainFront, adGainSurround);
- env->DeleteLocalRef(adClazz);
+ audioDescriptor = env->NewObject(mAudioDescriptorClass, mAudioDescriptorInitID, adFade,
+ adPan, versionTextTag, adGainCenter, adGainFront,
+ adGainSurround);
break;
}
case DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations: {
@@ -705,10 +693,10 @@
sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
}
- jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
- dts, dataLength, offset, nullptr, isSecureMemory, avDataId,
- mpuSequenceNumber, isPesPrivateData, sc, audioDescriptor,
- presentationsJObj);
+ jobject obj = env->NewObject(mMediaEventClass, mMediaEventInitID, streamId, isPtsPresent, pts,
+ isDtsPresent, dts, dataLength, offset, nullptr, isSecureMemory,
+ avDataId, mpuSequenceNumber, isPesPrivateData, sc,
+ audioDescriptor, presentationsJObj);
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
@@ -717,7 +705,7 @@
new MediaEvent(mFilterClient, dupFromAidl(mediaEvent.avMemory),
mediaEvent.avDataId, dataLength + offset, obj);
mediaEventSp->mAvHandleRefCnt++;
- env->SetLongField(obj, eventContext, (jlong)mediaEventSp.get());
+ env->SetLongField(obj, mMediaEventFieldContextID, (jlong)mediaEventSp.get());
mediaEventSp->incStrong(obj);
}
@@ -726,32 +714,27 @@
env->DeleteLocalRef(audioDescriptor);
}
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
env->DeleteLocalRef(presentationsJObj);
}
void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(III)V");
const DemuxFilterPesEvent &pesEvent = event.get<DemuxFilterEvent::Tag::pes>();
jint streamId = pesEvent.streamId;
jint dataLength = pesEvent.dataLength;
jint mpuSequenceNumber = pesEvent.mpuSequenceNumber;
- jobject obj = env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber);
+ jobject obj = env->NewObject(mPesEventClass, mPesEventInitID, streamId, dataLength,
+ mpuSequenceNumber);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getTsRecordEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJI)V");
const DemuxFilterTsRecordEvent &tsRecordEvent = event.get<DemuxFilterEvent::Tag::tsRecord>();
DemuxPid pid = tsRecordEvent.pid;
@@ -781,18 +764,15 @@
jlong pts = tsRecordEvent.pts;
jint firstMbInSlice = tsRecordEvent.firstMbInSlice;
- jobject obj =
- env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, pts, firstMbInSlice);
+ jobject obj = env->NewObject(mTsRecordEventClass, mTsRecordEventInitID, jpid, ts, sc,
+ byteNumber, pts, firstMbInSlice);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getMmtpRecordEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJII)V");
const DemuxFilterMmtpRecordEvent &mmtpRecordEvent =
event.get<DemuxFilterEvent::Tag::mmtpRecord>();
@@ -803,18 +783,15 @@
jint firstMbInSlice = mmtpRecordEvent.firstMbInSlice;
jlong tsIndexMask = mmtpRecordEvent.tsIndexMask;
- jobject obj = env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
- mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask);
+ jobject obj = env->NewObject(mMmtpRecordEventClass, mMmtpRecordEventInitID, scHevcIndexMask,
+ byteNumber, mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIIII)V");
const DemuxFilterDownloadEvent &downloadEvent = event.get<DemuxFilterEvent::Tag::download>();
jint itemId = downloadEvent.itemId;
@@ -824,32 +801,27 @@
jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex;
jint dataLength = downloadEvent.dataLength;
- jobject obj = env->NewObject(eventClazz, eventInit, itemId, downloadId, mpuSequenceNumber,
- itemFragmentIndex, lastItemFragmentIndex, dataLength);
+ jobject obj = env->NewObject(mDownloadEventClass, mDownloadEventInitID, itemId, downloadId,
+ mpuSequenceNumber, itemFragmentIndex, lastItemFragmentIndex,
+ dataLength);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getIpPayloadEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
const DemuxFilterIpPayloadEvent &ipPayloadEvent = event.get<DemuxFilterEvent::Tag::ipPayload>();
jint dataLength = ipPayloadEvent.dataLength;
- jobject obj = env->NewObject(eventClazz, eventInit, dataLength);
+ jobject obj = env->NewObject(mIpPayloadEventClass, mIpPayloadEventInitID, dataLength);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getTemiEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(JB[B)V");
const DemuxFilterTemiEvent &temiEvent = event.get<DemuxFilterEvent::Tag::temi>();
jlong pts = temiEvent.pts;
@@ -859,63 +831,53 @@
jbyteArray array = env->NewByteArray(descrData.size());
env->SetByteArrayRegion(array, 0, descrData.size(), reinterpret_cast<jbyte *>(&descrData[0]));
- jobject obj = env->NewObject(eventClazz, eventInit, pts, descrTag, array);
+ jobject obj = env->NewObject(mTemiEventClass, mTemiEventInitID, pts, descrTag, array);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(array);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getScramblingStatusEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
const DemuxFilterMonitorEvent &scramblingStatus =
event.get<DemuxFilterEvent::Tag::monitorEvent>()
.get<DemuxFilterMonitorEvent::Tag::scramblingStatus>();
- jobject obj = env->NewObject(eventClazz, eventInit, scramblingStatus);
+ jobject obj = env->NewObject(mScramblingStatusEventClass, mScramblingStatusEventInitID,
+ scramblingStatus);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getIpCidChangeEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
const DemuxFilterMonitorEvent &cid = event.get<DemuxFilterEvent::Tag::monitorEvent>()
.get<DemuxFilterMonitorEvent::Tag::cid>();
- jobject obj = env->NewObject(eventClazz, eventInit, cid);
+ jobject obj = env->NewObject(mIpCidChangeEventClass, mIpCidChangeEventInitID, cid);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getRestartEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
const int32_t &startId = event.get<DemuxFilterEvent::Tag::startId>();
- jobject obj = env->NewObject(eventClazz, eventInit, startId);
+ jobject obj = env->NewObject(mRestartEventClass, mRestartEventInitID, startId);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::onFilterEvent(const vector<DemuxFilterEvent> &events) {
ALOGV("FilterClientCallbackImpl::onFilterEvent");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
jobjectArray array;
if (!events.empty()) {
- array = env->NewObjectArray(events.size(), eventClazz, nullptr);
+ array = env->NewObjectArray(events.size(), mEventClass, nullptr);
}
for (int i = 0, arraySize = 0; i < events.size(); i++) {
@@ -1004,7 +966,6 @@
"Filter object has been freed. Ignoring callback.");
}
env->DeleteLocalRef(array);
- env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
@@ -1040,6 +1001,67 @@
mSharedFilter = true;
}
+FilterClientCallbackImpl::FilterClientCallbackImpl() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ ScopedLocalRef eventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/FilterEvent"));
+ ScopedLocalRef sectionEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/SectionEvent"));
+ ScopedLocalRef mediaEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/MediaEvent"));
+ ScopedLocalRef audioDescriptorClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/AudioDescriptor"));
+ ScopedLocalRef pesEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/PesEvent"));
+ ScopedLocalRef tsRecordEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/TsRecordEvent"));
+ ScopedLocalRef mmtpRecordEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent"));
+ ScopedLocalRef downloadEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/DownloadEvent"));
+ ScopedLocalRef ipPayloadEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent"));
+ ScopedLocalRef temiEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/TemiEvent"));
+ ScopedLocalRef scramblingStatusEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent"));
+ ScopedLocalRef ipCidChangeEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent"));
+ ScopedLocalRef restartEventClass =
+ ScopedLocalRef(env, env->FindClass("android/media/tv/tuner/filter/RestartEvent"));
+ mEventClass = (jclass) env->NewGlobalRef(eventClass.get());
+ mSectionEventClass = (jclass) env->NewGlobalRef(sectionEventClass.get());
+ mMediaEventClass = (jclass) env->NewGlobalRef(mediaEventClass.get());
+ mAudioDescriptorClass = (jclass) env->NewGlobalRef(audioDescriptorClass.get());
+ mPesEventClass = (jclass) env->NewGlobalRef(pesEventClass.get());
+ mTsRecordEventClass = (jclass) env->NewGlobalRef(tsRecordEventClass.get());
+ mMmtpRecordEventClass = (jclass) env->NewGlobalRef(mmtpRecordEventClass.get());
+ mDownloadEventClass = (jclass) env->NewGlobalRef(downloadEventClass.get());
+ mIpPayloadEventClass = (jclass) env->NewGlobalRef(ipPayloadEventClass.get());
+ mTemiEventClass = (jclass) env->NewGlobalRef(temiEventClass.get());
+ mScramblingStatusEventClass = (jclass) env->NewGlobalRef(scramblingStatusEventClass.get());
+ mIpCidChangeEventClass = (jclass) env->NewGlobalRef(ipCidChangeEventClass.get());
+ mRestartEventClass = (jclass) env->NewGlobalRef(restartEventClass.get());
+ mSectionEventInitID = env->GetMethodID(mSectionEventClass, "<init>", "(IIIJ)V");
+ mMediaEventInitID = env->GetMethodID(
+ mMediaEventClass,
+ "<init>",
+ "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
+ "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;"
+ "Ljava/util/List;)V");
+ mAudioDescriptorInitID = env->GetMethodID(mAudioDescriptorClass, "<init>", "(BBCBBB)V");
+ mPesEventInitID = env->GetMethodID(mPesEventClass, "<init>", "(III)V");
+ mTsRecordEventInitID = env->GetMethodID(mTsRecordEventClass, "<init>", "(IIIJJI)V");
+ mMmtpRecordEventInitID = env->GetMethodID(mMmtpRecordEventClass, "<init>", "(IJIJII)V");
+ mDownloadEventInitID = env->GetMethodID(mDownloadEventClass, "<init>", "(IIIIII)V");
+ mIpPayloadEventInitID = env->GetMethodID(mIpPayloadEventClass, "<init>", "(I)V");
+ mTemiEventInitID = env->GetMethodID(mTemiEventClass, "<init>", "(JB[B)V");
+ mScramblingStatusEventInitID = env->GetMethodID(mScramblingStatusEventClass, "<init>", "(I)V");
+ mIpCidChangeEventInitID = env->GetMethodID(mIpCidChangeEventClass, "<init>", "(I)V");
+ mRestartEventInitID = env->GetMethodID(mRestartEventClass, "<init>", "(I)V");
+ mMediaEventFieldContextID = env->GetFieldID(mMediaEventClass, "mNativeContext", "J");
+}
+
FilterClientCallbackImpl::~FilterClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (mFilterObj != nullptr) {
@@ -1047,6 +1069,19 @@
mFilterObj = nullptr;
}
mFilterClient = nullptr;
+ env->DeleteGlobalRef(mEventClass);
+ env->DeleteGlobalRef(mSectionEventClass);
+ env->DeleteGlobalRef(mMediaEventClass);
+ env->DeleteGlobalRef(mAudioDescriptorClass);
+ env->DeleteGlobalRef(mPesEventClass);
+ env->DeleteGlobalRef(mTsRecordEventClass);
+ env->DeleteGlobalRef(mMmtpRecordEventClass);
+ env->DeleteGlobalRef(mDownloadEventClass);
+ env->DeleteGlobalRef(mIpPayloadEventClass);
+ env->DeleteGlobalRef(mTemiEventClass);
+ env->DeleteGlobalRef(mScramblingStatusEventClass);
+ env->DeleteGlobalRef(mIpCidChangeEventClass);
+ env->DeleteGlobalRef(mRestartEventClass);
}
/////////////// FrontendClientCallbackImpl ///////////////////////
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 2bb14f6..6b1b6b1 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -125,6 +125,7 @@
};
struct FilterClientCallbackImpl : public FilterClientCallback {
+ FilterClientCallbackImpl();
~FilterClientCallbackImpl();
virtual void onFilterEvent(const vector<DemuxFilterEvent>& events);
virtual void onFilterStatus(const DemuxFilterStatus status);
@@ -135,6 +136,32 @@
private:
jweak mFilterObj;
sp<FilterClient> mFilterClient;
+ jclass mEventClass;
+ jclass mSectionEventClass;
+ jclass mMediaEventClass;
+ jclass mAudioDescriptorClass;
+ jclass mPesEventClass;
+ jclass mTsRecordEventClass;
+ jclass mMmtpRecordEventClass;
+ jclass mDownloadEventClass;
+ jclass mIpPayloadEventClass;
+ jclass mTemiEventClass;
+ jclass mScramblingStatusEventClass;
+ jclass mIpCidChangeEventClass;
+ jclass mRestartEventClass;
+ jmethodID mSectionEventInitID;
+ jmethodID mMediaEventInitID;
+ jmethodID mAudioDescriptorInitID;
+ jmethodID mPesEventInitID;
+ jmethodID mTsRecordEventInitID;
+ jmethodID mMmtpRecordEventInitID;
+ jmethodID mDownloadEventInitID;
+ jmethodID mIpPayloadEventInitID;
+ jmethodID mTemiEventInitID;
+ jmethodID mScramblingStatusEventInitID;
+ jmethodID mIpCidChangeEventInitID;
+ jmethodID mRestartEventInitID;
+ jfieldID mMediaEventFieldContextID;
bool mSharedFilter;
void getSectionEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
void getMediaEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index dd60763..5632458 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -250,7 +250,7 @@
leftButton = if (totalEntriesCount > 1) {
{
ActionButton(
- stringResource(R.string.get_dialog_use_saved_passkey_for),
+ stringResource(R.string.get_dialog_title_sign_in_options),
onMoreOptionSelected
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
index ca88f8d..215f6b9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.spa.framework.common
-import android.app.settings.SettingsEnums
import android.os.Bundle
// Defines the category of the log, for quick filter
@@ -32,14 +31,14 @@
}
// Defines the log events in Spa.
-enum class LogEvent(val action: Int) {
+enum class LogEvent {
// Page related events.
- PAGE_ENTER(SettingsEnums.PAGE_VISIBLE),
- PAGE_LEAVE(SettingsEnums.PAGE_HIDE),
+ PAGE_ENTER,
+ PAGE_LEAVE,
// Entry related events.
- ENTRY_CLICK(SettingsEnums.ACTION_SETTINGS_TILE_CLICK),
- ENTRY_SWITCH(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
+ ENTRY_CLICK,
+ ENTRY_SWITCH,
}
internal const val LOG_DATA_DISPLAY_NAME = "name"
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index a4c59ea..a8eeec3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -31,7 +31,7 @@
/**
* This class tracks changes for config/global/secure/system tables
- * on a per user basis and updates shared memory regions which
+ * on a per-user basis and updates shared memory regions which
* client processes can read to determine if their local caches are
* stale.
*/
@@ -196,7 +196,9 @@
if (backingStore == null) {
try {
if (mNumBackingStore >= NUM_MAX_BACKING_STORE) {
- Slog.e(LOG_TAG, "Error creating backing store - at capacity");
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error creating backing store - at capacity");
+ }
return null;
}
backingStore = new MemoryIntArray(MAX_BACKING_STORE_SIZE);
@@ -256,7 +258,9 @@
+ " on user:" + SettingsState.getUserIdFromKey(key));
}
} else {
- Slog.e(LOG_TAG, "Could not allocate generation index");
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Could not allocate generation index");
+ }
}
}
return index;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1136c11..4290ca0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -924,7 +924,7 @@
android:showForAllUsers="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
+ android:configChanges="screenLayout|keyboard|keyboardHidden|orientation"
android:visibleToInstantApps="true">
</activity>
@@ -946,7 +946,7 @@
android:showWhenLocked="true"
android:showForAllUsers="true"
android:finishOnTaskLaunch="true"
- android:lockTaskMode="if_whitelisted"
+ android:lockTaskMode="always"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
</activity>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
index 440c6e5..ca84265 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
@@ -58,8 +58,5 @@
<intent>
<action android:name="android.intent.action.VOICE_COMMAND" />
</intent>
- <!--intent>
- <action android:name="android.settings.ACCESSIBILITY_SETTINGS" />
- </intent-->
</queries>
</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 00c0a0b..e73afe7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -29,14 +29,15 @@
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
+import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.Assert
+import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-private val TAG = ClockRegistry::class.simpleName!!
private const val DEBUG = true
private val KEY_TIMESTAMP = "appliedTimestamp"
@@ -51,7 +52,10 @@
val handleAllUsers: Boolean,
defaultClockProvider: ClockProvider,
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
+ val keepAllLoaded: Boolean,
+ val subTag: String,
) {
+ private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
interface ClockChangeListener {
// Called when the active clock changes
fun onCurrentClockChanged() {}
@@ -76,11 +80,85 @@
private val pluginListener =
object : PluginListener<ClockProviderPlugin> {
- override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) =
- connectClocks(plugin)
+ override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) {
+ manager.loadPlugin()
+ }
- override fun onPluginDisconnected(plugin: ClockProviderPlugin) =
- disconnectClocks(plugin)
+ override fun onPluginLoaded(
+ plugin: ClockProviderPlugin,
+ pluginContext: Context,
+ manager: PluginLifecycleManager<ClockProviderPlugin>
+ ) {
+ var isClockListChanged = false
+ for (clock in plugin.getClocks()) {
+ val id = clock.clockId
+ var isNew = false
+ val info =
+ availableClocks.getOrPut(id) {
+ isNew = true
+ ClockInfo(clock, plugin, manager)
+ }
+
+ if (isNew) {
+ isClockListChanged = true
+ onConnected(id)
+ }
+
+ if (manager != info.manager) {
+ Log.e(
+ TAG,
+ "Clock Id conflict on load: $id is registered to another provider"
+ )
+ continue
+ }
+
+ info.provider = plugin
+ onLoaded(id)
+ }
+
+ if (isClockListChanged) {
+ triggerOnAvailableClocksChanged()
+ }
+ verifyLoadedProviders()
+ }
+
+ override fun onPluginUnloaded(
+ plugin: ClockProviderPlugin,
+ manager: PluginLifecycleManager<ClockProviderPlugin>
+ ) {
+ for (clock in plugin.getClocks()) {
+ val id = clock.clockId
+ val info = availableClocks[id]
+ if (info?.manager != manager) {
+ Log.e(
+ TAG,
+ "Clock Id conflict on unload: $id is registered to another provider"
+ )
+ continue
+ }
+ info.provider = null
+ onUnloaded(id)
+ }
+
+ verifyLoadedProviders()
+ }
+
+ override fun onPluginDetached(manager: PluginLifecycleManager<ClockProviderPlugin>) {
+ val removed = mutableListOf<ClockId>()
+ availableClocks.entries.removeAll {
+ if (it.value.manager != manager) {
+ return@removeAll false
+ }
+
+ removed.add(it.key)
+ return@removeAll true
+ }
+
+ removed.forEach(::onDisconnected)
+ if (removed.size > 0) {
+ triggerOnAvailableClocksChanged()
+ }
+ }
}
private val userSwitchObserver =
@@ -96,7 +174,8 @@
protected set(value) {
if (field != value) {
field = value
- scope.launch(mainDispatcher) { onClockChanged { it.onCurrentClockChanged() } }
+ verifyLoadedProviders()
+ triggerOnCurrentClockChanged()
}
}
@@ -168,9 +247,36 @@
Assert.isNotMainThread()
}
- private fun onClockChanged(func: (ClockChangeListener) -> Unit) {
- assertMainThread()
- clockChangeListeners.forEach(func)
+ private var isClockChanged = AtomicBoolean(false)
+ private fun triggerOnCurrentClockChanged() {
+ val shouldSchedule = isClockChanged.compareAndSet(false, true)
+ if (!shouldSchedule) {
+ return
+ }
+
+ android.util.Log.e("HAWK", "triggerOnCurrentClockChanged")
+ scope.launch(mainDispatcher) {
+ assertMainThread()
+ android.util.Log.e("HAWK", "isClockChanged")
+ isClockChanged.set(false)
+ clockChangeListeners.forEach { it.onCurrentClockChanged() }
+ }
+ }
+
+ private var isClockListChanged = AtomicBoolean(false)
+ private fun triggerOnAvailableClocksChanged() {
+ val shouldSchedule = isClockListChanged.compareAndSet(false, true)
+ if (!shouldSchedule) {
+ return
+ }
+
+ android.util.Log.e("HAWK", "triggerOnAvailableClocksChanged")
+ scope.launch(mainDispatcher) {
+ assertMainThread()
+ android.util.Log.e("HAWK", "isClockListChanged")
+ isClockListChanged.set(false)
+ clockChangeListeners.forEach { it.onAvailableClocksChanged() }
+ }
}
public fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
@@ -190,7 +296,12 @@
}
init {
- connectClocks(defaultClockProvider)
+ // Register default clock designs
+ for (clock in defaultClockProvider.getClocks()) {
+ availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null)
+ }
+
+ // Something has gone terribly wrong if the default clock isn't present
if (!availableClocks.containsKey(DEFAULT_CLOCK_ID)) {
throw IllegalArgumentException(
"$defaultClockProvider did not register clock at $DEFAULT_CLOCK_ID"
@@ -244,59 +355,87 @@
}
}
- private fun connectClocks(provider: ClockProvider) {
- var isAvailableChanged = false
- val currentId = currentClockId
- for (clock in provider.getClocks()) {
- val id = clock.clockId
- val current = availableClocks[id]
- if (current != null) {
- Log.e(
- TAG,
- "Clock Id conflict: $id is registered by both " +
- "${provider::class.simpleName} and ${current.provider::class.simpleName}"
- )
- continue
- }
-
- availableClocks[id] = ClockInfo(clock, provider)
- isAvailableChanged = true
- if (DEBUG) {
- Log.i(TAG, "Added ${clock.clockId}")
- }
-
- if (currentId == id) {
- if (DEBUG) {
- Log.i(TAG, "Current clock ($currentId) was connected")
- }
- onClockChanged { it.onCurrentClockChanged() }
- }
+ private var isVerifying = AtomicBoolean(false)
+ private fun verifyLoadedProviders() {
+ val shouldSchedule = isVerifying.compareAndSet(false, true)
+ if (!shouldSchedule) {
+ return
}
- if (isAvailableChanged) {
- onClockChanged { it.onAvailableClocksChanged() }
+ scope.launch(bgDispatcher) {
+ if (keepAllLoaded) {
+ // Enforce that all plugins are loaded if requested
+ for ((_, info) in availableClocks) {
+ info.manager?.loadPlugin()
+ }
+ isVerifying.set(false)
+ return@launch
+ }
+
+ val currentClock = availableClocks[currentClockId]
+ if (currentClock == null) {
+ // Current Clock missing, load no plugins and use default
+ for ((_, info) in availableClocks) {
+ info.manager?.unloadPlugin()
+ }
+ isVerifying.set(false)
+ return@launch
+ }
+
+ val currentManager = currentClock.manager
+ currentManager?.loadPlugin()
+
+ for ((_, info) in availableClocks) {
+ val manager = info.manager
+ if (manager != null && manager.isLoaded && currentManager != manager) {
+ manager.unloadPlugin()
+ }
+ }
+ isVerifying.set(false)
}
}
- private fun disconnectClocks(provider: ClockProvider) {
- var isAvailableChanged = false
- val currentId = currentClockId
- for (clock in provider.getClocks()) {
- availableClocks.remove(clock.clockId)
- isAvailableChanged = true
-
- if (DEBUG) {
- Log.i(TAG, "Removed ${clock.clockId}")
- }
-
- if (currentId == clock.clockId) {
- Log.w(TAG, "Current clock ($currentId) was disconnected")
- onClockChanged { it.onCurrentClockChanged() }
- }
+ private fun onConnected(clockId: ClockId) {
+ if (DEBUG) {
+ Log.i(TAG, "Connected $clockId")
}
- if (isAvailableChanged) {
- onClockChanged { it.onAvailableClocksChanged() }
+ if (currentClockId == clockId) {
+ if (DEBUG) {
+ Log.i(TAG, "Current clock ($clockId) was connected")
+ }
+ }
+ }
+
+ private fun onLoaded(clockId: ClockId) {
+ if (DEBUG) {
+ Log.i(TAG, "Loaded $clockId")
+ }
+
+ if (currentClockId == clockId) {
+ Log.i(TAG, "Current clock ($clockId) was loaded")
+ triggerOnCurrentClockChanged()
+ }
+ }
+
+ private fun onUnloaded(clockId: ClockId) {
+ if (DEBUG) {
+ Log.i(TAG, "Unloaded $clockId")
+ }
+
+ if (currentClockId == clockId) {
+ Log.w(TAG, "Current clock ($clockId) was unloaded")
+ triggerOnCurrentClockChanged()
+ }
+ }
+
+ private fun onDisconnected(clockId: ClockId) {
+ if (DEBUG) {
+ Log.i(TAG, "Disconnected $clockId")
+ }
+
+ if (currentClockId == clockId) {
+ Log.w(TAG, "Current clock ($clockId) was disconnected")
}
}
@@ -345,6 +484,7 @@
private data class ClockInfo(
val metadata: ClockMetadata,
- val provider: ClockProvider,
+ var provider: ClockProvider?,
+ val manager: PluginLifecycleManager<ClockProviderPlugin>?,
)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index 70b5d73..b7088d5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -50,6 +50,13 @@
boolean isPulsing();
/**
+ * Is device dreaming. This method is more inclusive than
+ * {@link android.service.dreams.IDreamManager.isDreaming}, as it will return true during the
+ * dream's wake-up phase.
+ */
+ boolean isDreaming();
+
+ /**
* Adds a state listener
*/
void addCallback(StateListener listener);
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
new file mode 100644
index 0000000..cc6a46f
--- /dev/null
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+/**
+ * Provides the ability for consumers to control plugin lifecycle.
+ *
+ * @param <T> is the target plugin type
+ */
+public interface PluginLifecycleManager<T extends Plugin> {
+ /** Returns the currently loaded plugin instance (if plugin is loaded) */
+ T getPlugin();
+
+ /** returns true if the plugin is currently loaded */
+ default boolean isLoaded() {
+ return getPlugin() != null;
+ }
+
+ /**
+ * Loads and creates the plugin instance if it does not exist.
+ *
+ * This will trigger {@link PluginListener#onPluginLoaded} with the new instance if it did not
+ * already exist.
+ */
+ void loadPlugin();
+
+ /**
+ * Unloads and destroys the plugin instance if it exists.
+ *
+ * This will trigger {@link PluginListener#onPluginUnloaded} if a concrete plugin instance
+ * existed when this call was made.
+ */
+ void unloadPlugin();
+}
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java
index b488d2a..c5f5032 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java
@@ -17,7 +17,32 @@
import android.content.Context;
/**
- * Interface for listening to plugins being connected.
+ * Interface for listening to plugins being connected and disconnected.
+ *
+ * The call order for a plugin is
+ * 1) {@link #onPluginAttached}
+ * Called when a new plugin is added to the device, or an existing plugin was replaced by
+ * the package manager. Will only be called once per package manager event. If multiple
+ * non-conflicting packages which have the same plugin interface are installed on the
+ * device, then this method can be called multiple times with different instances of
+ * {@link PluginLifecycleManager} (as long as `allowMultiple` was set to true when the
+ * listener was registered with {@link PluginManager#addPluginListener}).
+ * 2) {@link #onPluginLoaded}
+ * Called whenever a new instance of the plugin object is created and ready for use. Can be
+ * called multiple times per {@link PluginLifecycleManager}, but will always pass a newly
+ * created plugin object. {@link #onPluginUnloaded} with the previous plugin object will
+ * be called before another call to {@link #onPluginLoaded} is made. This method will be
+ * called once automatically after {@link #onPluginAttached}. Besides the initial call,
+ * {@link #onPluginLoaded} will occur due to {@link PluginLifecycleManager#loadPlugin}.
+ * 3) {@link #onPluginUnloaded}
+ * Called when a request to unload the plugin has been received. This can be triggered from
+ * a related call to {@link PluginLifecycleManager#unloadPlugin} or for any reason that
+ * {@link #onPluginDetached} would be triggered.
+ * 4) {@link #onPluginDetached}
+ * Called when the package is removed from the device, disabled, or replaced due to an
+ * external trigger. These are events from the android package manager.
+ *
+ * @param <T> is the target plugin type
*/
public interface PluginListener<T extends Plugin> {
/**
@@ -25,14 +50,69 @@
* This may be called multiple times if multiple plugins are allowed.
* It may also be called in the future if the plugin package changes
* and needs to be reloaded.
+ *
+ * @deprecated Migrate to {@link #onPluginLoaded} or {@link #onPluginAttached}
*/
- void onPluginConnected(T plugin, Context pluginContext);
+ @Deprecated
+ default void onPluginConnected(T plugin, Context pluginContext) {
+ // Optional
+ }
+
+ /**
+ * Called when the plugin is first attached to the host application. {@link #onPluginLoaded}
+ * will be automatically called as well when first attached. This may be called multiple times
+ * if multiple plugins are allowed. It may also be called in the future if the plugin package
+ * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new
+ * or different {@link PluginLifecycleManager}.
+ */
+ default void onPluginAttached(PluginLifecycleManager<T> manager) {
+ // Optional
+ }
/**
* Called when a plugin has been uninstalled/updated and should be removed
* from use.
+ *
+ * @deprecated Migrate to {@link #onPluginDetached} or {@link #onPluginUnloaded}
*/
+ @Deprecated
default void onPluginDisconnected(T plugin) {
// Optional.
}
-}
+
+ /**
+ * Called when the plugin has been detached from the host application. Implementers should no
+ * longer attempt to reload it via this {@link PluginLifecycleManager}. If the package was
+ * updated and not removed, then {@link #onPluginAttached} will be called again when the updated
+ * package is available.
+ */
+ default void onPluginDetached(PluginLifecycleManager<T> manager) {
+ // Optional.
+ }
+
+ /**
+ * Called when the plugin is loaded into the host's process and is available for use. This can
+ * happen several times if clients are using {@link PluginLifecycleManager} to manipulate a
+ * plugin's load state. Each call to {@link #onPluginLoaded} will have a matched call to
+ * {@link #onPluginUnloaded} when that plugin object should no longer be used.
+ */
+ default void onPluginLoaded(
+ T plugin,
+ Context pluginContext,
+ PluginLifecycleManager<T> manager
+ ) {
+ // Optional, default to deprecated version
+ onPluginConnected(plugin, pluginContext);
+ }
+
+ /**
+ * Called when the plugin should no longer be used. Listeners should clean up all references to
+ * the relevant plugin so that it can be garbage collected. If the plugin object is required in
+ * the future a call can be made to {@link PluginLifecycleManager#loadPlugin} to create a new
+ * plugin object and trigger {@link #onPluginLoaded}.
+ */
+ default void onPluginUnloaded(T plugin, PluginLifecycleManager<T> manager) {
+ // Optional, default to deprecated version
+ onPluginDisconnected(plugin);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 9d91419..85b6e8d 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -97,7 +97,8 @@
android:background="@drawable/qs_media_light_source"
android:forceHasOverlappingRendering="false"
android:layout_width="wrap_content"
- android:layout_height="@dimen/min_clickable_item_size"
+ android:minHeight="@dimen/min_clickable_item_size"
+ android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/res/layout/multi_shade.xml b/packages/SystemUI/res/layout/multi_shade.xml
new file mode 100644
index 0000000..78ff5f0
--- /dev/null
+++ b/packages/SystemUI/res/layout/multi_shade.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ ~
+ -->
+
+<com.android.systemui.multishade.ui.view.MultiShadeView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 4abc176..fe9542b 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -110,6 +110,13 @@
android:clipChildren="false"
android:clipToPadding="false" />
+ <ViewStub
+ android:id="@+id/multi_shade_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:inflatedId="@+id/multi_shade"
+ android:layout="@layout/multi_shade" />
+
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 6354752..763930d 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -30,9 +30,6 @@
<bool name="flag_charging_ripple">false</bool>
- <!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. -->
- <bool name="flag_active_unlock_chipbar">true</bool>
-
<!-- Whether the user switcher chip shows in the status bar. When true, the multi user
avatar will no longer show on the lockscreen -->
<bool name="flag_user_switcher_chip">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a02c429..ebf0f8e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2431,7 +2431,7 @@
<!-- Shows in a dialog presented to the user to authorize this app to display a Device controls
panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] -->
- <string name="controls_panel_authorization"><xliff:g id="appName" example="My app">%s</xliff:g>can choose which controls and content show here.</string>
+ <string name="controls_panel_authorization"><xliff:g id="appName" example="My app">%s</xliff:g> can choose which controls and content show here.</string>
<!-- Shows in a dialog presented to the user to authorize this app removal from a Device
controls panel [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
index 9766514..dedf0a7 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
@@ -53,6 +53,7 @@
fun View.captureToBitmap(window: Window? = null): ListenableFuture<Bitmap> {
val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create()
val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper()))
+ val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
// disable drawing again if necessary once work is complete
if (!HardwareRendererCompat.isDrawingEnabled()) {
@@ -61,8 +62,12 @@
}
mainExecutor.execute {
- val forceRedrawFuture = forceRedraw()
- forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor)
+ if (isRobolectric) {
+ generateBitmap(bitmapFuture)
+ } else {
+ val forceRedrawFuture = forceRedraw()
+ forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor)
+ }
}
return bitmapFuture
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
index 2d47356..f96d1e3 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
@@ -19,6 +19,7 @@
import android.app.Activity
import android.app.Dialog
import android.graphics.Bitmap
+import android.os.Build
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
@@ -26,6 +27,7 @@
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.activity.ComponentActivity
import androidx.test.ext.junit.rules.ActivityScenarioRule
+import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -54,14 +56,14 @@
)
)
private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java)
- private val delegateRule =
- RuleChain.outerRule(colorsRule)
- .around(deviceEmulationRule)
- .around(screenshotRule)
- .around(activityRule)
+ private val roboRule =
+ RuleChain.outerRule(deviceEmulationRule).around(screenshotRule).around(activityRule)
+ private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule)
+ private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
override fun apply(base: Statement, description: Description): Statement {
- return delegateRule.apply(base, description)
+ val ruleToApply = if (isRobolectric) roboRule else delegateRule
+ return ruleToApply.apply(base, description)
}
protected fun takeScreenshot(
@@ -94,7 +96,12 @@
contentView = content.getChildAt(0)
}
- return contentView?.toBitmap() ?: error("contentView is null")
+ return if (isRobolectric) {
+ contentView?.captureToBitmap()?.get(10, TimeUnit.SECONDS)
+ ?: error("timeout while trying to capture view to bitmap")
+ } else {
+ contentView?.toBitmap() ?: error("contentView is null")
+ }
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
index cc3d7a8..3d05542 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
@@ -210,12 +210,12 @@
private void onPluginConnected(PluginInstance<T> pluginInstance) {
if (DEBUG) Log.d(TAG, "onPluginConnected");
PluginPrefs.setHasPlugins(mContext);
- pluginInstance.onCreate(mContext, mListener);
+ pluginInstance.onCreate();
}
private void onPluginDisconnected(PluginInstance<T> pluginInstance) {
if (DEBUG) Log.d(TAG, "onPluginDisconnected");
- pluginInstance.onDestroy(mListener);
+ pluginInstance.onDestroy();
}
private void queryAll() {
@@ -312,7 +312,7 @@
try {
return mPluginInstanceFactory.create(
mContext, appInfo, component,
- mPluginClass);
+ mPluginClass, mListener);
} catch (InvalidVersionException e) {
reportInvalidVersion(component, component.getClassName(), e);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 2f84602..016d573 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -21,13 +21,16 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginFragment;
+import com.android.systemui.plugins.PluginLifecycleManager;
import com.android.systemui.plugins.PluginListener;
import dalvik.system.PathClassLoader;
@@ -35,7 +38,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
+import java.util.function.Supplier;
/**
* Contains a single instantiation of a Plugin.
@@ -45,42 +48,102 @@
*
* @param <T> The type of plugin that this contains.
*/
-public class PluginInstance<T extends Plugin> {
+public class PluginInstance<T extends Plugin> implements PluginLifecycleManager {
private static final String TAG = "PluginInstance";
- private static final Map<String, ClassLoader> sClassLoaders = new ArrayMap<>();
- private final Context mPluginContext;
- private final VersionInfo mVersionInfo;
+ private final Context mAppContext;
+ private final PluginListener<T> mListener;
private final ComponentName mComponentName;
- private final T mPlugin;
+ private final PluginFactory<T> mPluginFactory;
+
+ private Context mPluginContext;
+ private T mPlugin;
/** */
- public PluginInstance(ComponentName componentName, T plugin, Context pluginContext,
- VersionInfo versionInfo) {
+ public PluginInstance(
+ Context appContext,
+ PluginListener<T> listener,
+ ComponentName componentName,
+ PluginFactory<T> pluginFactory,
+ @Nullable T plugin) {
+ mAppContext = appContext;
+ mListener = listener;
mComponentName = componentName;
+ mPluginFactory = pluginFactory;
mPlugin = plugin;
- mPluginContext = pluginContext;
- mVersionInfo = versionInfo;
+
+ if (mPlugin != null) {
+ mPluginContext = mPluginFactory.createPluginContext();
+ }
}
/** Alerts listener and plugin that the plugin has been created. */
- public void onCreate(Context appContext, PluginListener<T> listener) {
- if (!(mPlugin instanceof PluginFragment)) {
- // Only call onCreate for plugins that aren't fragments, as fragments
- // will get the onCreate as part of the fragment lifecycle.
- mPlugin.onCreate(appContext, mPluginContext);
+ public void onCreate() {
+ mListener.onPluginAttached(this);
+ if (mPlugin == null) {
+ loadPlugin();
+ } else {
+ if (!(mPlugin instanceof PluginFragment)) {
+ // Only call onCreate for plugins that aren't fragments, as fragments
+ // will get the onCreate as part of the fragment lifecycle.
+ mPlugin.onCreate(mAppContext, mPluginContext);
+ }
+ mListener.onPluginLoaded(mPlugin, mPluginContext, this);
}
- listener.onPluginConnected(mPlugin, mPluginContext);
}
/** Alerts listener and plugin that the plugin is being shutdown. */
- public void onDestroy(PluginListener<T> listener) {
- listener.onPluginDisconnected(mPlugin);
+ public void onDestroy() {
+ unloadPlugin();
+ mListener.onPluginDetached(this);
+ }
+
+ /** Returns the current plugin instance (if it is loaded). */
+ @Nullable
+ public T getPlugin() {
+ return mPlugin;
+ }
+
+ /**
+ * Loads and creates the plugin if it does not exist.
+ */
+ public void loadPlugin() {
+ if (mPlugin != null) {
+ return;
+ }
+
+ mPlugin = mPluginFactory.createPlugin();
+ mPluginContext = mPluginFactory.createPluginContext();
+ if (mPlugin == null || mPluginContext == null) {
+ return;
+ }
+
+ if (!(mPlugin instanceof PluginFragment)) {
+ // Only call onCreate for plugins that aren't fragments, as fragments
+ // will get the onCreate as part of the fragment lifecycle.
+ mPlugin.onCreate(mAppContext, mPluginContext);
+ }
+ mListener.onPluginLoaded(mPlugin, mPluginContext, this);
+ }
+
+ /**
+ * Unloads and destroys the current plugin instance if it exists.
+ *
+ * This will free the associated memory if there are not other references.
+ */
+ public void unloadPlugin() {
+ if (mPlugin == null) {
+ return;
+ }
+
+ mListener.onPluginUnloaded(mPlugin, this);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onDestroy for plugins that aren't fragments, as fragments
// will get the onDestroy as part of the fragment lifecycle.
mPlugin.onDestroy();
}
+ mPlugin = null;
+ mPluginContext = null;
}
/**
@@ -89,7 +152,7 @@
* It does this by string comparison of the class names.
**/
public boolean containsPluginClass(Class pluginClass) {
- return mPlugin.getClass().getName().equals(pluginClass.getName());
+ return mComponentName.getClassName().equals(pluginClass.getName());
}
public ComponentName getComponentName() {
@@ -101,7 +164,7 @@
}
public VersionInfo getVersionInfo() {
- return mVersionInfo;
+ return mPluginFactory.checkVersion(mPlugin);
}
@VisibleForTesting
@@ -134,21 +197,20 @@
Context context,
ApplicationInfo appInfo,
ComponentName componentName,
- Class<T> pluginClass)
+ Class<T> pluginClass,
+ PluginListener<T> listener)
throws PackageManager.NameNotFoundException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
- ClassLoader classLoader = getClassLoader(appInfo, mBaseClassLoader);
- Context pluginContext = new PluginActionManager.PluginContextWrapper(
- context.createApplicationContext(appInfo, 0), classLoader);
- Class<T> instanceClass = (Class<T>) Class.forName(
- componentName.getClassName(), true, classLoader);
+ PluginFactory<T> pluginFactory = new PluginFactory<T>(
+ context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass,
+ () -> getClassLoader(appInfo, mBaseClassLoader));
// TODO: Only create the plugin before version check if we need it for
// legacy version check.
- T instance = (T) mInstanceFactory.create(instanceClass);
- VersionInfo version = mVersionChecker.checkVersion(
- instanceClass, pluginClass, instance);
- return new PluginInstance<T>(componentName, instance, pluginContext, version);
+ T instance = pluginFactory.createPlugin();
+ pluginFactory.checkVersion(instance);
+ return new PluginInstance<T>(
+ context, listener, componentName, pluginFactory, instance);
}
private boolean isPluginPackagePrivileged(String packageName) {
@@ -179,9 +241,6 @@
+ appInfo.sourceDir + ", pkg: " + appInfo.packageName);
return null;
}
- if (sClassLoaders.containsKey(appInfo.packageName)) {
- return sClassLoaders.get(appInfo.packageName);
- }
List<String> zipPaths = new ArrayList<>();
List<String> libPaths = new ArrayList<>();
@@ -190,13 +249,20 @@
TextUtils.join(File.pathSeparator, zipPaths),
TextUtils.join(File.pathSeparator, libPaths),
getParentClassLoader(baseClassLoader));
- sClassLoaders.put(appInfo.packageName, classLoader);
return classLoader;
}
}
/** Class that compares a plugin class against an implementation for version matching. */
- public static class VersionChecker {
+ public interface VersionChecker {
+ /** Compares two plugin classes. */
+ <T extends Plugin> VersionInfo checkVersion(
+ Class<T> instanceClass, Class<T> pluginClass, Plugin plugin);
+ }
+
+ /** Class that compares a plugin class against an implementation for version matching. */
+ public static class VersionCheckerImpl implements VersionChecker {
+ @Override
/** Compares two plugin classes. */
public <T extends Plugin> VersionInfo checkVersion(
Class<T> instanceClass, Class<T> pluginClass, Plugin plugin) {
@@ -204,7 +270,7 @@
VersionInfo instanceVersion = new VersionInfo().addClass(instanceClass);
if (instanceVersion.hasVersionInfo()) {
pluginVersion.checkVersion(instanceVersion);
- } else {
+ } else if (plugin != null) {
int fallbackVersion = plugin.getVersion();
if (fallbackVersion != pluginVersion.getDefaultVersion()) {
throw new VersionInfo.InvalidVersionException("Invalid legacy version", false);
@@ -225,4 +291,74 @@
return (T) cls.newInstance();
}
}
+
+ /**
+ * Instanced wrapper of InstanceFactory
+ *
+ * @param <T> is the type of the plugin object to be built
+ **/
+ public static class PluginFactory<T extends Plugin> {
+ private final Context mContext;
+ private final InstanceFactory<?> mInstanceFactory;
+ private final ApplicationInfo mAppInfo;
+ private final ComponentName mComponentName;
+ private final VersionChecker mVersionChecker;
+ private final Class<T> mPluginClass;
+ private final Supplier<ClassLoader> mClassLoaderFactory;
+
+ public PluginFactory(
+ Context context,
+ InstanceFactory<?> instanceFactory,
+ ApplicationInfo appInfo,
+ ComponentName componentName,
+ VersionChecker versionChecker,
+ Class<T> pluginClass,
+ Supplier<ClassLoader> classLoaderFactory) {
+ mContext = context;
+ mInstanceFactory = instanceFactory;
+ mAppInfo = appInfo;
+ mComponentName = componentName;
+ mVersionChecker = versionChecker;
+ mPluginClass = pluginClass;
+ mClassLoaderFactory = classLoaderFactory;
+ }
+
+ /** Creates the related plugin object from the factory */
+ public T createPlugin() {
+ try {
+ ClassLoader loader = mClassLoaderFactory.get();
+ Class<T> instanceClass = (Class<T>) Class.forName(
+ mComponentName.getClassName(), true, loader);
+ return (T) mInstanceFactory.create(instanceClass);
+ } catch (ClassNotFoundException ex) {
+ Log.e(TAG, "Failed to load plugin", ex);
+ } catch (IllegalAccessException ex) {
+ Log.e(TAG, "Failed to load plugin", ex);
+ } catch (InstantiationException ex) {
+ Log.e(TAG, "Failed to load plugin", ex);
+ }
+ return null;
+ }
+
+ /** Creates a context wrapper for the plugin */
+ public Context createPluginContext() {
+ try {
+ ClassLoader loader = mClassLoaderFactory.get();
+ return new PluginActionManager.PluginContextWrapper(
+ mContext.createApplicationContext(mAppInfo, 0), loader);
+ } catch (NameNotFoundException ex) {
+ Log.e(TAG, "Failed to create plugin context", ex);
+ }
+ return null;
+ }
+
+ /** Check Version and create VersionInfo for instance */
+ public VersionInfo checkVersion(T instance) {
+ if (instance == null) {
+ instance = createPlugin();
+ }
+ return mVersionChecker.checkVersion(
+ (Class<T>) instance.getClass(), mPluginClass, instance);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index b1a83fb..6e98a18 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -60,7 +60,9 @@
featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
/* handleAllUsers= */ true,
new DefaultClockProvider(context, layoutInflater, resources),
- context.getString(R.string.lockscreen_clock_id_fallback));
+ context.getString(R.string.lockscreen_clock_id_fallback),
+ /* keepAllLoaded = */ false,
+ /* subTag = */ "System");
registry.registerListeners();
return registry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index bf0a692..224eb1c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -20,6 +20,8 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
import android.os.Bundle
import android.os.RemoteException
import android.service.dreams.IDreamManager
@@ -57,9 +59,11 @@
private lateinit var parent: ViewGroup
private lateinit var broadcastReceiver: BroadcastReceiver
private var mExitToDream: Boolean = false
+ private lateinit var lastConfiguration: Configuration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ lastConfiguration = resources.configuration
if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
}
@@ -92,6 +96,14 @@
initBroadcastReceiver()
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ if (lastConfiguration.diff(newConfig) and ActivityInfo.CONFIG_ORIENTATION != 0 ) {
+ uiController.onOrientationChange()
+ }
+ lastConfiguration = newConfig
+ }
+
override fun onStart() {
super.onStart()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 0d53117..3ecf423 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -64,6 +64,8 @@
* This element will be the one that appears when the user first opens the controls activity.
*/
fun getPreferredSelectedItem(structures: List<StructureInfo>): SelectedItem
+
+ fun onOrientationChange()
}
sealed class SelectedItem {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 5da86de9..ee12db8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -124,6 +124,7 @@
}
private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION
+ private var selectionItem: SelectionItem? = null
private lateinit var allStructures: List<StructureInfo>
private val controlsById = mutableMapOf<ControlKey, ControlWithState>()
private val controlViewsById = mutableMapOf<ControlKey, ControlViewHolder>()
@@ -230,6 +231,7 @@
this.overflowMenuAdapter = null
hidden = false
retainCache = false
+ selectionItem = null
controlActionCoordinator.activityContext = activityContext
@@ -272,7 +274,7 @@
}
}
- private fun reload(parent: ViewGroup) {
+ private fun reload(parent: ViewGroup, dismissTaskView: Boolean = true) {
if (hidden) return
controlsListingController.get().removeCallback(listingCallback)
@@ -327,8 +329,8 @@
@VisibleForTesting
internal fun startRemovingApp(componentName: ComponentName, appName: CharSequence) {
removeAppDialog?.cancel()
- removeAppDialog = dialogsFactory.createRemoveAppDialog(context, appName) {
- if (!controlsController.get().removeFavorites(componentName)) {
+ removeAppDialog = dialogsFactory.createRemoveAppDialog(context, appName) { shouldRemove ->
+ if (!shouldRemove || !controlsController.get().removeFavorites(componentName)) {
return@createRemoveAppDialog
}
@@ -425,6 +427,7 @@
} else {
Log.w(ControlsUiController.TAG, "Not TaskViewFactory to display panel $selectionItem")
}
+ this.selectionItem = selectionItem
bgExecutor.execute {
val intent = Intent(Intent.ACTION_MAIN)
@@ -657,6 +660,7 @@
val maxColumns = ControlAdapter.findMaxColumns(activityContext.resources)
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
+ listView.removeAllViews()
var lastRow: ViewGroup = createRow(inflater, listView)
selectedStructure.controls.forEach {
val key = ControlKey(selectedStructure.componentName, it.controlId)
@@ -804,6 +808,15 @@
}
}
+ override fun onOrientationChange() {
+ selectionItem?.let {
+ when (selectedItem) {
+ is SelectedItem.StructureItem -> createListView(it)
+ is SelectedItem.PanelItem -> taskViewController?.refreshBounds() ?: reload(parent)
+ }
+ } ?: reload(parent)
+ }
+
private fun createRow(inflater: LayoutInflater, listView: ViewGroup): ViewGroup {
val row = inflater.inflate(R.layout.controls_row, listView, false) as ViewGroup
listView.addView(row)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
index 78e87ca..1f89c91 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
@@ -37,7 +37,7 @@
private val activityContext: Context,
private val uiExecutor: Executor,
private val pendingIntent: PendingIntent,
- private val taskView: TaskView,
+ val taskView: TaskView,
private val hide: () -> Unit = {}
) {
@@ -108,6 +108,10 @@
}
}
+ fun refreshBounds() {
+ taskView.onLocationChanged()
+ }
+
fun dismiss() {
taskView.release()
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 661b2b6..c0e1133 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -162,12 +162,6 @@
val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES =
releasedFlag(216, "customizable_lock_screen_quick_affordances")
- /** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
- // TODO(b/256513609): Tracking Bug
- @JvmField
- val ACTIVE_UNLOCK_CHIPBAR =
- resourceBooleanFlag(217, R.bool.flag_active_unlock_chipbar, "active_unlock_chipbar")
-
/**
* Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
* new KeyguardTransitionRepository.
@@ -284,8 +278,7 @@
/** Enables new QS Edit Mode visual refresh */
// TODO(b/269787742): Tracking Bug
@JvmField
- val ENABLE_NEW_QS_EDIT_MODE =
- unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false)
+ val ENABLE_NEW_QS_EDIT_MODE = unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false)
// 600- status bar
@@ -680,4 +673,9 @@
@JvmField
val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true)
+
+ // TODO(b/272805037): Tracking Bug
+ @JvmField
+ val ADVANCED_VPN_ENABLED = unreleasedFlag(2800, name = "AdvancedVpn__enable_feature",
+ namespace = "vpn", teamfood = false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index fab8c06..78082c3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -225,8 +225,10 @@
val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
val translationYBy = getTranslationAmount()
+ // Expand ripple before translating icon container to make sure both views have same bounds.
+ rippleController.expandToInProgressState(rippleView, iconRippleView)
// Make the icon container view starts animation from bottom of the screen.
- iconContainerView.translationY += rippleController.getReceiverIconSize()
+ iconContainerView.translationY = rippleController.getReceiverIconSize().toFloat()
animateViewTranslationAndFade(
iconContainerView,
translationYBy = -1 * translationYBy,
@@ -235,7 +237,6 @@
) {
animateBouncingView(iconContainerView, translationYBy * BOUNCE_TRANSLATION_RATIO)
}
- rippleController.expandToInProgressState(rippleView, iconRippleView)
}
override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
@@ -293,7 +294,7 @@
/** Returns the amount that the chip will be translated by in its intro animation. */
private fun getTranslationAmount(): Float {
- return rippleController.getRippleSize() * 0.5f
+ return rippleController.getReceiverIconSize() * 2f
}
private fun View.getAppIconView(): CachingIconView {
diff --git a/packages/SystemUI/src/com/android/systemui/multishade/ui/view/MultiShadeView.kt b/packages/SystemUI/src/com/android/systemui/multishade/ui/view/MultiShadeView.kt
new file mode 100644
index 0000000..aecec39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/multishade/ui/view/MultiShadeView.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.multishade.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
+import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel
+import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.launch
+
+/**
+ * View that hosts the multi-shade system and acts as glue between legacy code and the
+ * implementation.
+ */
+class MultiShadeView(
+ context: Context,
+ attrs: AttributeSet?,
+) :
+ FrameLayout(
+ context,
+ attrs,
+ ) {
+
+ fun init(
+ interactor: MultiShadeInteractor,
+ clock: SystemClock,
+ ) {
+ repeatWhenAttached {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ addView(
+ ComposeFacade.createMultiShadeView(
+ context = context,
+ viewModel =
+ MultiShadeViewModel(
+ viewModelScope = this,
+ interactor = interactor,
+ ),
+ clock = clock,
+ )
+ )
+ }
+
+ // Here when destroyed.
+ removeAllViews()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index 95f1419..fbf1a0e 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -73,7 +73,7 @@
return new PluginInstance.Factory(
PluginModule.class.getClassLoader(),
new PluginInstance.InstanceFactory<>(),
- new PluginInstance.VersionChecker(),
+ new PluginInstance.VersionCheckerImpl(),
privilegedPlugins,
isDebug);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index baa812c..584d27f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -635,7 +635,8 @@
&& mLastKeyguardAndExpanded == onKeyguardAndExpanded
&& mLastViewHeight == currentHeight
&& mLastHeaderTranslation == headerTranslation
- && mSquishinessFraction == squishinessFraction) {
+ && mSquishinessFraction == squishinessFraction
+ && mLastPanelFraction == panelExpansionFraction) {
return;
}
mLastHeaderTranslation = headerTranslation;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index a71e6dd..9ece72d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -26,6 +26,7 @@
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.util.leak.GarbageMonitor;
import java.util.ArrayList;
@@ -33,7 +34,7 @@
import java.util.Collection;
import java.util.List;
-public interface QSHost {
+public interface QSHost extends PanelInteractor {
String TILES_SETTING = Settings.Secure.QS_TILES;
int POSITION_AT_END = -1;
@@ -57,9 +58,6 @@
}
void warn(String message, Throwable t);
- void collapsePanels();
- void forceCollapsePanels();
- void openPanels();
Context getContext();
Context getUserContext();
int getUserId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
new file mode 100644
index 0000000..958fa71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.qs.dagger
+
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface QSHostModule {
+
+ @Binds fun provideQsHost(controllerImpl: QSTileHost): QSHost
+
+ @Module
+ companion object {
+ @Provides
+ @JvmStatic
+ fun providePanelInteractor(
+ featureFlags: FeatureFlags,
+ qsHost: QSHost,
+ panelInteractorImpl: PanelInteractorImpl
+ ): PanelInteractor {
+ return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+ panelInteractorImpl
+ } else {
+ qsHost
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 431d6e8..cfe9313 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -27,7 +27,6 @@
import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -45,7 +44,6 @@
import javax.inject.Named;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
@@ -54,7 +52,13 @@
* Module for QS dependencies
*/
@Module(subcomponents = {QSFragmentComponent.class},
- includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class})
+ includes = {
+ MediaModule.class,
+ QSExternalModule.class,
+ QSFlagsModule.class,
+ QSHostModule.class
+ }
+)
public interface QSModule {
/** A map of internal QS tiles. Ensures that this can be injected even if
@@ -100,8 +104,4 @@
manager.init();
return manager;
}
-
- /** */
- @Binds
- QSHost provideQsHost(QSTileHost controllerImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index adc7165..2083cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -40,6 +40,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -74,6 +75,7 @@
private final CommandQueue mCommandQueue;
private final UserTracker mUserTracker;
private final StatusBarIconController mStatusBarIconController;
+ private final PanelInteractor mPanelInteractor;
private int mMaxBound = DEFAULT_MAX_BOUND;
@@ -85,7 +87,8 @@
UserTracker userTracker,
KeyguardStateController keyguardStateController,
CommandQueue commandQueue,
- StatusBarIconController statusBarIconController) {
+ StatusBarIconController statusBarIconController,
+ PanelInteractor panelInteractor) {
mHost = host;
mKeyguardStateController = keyguardStateController;
mContext = mHost.getContext();
@@ -96,6 +99,7 @@
mCommandQueue = commandQueue;
mStatusBarIconController = statusBarIconController;
mCommandQueue.addCallback(mRequestListeningCallback);
+ mPanelInteractor = panelInteractor;
}
public Context getContext() {
@@ -255,7 +259,7 @@
if (customTile != null) {
verifyCaller(customTile);
customTile.onDialogShown();
- mHost.forceCollapsePanels();
+ mPanelInteractor.forceCollapsePanels();
Objects.requireNonNull(mServices.get(customTile)).setShowingDialog(true);
}
}
@@ -275,7 +279,7 @@
CustomTile customTile = getTileForToken(token);
if (customTile != null) {
verifyCaller(customTile);
- mHost.forceCollapsePanels();
+ mPanelInteractor.forceCollapsePanels();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt
new file mode 100644
index 0000000..260caa7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import java.util.Optional
+import javax.inject.Inject
+
+/** Encapsulates business logic for interacting with the QS panel. */
+interface PanelInteractor {
+
+ /** Collapse the shade */
+ fun collapsePanels()
+
+ /** Collapse the shade forcefully, skipping some animations. */
+ fun forceCollapsePanels()
+
+ /** Open the Quick Settings panel */
+ fun openPanels()
+}
+
+@SysUISingleton
+class PanelInteractorImpl
+@Inject
+constructor(
+ private val centralSurfaces: Optional<CentralSurfaces>,
+) : PanelInteractor {
+ override fun collapsePanels() {
+ centralSurfaces.ifPresent { it.postAnimateCollapsePanels() }
+ }
+
+ override fun forceCollapsePanels() {
+ centralSurfaces.ifPresent { it.postAnimateForceCollapsePanels() }
+ }
+
+ override fun openPanels() {
+ centralSurfaces.ifPresent { it.postAnimateOpenPanels() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 89d402a3..27f5826 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -38,6 +38,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -52,6 +53,7 @@
private final LocationController mController;
private final KeyguardStateController mKeyguard;
+ private final PanelInteractor mPanelInteractor;
private final Callback mCallback = new Callback();
@Inject
@@ -65,12 +67,14 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
LocationController locationController,
- KeyguardStateController keyguardStateController
+ KeyguardStateController keyguardStateController,
+ PanelInteractor panelInteractor
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = locationController;
mKeyguard = keyguardStateController;
+ mPanelInteractor = panelInteractor;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
}
@@ -90,7 +94,7 @@
if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
final boolean wasEnabled = mState.value;
- mHost.openPanels();
+ mPanelInteractor.openPanels();
mController.setLocationEnabled(!wasEnabled);
});
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 07b50c9..65592a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -42,6 +42,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -66,6 +67,7 @@
private final Callback mCallback = new Callback();
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final FeatureFlags mFlags;
+ private final PanelInteractor mPanelInteractor;
private long mMillisUntilFinished = 0;
@@ -83,7 +85,8 @@
RecordingController controller,
KeyguardDismissUtil keyguardDismissUtil,
KeyguardStateController keyguardStateController,
- DialogLaunchAnimator dialogLaunchAnimator
+ DialogLaunchAnimator dialogLaunchAnimator,
+ PanelInteractor panelInteractor
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -93,6 +96,7 @@
mKeyguardDismissUtil = keyguardDismissUtil;
mKeyguardStateController = keyguardStateController;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mPanelInteractor = panelInteractor;
}
@Override
@@ -171,7 +175,7 @@
// disable the exit animation which looks weird when it happens at the same time as the
// shade collapsing.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
- getHost().collapsePanels();
+ mPanelInteractor.collapsePanels();
};
final Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 8a3ecc6..0748bcb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -21,7 +21,6 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
@@ -713,6 +712,7 @@
}
public void startConnectionToCurrentUser() {
+ Log.v(TAG_OPS, "startConnectionToCurrentUser: connection is restarted");
if (mHandler.getLooper() != Looper.myLooper()) {
mHandler.post(mConnectionRunnable);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 06426b3..30865aa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3581,15 +3581,12 @@
}
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
- // don't fling while in keyguard to avoid jump in shade expand animation
- boolean fullyExpandedInKeyguard = mBarState == KEYGUARD && mExpandedFraction >= 1.0;
mTrackingPointer = -1;
mAmbientState.setSwipingUp(false);
- if (!fullyExpandedInKeyguard && ((mTracking && mTouchSlopExceeded)
- || Math.abs(x - mInitialExpandX) > mTouchSlop
+ if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
|| Math.abs(y - mInitialExpandY) > mTouchSlop
|| (!isFullyExpanded() && !isFullyCollapsed())
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel)) {
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
mVelocityTracker.computeCurrentVelocity(1000);
float vel = mVelocityTracker.getYVelocity();
float vectorVel = (float) Math.hypot(
@@ -3637,9 +3634,9 @@
if (mUpdateFlingOnLayout) {
mUpdateFlingVelocity = vel;
}
- } else if (fullyExpandedInKeyguard || (!mCentralSurfaces.isBouncerShowing()
+ } else if (!mCentralSurfaces.isBouncerShowing()
&& !mAlternateBouncerInteractor.isVisibleState()
- && !mKeyguardStateController.isKeyguardGoingAway())) {
+ && !mKeyguardStateController.isKeyguardGoingAway()) {
onEmptySpaceClick();
onTrackingStopped(true);
}
@@ -3782,10 +3779,10 @@
mHeightAnimator.end();
}
}
- mQsController.setShadeExpandedHeight(mExpandedHeight);
- mExpansionDragDownAmountPx = h;
mExpandedFraction = Math.min(1f,
maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
+ mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
+ mExpansionDragDownAmountPx = h;
mAmbientState.setExpansionFraction(mExpandedFraction);
onHeightUpdated(mExpandedHeight);
updatePanelExpansionAndVisibility();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 2899081..a716a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -23,7 +23,6 @@
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputDevice;
@@ -31,6 +30,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStub;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
@@ -39,8 +39,10 @@
import com.android.systemui.R;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -49,6 +51,8 @@
import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
+import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor;
+import com.android.systemui.multishade.ui.view.MultiShadeView;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController;
@@ -63,11 +67,13 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Controller for {@link NotificationShadeWindowView}.
@@ -115,6 +121,7 @@
mIsOcclusionTransitionRunning =
step.getTransitionState() == TransitionState.RUNNING;
};
+ private final SystemClock mClock;
@Inject
public NotificationShadeWindowViewController(
@@ -142,7 +149,9 @@
UdfpsOverlayInteractor udfpsOverlayInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ Provider<MultiShadeInteractor> multiShadeInteractorProvider,
+ SystemClock clock) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -175,6 +184,16 @@
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
+
+ mClock = clock;
+ if (ComposeFacade.INSTANCE.isComposeAvailable()
+ && featureFlags.isEnabled(Flags.DUAL_SHADE)) {
+ final ViewStub multiShadeViewStub = mView.findViewById(R.id.multi_shade_stub);
+ if (multiShadeViewStub != null) {
+ final MultiShadeView multiShadeView = (MultiShadeView) multiShadeViewStub.inflate();
+ multiShadeView.init(multiShadeInteractorProvider.get(), clock);
+ }
+ }
}
/**
@@ -269,7 +288,7 @@
mLockIconViewController.onTouchEvent(
ev,
() -> mService.wakeUpIfDozing(
- SystemClock.uptimeMillis(),
+ mClock.uptimeMillis(),
mView,
"LOCK_ICON_TOUCH",
PowerManager.WAKE_REASON_GESTURE)
@@ -453,7 +472,7 @@
public void cancelCurrentTouch() {
if (mTouchActive) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
final MotionEvent event;
if (mIsTrackpadGestureBackEnabled) {
event = MotionEvent.obtain(mDownEvent);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index df8ae50..9f46707 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -351,7 +351,6 @@
mFeatureFlags = featureFlags;
mInteractionJankMonitor = interactionJankMonitor;
- mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
}
@@ -878,8 +877,9 @@
mCollapsedOnDown = collapsedOnDown;
}
- void setShadeExpandedHeight(float shadeExpandedHeight) {
- mShadeExpandedHeight = shadeExpandedHeight;
+ void setShadeExpansion(float expandedHeight, float expandedFraction) {
+ mShadeExpandedHeight = expandedHeight;
+ mShadeExpandedFraction = expandedFraction;
}
@VisibleForTesting
@@ -1749,11 +1749,6 @@
return false;
}
- @VisibleForTesting
- void onPanelExpansionChanged(ShadeExpansionChangeEvent event) {
- mShadeExpandedFraction = event.getFraction();
- }
-
/**
* Animate QS closing by flinging it.
* If QS is expanded, it will collapse into QQS and stop.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 01e042b..c920e1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -657,25 +657,6 @@
R.string.input_switch_input_language_previous),
KeyEvent.KEYCODE_SPACE,
KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON),
- null))),
- /* Access emoji: Meta + . */
- new ShortcutMultiMappingInfo(
- context.getString(R.string.input_access_emoji),
- null,
- Arrays.asList(
- new ShortcutKeyGroup(new KeyboardShortcutInfo(
- context.getString(R.string.input_access_emoji),
- KeyEvent.KEYCODE_PERIOD,
- KeyEvent.META_META_ON),
- null))),
- /* Access voice typing: Meta + V */
- new ShortcutMultiMappingInfo(
- context.getString(R.string.input_access_voice_typing),
- null,
- Arrays.asList(
- new ShortcutKeyGroup(new KeyboardShortcutInfo(
- context.getString(R.string.input_access_voice_typing),
- KeyEvent.KEYCODE_V, KeyEvent.META_META_ON),
null)))
);
return new KeyboardShortcutMultiMappingGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 51c5183..cac4251 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.KeyguardManager;
@@ -145,7 +144,10 @@
break;
case Intent.ACTION_USER_UNLOCKED:
// Start the overview connection to the launcher service
- mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+ // Connect if user hasn't connected yet
+ if (mOverviewProxyServiceLazy.get().getProxy() == null) {
+ mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+ }
break;
case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION:
final IntentSender intentSender = intent.getParcelableExtra(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index b9ac918..79d01b4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -56,6 +56,7 @@
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.util.Compile;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -299,7 +300,7 @@
@Override
public boolean setIsDreaming(boolean isDreaming) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG) {
Log.d(TAG, "setIsDreaming:" + isDreaming);
}
if (mIsDreaming == isDreaming) {
@@ -321,6 +322,11 @@
}
@Override
+ public boolean isDreaming() {
+ return mIsDreaming;
+ }
+
+ @Override
public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
if (animated && mDozeAmountTarget == dozeAmount) {
@@ -580,6 +586,7 @@
pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide);
pw.println(" mKeyguardRequested=" + mKeyguardRequested);
pw.println(" mIsDozing=" + mIsDozing);
+ pw.println(" mIsDreaming=" + mIsDreaming);
pw.println(" mListeners{" + mListeners.size() + "}=");
for (RankedListener rl : mListeners) {
pw.println(" " + rl.mListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 274377f..6f4eed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -28,12 +28,9 @@
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
-import android.util.Log;
import androidx.annotation.NonNull;
@@ -70,7 +67,6 @@
private final KeyguardStateController mKeyguardStateController;
private final ContentResolver mContentResolver;
private final PowerManager mPowerManager;
- private final IDreamManager mDreamManager;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatteryController mBatteryController;
private final HeadsUpManager mHeadsUpManager;
@@ -112,7 +108,6 @@
public NotificationInterruptStateProviderImpl(
ContentResolver contentResolver,
PowerManager powerManager,
- IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
BatteryController batteryController,
StatusBarStateController statusBarStateController,
@@ -126,7 +121,6 @@
UserTracker userTracker) {
mContentResolver = contentResolver;
mPowerManager = powerManager;
- mDreamManager = dreamManager;
mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mStatusBarStateController = statusBarStateController;
@@ -287,7 +281,9 @@
}
// If the device is currently dreaming, then launch the FullScreenIntent
- if (isDreaming()) {
+ // We avoid using IDreamManager#isDreaming here as that method will return false during
+ // the dream's wake-up phase.
+ if (mStatusBarStateController.isDreaming()) {
return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_DEVICE_IS_DREAMING,
suppressedByDND);
}
@@ -365,16 +361,6 @@
}
}
}
-
- private boolean isDreaming() {
- try {
- return mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- return false;
- }
- }
-
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
@@ -424,7 +410,7 @@
return false;
}
- boolean inUse = mPowerManager.isScreenOn() && !isDreaming();
+ boolean inUse = mPowerManager.isScreenOn() && !mStatusBarStateController.isDreaming();
if (!inUse) {
if (log) mLogger.logNoHeadsUpNotInUse(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index a0a0372..209ea41 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -59,6 +59,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -412,6 +413,7 @@
private final GarbageMonitor gm;
private ProcessMemInfo pmi;
private boolean dumpInProgress;
+ private final PanelInteractor mPanelInteractor;
@Inject
public MemoryTile(
@@ -423,11 +425,13 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- GarbageMonitor monitor
+ GarbageMonitor monitor,
+ PanelInteractor panelInteractor
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
gm = monitor;
+ mPanelInteractor = panelInteractor;
}
@Override
@@ -457,7 +461,7 @@
mHandler.post(() -> {
dumpInProgress = false;
refreshState();
- getHost().collapsePanels();
+ mPanelInteractor.collapsePanels();
mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0);
});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 3f61bf7..10757ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -347,7 +347,7 @@
whenever(controlsController.removeFavorites(eq(componentName))).thenReturn(true)
val panel = SelectedItem.PanelItem("App name", componentName)
preferredPanelRepository.setSelectedComponent(
- SelectedComponentRepository.SelectedComponent(panel)
+ SelectedComponentRepository.SelectedComponent(panel)
)
underTest.show(parent, {}, context)
underTest.startRemovingApp(componentName, "Test App")
@@ -362,6 +362,26 @@
}
@Test
+ fun testCancelRemovingAppsDoesntRemoveFavorite() {
+ val componentName = ComponentName(context, "cls")
+ whenever(controlsController.removeFavorites(eq(componentName))).thenReturn(true)
+ val panel = SelectedItem.PanelItem("App name", componentName)
+ preferredPanelRepository.setSelectedComponent(
+ SelectedComponentRepository.SelectedComponent(panel)
+ )
+ underTest.show(parent, {}, context)
+ underTest.startRemovingApp(componentName, "Test App")
+
+ fakeDialogController.clickNeutral()
+
+ verify(controlsController, never()).removeFavorites(eq(componentName))
+ assertThat(underTest.getPreferredSelectedItem(emptyList())).isEqualTo(panel)
+ assertThat(preferredPanelRepository.shouldAddDefaultComponent()).isTrue()
+ assertThat(preferredPanelRepository.getSelectedComponent())
+ .isEqualTo(SelectedComponentRepository.SelectedComponent(panel))
+ }
+
+ @Test
fun testHideCancelsTheRemoveAppDialog() {
val componentName = ComponentName(context, "cls")
underTest.show(parent, {}, context)
@@ -372,10 +392,42 @@
verify(fakeDialogController.dialog).cancel()
}
+ @Test
+ fun testOnRotationWithPanelUpdateBoundsCalled() {
+ mockLayoutInflater()
+ val packageName = "pkg"
+ `when`(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf(packageName))
+ val panel = SelectedItem.PanelItem("App name", ComponentName(packageName, "cls"))
+ val serviceInfo = setUpPanel(panel)
+
+ underTest.show(parent, {}, context)
+
+ val captor = argumentCaptor<ControlsListingController.ControlsListingCallback>()
+
+ verify(controlsListingController).addCallback(capture(captor))
+ captor.value.onServicesUpdated(listOf(serviceInfo))
+ FakeExecutor.exhaustExecutors(uiExecutor, bgExecutor)
+
+ val taskViewConsumerCaptor = argumentCaptor<Consumer<TaskView>>()
+ verify(taskViewFactory).create(eq(context), eq(uiExecutor), capture(taskViewConsumerCaptor))
+
+ val taskView: TaskView = mock {
+ `when`(this.post(any())).thenAnswer {
+ uiExecutor.execute(it.arguments[0] as Runnable)
+ true
+ }
+ }
+
+ taskViewConsumerCaptor.value.accept(taskView)
+
+ underTest.onOrientationChange()
+ verify(taskView).onLocationChanged()
+ }
+
private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
val activity = ComponentName(context, "activity")
preferredPanelRepository.setSelectedComponent(
- SelectedComponentRepository.SelectedComponent(panel)
+ SelectedComponentRepository.SelectedComponent(panel)
)
return ControlsServiceInfo(panel.componentName, panel.appName, activity)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
index de04ef8..9df7992 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
@@ -152,4 +152,12 @@
listenerCaptor.value.onTaskRemovalStarted(0)
verify(taskView).release()
}
+
+ @Test
+ fun testOnRefreshBounds() {
+ underTest.launchTaskView()
+
+ underTest.refreshBounds()
+ verify(taskView).onLocationChanged()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 64e9a3e..7e052bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -29,6 +29,7 @@
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
+import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -41,6 +42,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -91,6 +93,8 @@
private TileLifecycleManager mTileLifecycleManager;
@Mock
private QSHost mQSHost;
+ @Mock
+ private PanelInteractor mPanelInteractor;
@Before
public void setUp() throws Exception {
@@ -107,7 +111,8 @@
Provider<Handler> provider = () -> new Handler(mTestableLooper.getLooper());
mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher,
- mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController);
+ mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController,
+ mPanelInteractor);
}
@After
@@ -222,13 +227,37 @@
verify(tile, never()).startActivityAndCollapse(pi);
}
+ @Test
+ public void testOnStartActivityCollapsesPanel() {
+ CustomTile tile = mock(CustomTile.class);
+ ComponentName componentName = mock(ComponentName.class);
+ when(tile.getComponent()).thenReturn(componentName);
+ when(componentName.getPackageName()).thenReturn(this.getContext().getPackageName());
+ TileServiceManager manager = mTileService.getTileWrapper(tile);
+
+ mTileService.onStartActivity(manager.getToken());
+ verify(mPanelInteractor).forceCollapsePanels();
+ }
+
+ @Test
+ public void testOnShowDialogCollapsesPanel() {
+ CustomTile tile = mock(CustomTile.class);
+ ComponentName componentName = mock(ComponentName.class);
+ when(tile.getComponent()).thenReturn(componentName);
+ when(componentName.getPackageName()).thenReturn(this.getContext().getPackageName());
+ TileServiceManager manager = mTileService.getTileWrapper(tile);
+
+ mTileService.onShowDialog(manager.getToken());
+ verify(mPanelInteractor).forceCollapsePanels();
+ }
+
private class TestTileServices extends TileServices {
TestTileServices(QSHost host, Provider<Handler> handlerProvider,
BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
KeyguardStateController keyguardStateController, CommandQueue commandQueue,
- StatusBarIconController statusBarIconController) {
+ StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) {
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
- commandQueue, statusBarIconController);
+ commandQueue, statusBarIconController, panelInteractor);
}
@Override
@@ -237,6 +266,8 @@
TileServiceManager manager = mock(TileServiceManager.class);
mManagers.add(manager);
when(manager.isLifecycleStarted()).thenReturn(true);
+ Binder b = new Binder();
+ when(manager.getToken()).thenReturn(b);
return manager;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
new file mode 100644
index 0000000..45783ab
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PanelInteractorImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun openPanels_callsCentralSurfaces() {
+ val underTest = PanelInteractorImpl(Optional.of(centralSurfaces))
+
+ underTest.openPanels()
+
+ verify(centralSurfaces).postAnimateOpenPanels()
+ }
+
+ @Test
+ fun collapsePanels_callsCentralSurfaces() {
+ val underTest = PanelInteractorImpl(Optional.of(centralSurfaces))
+
+ underTest.collapsePanels()
+
+ verify(centralSurfaces).postAnimateCollapsePanels()
+ }
+
+ @Test
+ fun forceCollapsePanels_callsCentralSurfaces() {
+ val underTest = PanelInteractorImpl(Optional.of(centralSurfaces))
+
+ underTest.forceCollapsePanels()
+
+ verify(centralSurfaces).postAnimateForceCollapsePanels()
+ }
+
+ @Test
+ fun whenOptionalEmpty_doesnThrow() {
+ val underTest = PanelInteractorImpl(Optional.empty())
+
+ underTest.openPanels()
+ underTest.collapsePanels()
+ underTest.forceCollapsePanels()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 33921c7..3642e87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -31,9 +31,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -41,6 +44,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -65,6 +69,8 @@
private lateinit var locationController: LocationController
@Mock
private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var panelInteractor: PanelInteractor
private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper
@@ -86,7 +92,9 @@
activityStarter,
qsLogger,
locationController,
- keyguardStateController)
+ keyguardStateController,
+ panelInteractor,
+ )
}
@After
@@ -116,4 +124,18 @@
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on))
}
+
+ @Test
+ fun testClickWhenLockedWillCallOpenPanels() {
+ `when`(keyguardStateController.isMethodSecure).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+
+ tile.handleClick(null)
+
+ val captor = argumentCaptor<Runnable>()
+ verify(activityStarter).postQSRunnableDismissingKeyguard(capture(captor))
+ captor.value.run()
+
+ verify(panelInteractor).openPanels()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 5aef758..d9ed1a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -83,6 +84,8 @@
private KeyguardStateController mKeyguardStateController;
@Mock
private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock
+ private PanelInteractor mPanelInteractor;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -108,7 +111,8 @@
mController,
mKeyguardDismissUtil,
mKeyguardStateController,
- mDialogLaunchAnimator
+ mDialogLaunchAnimator,
+ mPanelInteractor
);
mTile.initialize();
@@ -146,7 +150,7 @@
assertNotNull(onStartRecordingClicked.getValue());
onStartRecordingClicked.getValue().run();
verify(mDialogLaunchAnimator).disableAllCurrentDialogsExitAnimations();
- verify(mHost).collapsePanels();
+ verify(mPanelInteractor).collapsePanels();
}
// Test that the tile is active and labeled correctly when the controller is starting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 0dc2d17..bdb0e7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -37,6 +37,9 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
+import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
+import com.android.systemui.multishade.data.repository.MultiShadeRepository
+import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
@@ -50,8 +53,12 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,10 +72,12 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
+
@Mock private lateinit var view: NotificationShadeWindowView
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var centralSurfaces: CentralSurfaces
@@ -102,6 +111,8 @@
private lateinit var underTest: NotificationShadeWindowViewController
+ private lateinit var testScope: TestScope
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -115,8 +126,12 @@
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow<TransitionStep>())
- val featureFlags = FakeFeatureFlags();
+ val featureFlags = FakeFeatureFlags()
featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
+ featureFlags.set(Flags.DUAL_SHADE, false)
+
+ val inputProxy = MultiShadeInputProxy()
+ testScope = TestScope()
underTest =
NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
@@ -144,6 +159,18 @@
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
+ {
+ MultiShadeInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository =
+ MultiShadeRepository(
+ applicationContext = context,
+ inputProxy = inputProxy,
+ ),
+ inputProxy = inputProxy,
+ )
+ },
+ FakeSystemClock(),
)
underTest.setupExpandedStatusBar()
@@ -156,147 +183,162 @@
// tests need to be added to test the rest of handleDispatchTouchEvent.
@Test
- fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
- underTest.setStatusBarViewController(null)
+ fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(null)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
- assertThat(returnVal).isFalse()
- }
+ assertThat(returnVal).isFalse()
+ }
@Test
- fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
- whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
+ fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
+ whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
- verify(phoneStatusBarViewController).sendTouchToView(ev)
- assertThat(returnVal).isTrue()
- }
+ verify(phoneStatusBarViewController).sendTouchToView(ev)
+ assertThat(returnVal).isTrue()
+ }
@Test
- fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- val downEvBelow =
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
- interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
+ fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ val downEvBelow =
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
+ interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
- val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
- whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
+ val nextEvent =
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
+ whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
- verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
- assertThat(returnVal).isTrue()
- }
+ verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
+ assertThat(returnVal).isTrue()
+ }
@Test
- fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
- whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
- whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
+ fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
- verify(phoneStatusBarViewController).sendTouchToView(downEv)
- assertThat(returnVal).isTrue()
- }
+ verify(phoneStatusBarViewController).sendTouchToView(DOWN_EVENT)
+ assertThat(returnVal).isTrue()
+ }
@Test
- fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
- // Item we're testing
- whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
+ fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ // Item we're testing
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
- verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
- assertThat(returnVal).isNull()
- }
+ verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
+ assertThat(returnVal).isNull()
+ }
@Test
- fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
- // Item we're testing
- whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(false)
+ fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ // Item we're testing
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(false)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
- verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
- assertThat(returnVal).isNull()
- }
+ verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
+ assertThat(returnVal).isNull()
+ }
@Test
- fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
- whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
- // Item we're testing
- whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
+ fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ // Item we're testing
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
- verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
- assertThat(returnVal).isTrue()
- }
+ verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
+ assertThat(returnVal).isTrue()
+ }
@Test
- fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
- whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
- whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
+ fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() =
+ testScope.runTest {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
- // Down event first
- interactionEventHandler.handleDispatchTouchEvent(downEv)
+ // Down event first
+ interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
- // Then another event
- val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
+ // Then another event
+ val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
+ whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
- val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
- verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
- assertThat(returnVal).isTrue()
- }
+ verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
+ assertThat(returnVal).isTrue()
+ }
@Test
- fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() {
- // Down event within udfpsOverlay bounds while alternateBouncer is showing
- whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(downEv)).thenReturn(false)
- whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
+ fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() =
+ testScope.runTest {
+ // Down event within udfpsOverlay bounds while alternateBouncer is showing
+ whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(DOWN_EVENT))
+ .thenReturn(false)
+ whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
- // Then touch should not be intercepted
- val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(downEv)
- assertThat(shouldIntercept).isFalse()
- }
+ // Then touch should not be intercepted
+ val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
+ assertThat(shouldIntercept).isFalse()
+ }
@Test
- fun testGetBouncerContainer() {
- Mockito.clearInvocations(view)
- underTest.bouncerContainer
- verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
- }
+ fun testGetBouncerContainer() =
+ testScope.runTest {
+ Mockito.clearInvocations(view)
+ underTest.bouncerContainer
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
+ }
@Test
- fun testGetKeyguardMessageArea() {
- underTest.keyguardMessageArea
- verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
+ fun testGetKeyguardMessageArea() =
+ testScope.runTest {
+ underTest.keyguardMessageArea
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
+ }
+
+ companion object {
+ private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ private const val VIEW_BOTTOM = 100
}
}
-
-private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
deleted file mode 100644
index 2797440..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shade;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
-import android.os.SystemClock;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.MotionEvent;
-import android.view.ViewGroup;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardSecurityContainerController;
-import com.android.keyguard.LockIconViewController;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
-import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
-import com.android.systemui.statusbar.DragDownHelper;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationInsetsController;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.stack.AmbientState;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.window.StatusBarWindowStateController;
-import com.android.systemui.tuner.TunerService;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@SmallTest
-public class NotificationShadeWindowViewTest extends SysuiTestCase {
-
- private NotificationShadeWindowView mView;
- private NotificationShadeWindowViewController mController;
-
- @Mock private TunerService mTunerService;
- @Mock private DragDownHelper mDragDownHelper;
- @Mock private SysuiStatusBarStateController mStatusBarStateController;
- @Mock private ShadeController mShadeController;
- @Mock private CentralSurfaces mCentralSurfaces;
- @Mock private DockManager mDockManager;
- @Mock private NotificationPanelViewController mNotificationPanelViewController;
- @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
- @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
- @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
- @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
- @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private StatusBarWindowStateController mStatusBarWindowStateController;
- @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private LockIconViewController mLockIconViewController;
- @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- @Mock private AmbientState mAmbientState;
- @Mock private PulsingGestureListener mPulsingGestureListener;
- @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
- @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
- @Mock private KeyguardBouncerComponent mKeyguardBouncerComponent;
- @Mock private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
- @Mock private NotificationInsetsController mNotificationInsetsController;
- @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
- @Mock private UdfpsOverlayInteractor mUdfpsOverlayInteractor;
- @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
- @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
-
- @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
- mInteractionEventHandlerCaptor;
- private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mView = spy(new NotificationShadeWindowView(getContext(), null));
- when(mView.findViewById(R.id.notification_stack_scroller))
- .thenReturn(mNotificationStackScrollLayout);
-
- when(mView.findViewById(R.id.keyguard_bouncer_container)).thenReturn(mock(ViewGroup.class));
- when(mKeyguardBouncerComponentFactory.create(any(ViewGroup.class))).thenReturn(
- mKeyguardBouncerComponent);
- when(mKeyguardBouncerComponent.getSecurityContainerController()).thenReturn(
- mKeyguardSecurityContainerController);
-
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- mDependency.injectTestDependency(ShadeController.class, mShadeController);
-
- when(mDockManager.isDocked()).thenReturn(false);
-
- when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
- .thenReturn(emptyFlow());
-
- FakeFeatureFlags featureFlags = new FakeFeatureFlags();
- featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false);
- mController = new NotificationShadeWindowViewController(
- mLockscreenShadeTransitionController,
- new FalsingCollectorFake(),
- mStatusBarStateController,
- mDockManager,
- mNotificationShadeDepthController,
- mView,
- mNotificationPanelViewController,
- new ShadeExpansionStateManager(),
- mNotificationStackScrollLayoutController,
- mStatusBarKeyguardViewManager,
- mStatusBarWindowStateController,
- mLockIconViewController,
- mCentralSurfaces,
- mNotificationShadeWindowController,
- mKeyguardUnlockAnimationController,
- mNotificationInsetsController,
- mAmbientState,
- mPulsingGestureListener,
- mKeyguardBouncerViewModel,
- mKeyguardBouncerComponentFactory,
- mAlternateBouncerInteractor,
- mUdfpsOverlayInteractor,
- mKeyguardTransitionInteractor,
- mPrimaryBouncerToGoneTransitionViewModel,
- featureFlags
- );
- mController.setupExpandedStatusBar();
- mController.setDragDownHelper(mDragDownHelper);
- }
-
- @Test
- public void testDragDownHelperCalledWhenDraggingDown() {
- when(mDragDownHelper.isDraggingDown()).thenReturn(true);
- long now = SystemClock.elapsedRealtime();
- MotionEvent ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0 /* x */, 0 /* y */,
- 0 /* meta */);
- mView.onTouchEvent(ev);
- verify(mDragDownHelper).onTouchEvent(ev);
- ev.recycle();
- }
-
- @Test
- public void testInterceptTouchWhenShowingAltAuth() {
- captureInteractionEventHandler();
-
- // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true);
- when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
-
- // THEN we should intercept touch
- assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
- }
-
- @Test
- public void testNoInterceptTouch() {
- captureInteractionEventHandler();
-
- // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
- when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
-
- // THEN we shouldn't intercept touch
- assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
- }
-
- @Test
- public void testHandleTouchEventWhenShowingAltAuth() {
- captureInteractionEventHandler();
-
- // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
-
- // THEN we should handle the touch
- assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class)));
- }
-
- private void captureInteractionEventHandler() {
- verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture());
- mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue();
-
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
new file mode 100644
index 0000000..5d0f408
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shade
+
+import android.os.SystemClock
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.MotionEvent
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityContainerController
+import com.android.keyguard.LockIconViewController
+import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.dock.DockManager
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
+import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
+import com.android.systemui.multishade.data.repository.MultiShadeRepository
+import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
+import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.statusbar.DragDownHelper
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.NotificationInsetsController
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class NotificationShadeWindowViewTest : SysuiTestCase() {
+
+ @Mock private lateinit var dragDownHelper: DragDownHelper
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var shadeController: ShadeController
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dockManager: DockManager
+ @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
+ @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock
+ private lateinit var notificationStackScrollLayoutController:
+ NotificationStackScrollLayoutController
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+ @Mock
+ private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
+ @Mock private lateinit var lockIconViewController: LockIconViewController
+ @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock private lateinit var ambientState: AmbientState
+ @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
+ @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+ @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
+ @Mock private lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
+ @Mock
+ private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
+ @Mock private lateinit var notificationInsetsController: NotificationInsetsController
+ @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock
+ private lateinit var primaryBouncerToGoneTransitionViewModel:
+ PrimaryBouncerToGoneTransitionViewModel
+ @Captor
+ private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
+ @Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
+
+ private lateinit var underTest: NotificationShadeWindowView
+ private lateinit var controller: NotificationShadeWindowViewController
+ private lateinit var interactionEventHandler: InteractionEventHandler
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest = spy(NotificationShadeWindowView(context, null))
+ whenever(
+ underTest.findViewById<NotificationStackScrollLayout>(
+ R.id.notification_stack_scroller
+ )
+ )
+ .thenReturn(notificationStackScrollLayout)
+ whenever(underTest.findViewById<FrameLayout>(R.id.keyguard_bouncer_container))
+ .thenReturn(mock())
+ whenever(keyguardBouncerComponentFactory.create(any())).thenReturn(keyguardBouncerComponent)
+ whenever(keyguardBouncerComponent.securityContainerController)
+ .thenReturn(keyguardSecurityContainerController)
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ mDependency.injectTestDependency(ShadeController::class.java, shadeController)
+ whenever(dockManager.isDocked).thenReturn(false)
+ whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
+ .thenReturn(emptyFlow())
+
+ val featureFlags = FakeFeatureFlags()
+ featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
+ featureFlags.set(Flags.DUAL_SHADE, false)
+ val inputProxy = MultiShadeInputProxy()
+ testScope = TestScope()
+ controller =
+ NotificationShadeWindowViewController(
+ lockscreenShadeTransitionController,
+ FalsingCollectorFake(),
+ statusBarStateController,
+ dockManager,
+ notificationShadeDepthController,
+ underTest,
+ notificationPanelViewController,
+ ShadeExpansionStateManager(),
+ notificationStackScrollLayoutController,
+ statusBarKeyguardViewManager,
+ statusBarWindowStateController,
+ lockIconViewController,
+ centralSurfaces,
+ notificationShadeWindowController,
+ keyguardUnlockAnimationController,
+ notificationInsetsController,
+ ambientState,
+ pulsingGestureListener,
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory,
+ alternateBouncerInteractor,
+ udfpsOverlayInteractor,
+ keyguardTransitionInteractor,
+ primaryBouncerToGoneTransitionViewModel,
+ featureFlags,
+ {
+ MultiShadeInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository =
+ MultiShadeRepository(
+ applicationContext = context,
+ inputProxy = inputProxy,
+ ),
+ inputProxy = inputProxy,
+ )
+ },
+ FakeSystemClock(),
+ )
+
+ controller.setupExpandedStatusBar()
+ controller.setDragDownHelper(dragDownHelper)
+ }
+
+ @Test
+ fun testDragDownHelperCalledWhenDraggingDown() =
+ testScope.runTest {
+ whenever(dragDownHelper.isDraggingDown).thenReturn(true)
+ val now = SystemClock.elapsedRealtime()
+ val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
+ underTest.onTouchEvent(ev)
+ verify(dragDownHelper).onTouchEvent(ev)
+ ev.recycle()
+ }
+
+ @Test
+ fun testInterceptTouchWhenShowingAltAuth() =
+ testScope.runTest {
+ captureInteractionEventHandler()
+
+ // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
+ whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true)
+ whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
+
+ // THEN we should intercept touch
+ assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue()
+ }
+
+ @Test
+ fun testNoInterceptTouch() =
+ testScope.runTest {
+ captureInteractionEventHandler()
+
+ // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(false)
+ whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
+
+ // THEN we shouldn't intercept touch
+ assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse()
+ }
+
+ @Test
+ fun testHandleTouchEventWhenShowingAltAuth() =
+ testScope.runTest {
+ captureInteractionEventHandler()
+
+ // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
+ whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
+
+ // THEN we should handle the touch
+ assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue()
+ }
+
+ private fun captureInteractionEventHandler() {
+ verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
+ interactionEventHandler = interactionEventHandlerCaptor.value
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 15b8423..d8ffe39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -275,9 +275,7 @@
@Test
public void testPanelStaysOpenWhenClosingQs() {
- mShadeExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1,
- /* expanded= */ true, /* tracking= */ false, /* dragDownPxAmount= */ 0);
- mQsController.setShadeExpandedHeight(1);
+ mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1);
float shadeExpandedHeight = mQsController.getShadeExpandedHeight();
mQsController.animateCloseQs(false);
@@ -289,7 +287,7 @@
public void interceptTouchEvent_withinQs_shadeExpanded_startsQsTracking() {
mQsController.setQs(mQs);
- mQsController.setShadeExpandedHeight(1f);
+ mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1);
mQsController.onIntercept(
createMotionEvent(0, 0, ACTION_DOWN));
mQsController.onIntercept(
@@ -303,7 +301,7 @@
enableSplitShade(true);
mQsController.setQs(mQs);
- mQsController.setShadeExpandedHeight(1f);
+ mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1);
mQsController.onIntercept(
createMotionEvent(0, 0, ACTION_DOWN));
mQsController.onIntercept(
@@ -342,13 +340,8 @@
public void handleTouch_downActionInQsArea() {
mQsController.setQs(mQs);
mQsController.setBarState(SHADE);
- mQsController.onPanelExpansionChanged(
- new ShadeExpansionChangeEvent(
- 0.5f,
- true,
- true,
- 0
- ));
+ mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 0.5f);
+
MotionEvent event =
createMotionEvent(QS_FRAME_WIDTH / 2, QS_FRAME_BOTTOM / 2, ACTION_DOWN);
mQsController.handleTouch(event, false, false);
@@ -385,7 +378,7 @@
@Test
public void handleTouch_isConflictingExpansionGestureSet() {
assertThat(mQsController.isConflictingExpansionGesture()).isFalse();
- mShadeExpansionStateManager.onPanelExpansionChanged(1f, true, false, 0f);
+ mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1);
mQsController.handleTouch(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, ACTION_DOWN, 0f /* x */, 0f /* y */,
0 /* metaState */), false, false);
@@ -394,7 +387,7 @@
@Test
public void handleTouch_isConflictingExpansionGestureSet_cancel() {
- mShadeExpansionStateManager.onPanelExpansionChanged(1f, true, false, 0f);
+ mQsController.setShadeExpansion(/* shadeExpandedHeight= */ 1, /* expandedFraction=*/ 1);
mQsController.handleTouch(createMotionEvent(0, 0, ACTION_DOWN), false, false);
assertThat(mQsController.isConflictingExpansionGesture()).isTrue();
mQsController.handleTouch(createMotionEvent(0, 0, ACTION_UP), true, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 1fdb364..374aae1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -27,13 +27,16 @@
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
import com.android.systemui.plugins.PluginListener
+import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Rule
@@ -49,6 +52,7 @@
class ClockRegistryTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
+ private lateinit var scheduler: TestCoroutineScheduler
private lateinit var dispatcher: CoroutineDispatcher
private lateinit var scope: TestScope
@@ -58,37 +62,38 @@
@Mock private lateinit var mockDefaultClock: ClockController
@Mock private lateinit var mockThumbnail: Drawable
@Mock private lateinit var mockContentResolver: ContentResolver
+ @Mock private lateinit var mockPluginLifecycle: PluginLifecycleManager<ClockProviderPlugin>
private lateinit var fakeDefaultProvider: FakeClockPlugin
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry
companion object {
- private fun failFactory(): ClockController {
- fail("Unexpected call to createClock")
+ private fun failFactory(clockId: ClockId): ClockController {
+ fail("Unexpected call to createClock: $clockId")
return null!!
}
- private fun failThumbnail(): Drawable? {
- fail("Unexpected call to getThumbnail")
+ private fun failThumbnail(clockId: ClockId): Drawable? {
+ fail("Unexpected call to getThumbnail: $clockId")
return null
}
}
private class FakeClockPlugin : ClockProviderPlugin {
private val metadata = mutableListOf<ClockMetadata>()
- private val createCallbacks = mutableMapOf<ClockId, () -> ClockController>()
- private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()
+ private val createCallbacks = mutableMapOf<ClockId, (ClockId) -> ClockController>()
+ private val thumbnailCallbacks = mutableMapOf<ClockId, (ClockId) -> Drawable?>()
override fun getClocks() = metadata
override fun createClock(settings: ClockSettings): ClockController =
- createCallbacks[settings.clockId!!]!!()
- override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()
+ createCallbacks[settings.clockId!!]!!(settings.clockId!!)
+ override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id)
fun addClock(
id: ClockId,
name: String,
- create: () -> ClockController = ::failFactory,
- getThumbnail: () -> Drawable? = ::failThumbnail
+ create: (ClockId) -> ClockController = ::failFactory,
+ getThumbnail: (ClockId) -> Drawable? = ::failThumbnail
): FakeClockPlugin {
metadata.add(ClockMetadata(id, name))
createCallbacks[id] = create
@@ -99,7 +104,8 @@
@Before
fun setUp() {
- dispatcher = StandardTestDispatcher()
+ scheduler = TestCoroutineScheduler()
+ dispatcher = StandardTestDispatcher(scheduler)
scope = TestScope(dispatcher)
fakeDefaultProvider = FakeClockPlugin()
@@ -116,6 +122,8 @@
isEnabled = true,
handleAllUsers = true,
defaultClockProvider = fakeDefaultProvider,
+ keepAllLoaded = true,
+ subTag = "Test",
) {
override fun querySettings() { }
override fun applySettings(value: ClockSettings?) {
@@ -142,8 +150,8 @@
.addClock("clock_3", "clock 3")
.addClock("clock_4", "clock 4")
- pluginListener.onPluginConnected(plugin1, mockContext)
- pluginListener.onPluginConnected(plugin2, mockContext)
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
val list = registry.getClocks()
assertEquals(
list,
@@ -165,16 +173,18 @@
@Test
fun clockIdConflict_ErrorWithoutCrash() {
+ val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
+ val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin2 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
- pluginListener.onPluginConnected(plugin1, mockContext)
- pluginListener.onPluginConnected(plugin2, mockContext)
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
val list = registry.getClocks()
assertEquals(
list,
@@ -202,8 +212,8 @@
.addClock("clock_4", "clock 4")
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginConnected(plugin1, mockContext)
- pluginListener.onPluginConnected(plugin2, mockContext)
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
val clock = registry.createCurrentClock()
assertEquals(mockClock, clock)
@@ -220,9 +230,9 @@
.addClock("clock_4", "clock 4")
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginConnected(plugin1, mockContext)
- pluginListener.onPluginConnected(plugin2, mockContext)
- pluginListener.onPluginDisconnected(plugin2)
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle)
val clock = registry.createCurrentClock()
assertEquals(clock, mockDefaultClock)
@@ -230,15 +240,16 @@
@Test
fun pluginRemoved_clockAndListChanged() {
+ val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
-
var changeCallCount = 0
var listChangeCallCount = 0
registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener {
@@ -247,23 +258,38 @@
})
registry.applySettings(ClockSettings("clock_3", null))
- assertEquals(0, changeCallCount)
+ scheduler.runCurrent()
+ assertEquals(1, changeCallCount)
assertEquals(0, listChangeCallCount)
- pluginListener.onPluginConnected(plugin1, mockContext)
- assertEquals(0, changeCallCount)
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+ scheduler.runCurrent()
+ assertEquals(1, changeCallCount)
assertEquals(1, listChangeCallCount)
- pluginListener.onPluginConnected(plugin2, mockContext)
- assertEquals(1, changeCallCount)
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+ scheduler.runCurrent()
+ assertEquals(2, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginDisconnected(plugin1)
- assertEquals(1, changeCallCount)
+ pluginListener.onPluginUnloaded(plugin1, mockPluginLifecycle1)
+ scheduler.runCurrent()
+ assertEquals(2, changeCallCount)
+ assertEquals(2, listChangeCallCount)
+
+ pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle2)
+ scheduler.runCurrent()
+ assertEquals(3, changeCallCount)
+ assertEquals(2, listChangeCallCount)
+
+ pluginListener.onPluginDetached(mockPluginLifecycle1)
+ scheduler.runCurrent()
+ assertEquals(3, changeCallCount)
assertEquals(3, listChangeCallCount)
- pluginListener.onPluginDisconnected(plugin2)
- assertEquals(2, changeCallCount)
+ pluginListener.onPluginDetached(mockPluginLifecycle2)
+ scheduler.runCurrent()
+ assertEquals(3, changeCallCount)
assertEquals(4, listChangeCallCount)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java
index 05280fa..c39b29f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java
@@ -79,11 +79,11 @@
private PluginInstance<TestPlugin> mPluginInstance;
private PluginInstance.Factory mPluginInstanceFactory = new PluginInstance.Factory(
this.getClass().getClassLoader(),
- new PluginInstance.InstanceFactory<>(), new PluginInstance.VersionChecker(),
+ new PluginInstance.InstanceFactory<>(), new PluginInstance.VersionCheckerImpl(),
Collections.emptyList(), false) {
@Override
public <T extends Plugin> PluginInstance<T> create(Context context, ApplicationInfo appInfo,
- ComponentName componentName, Class<T> pluginClass) {
+ ComponentName componentName, Class<T> pluginClass, PluginListener<T> listener) {
return (PluginInstance<T>) mPluginInstance;
}
};
@@ -128,7 +128,7 @@
createPlugin();
// Verify startup lifecycle
- verify(mPluginInstance).onCreate(mContext, mMockListener);
+ verify(mPluginInstance).onCreate();
}
@Test
@@ -140,7 +140,7 @@
mFakeExecutor.runAllReady();
// Verify shutdown lifecycle
- verify(mPluginInstance).onDestroy(mMockListener);
+ verify(mPluginInstance).onDestroy();
}
@Test
@@ -152,9 +152,9 @@
mFakeExecutor.runAllReady();
// Verify the old one was destroyed.
- verify(mPluginInstance).onDestroy(mMockListener);
+ verify(mPluginInstance).onDestroy();
verify(mPluginInstance, Mockito.times(2))
- .onCreate(mContext, mMockListener);
+ .onCreate();
}
@Test
@@ -188,7 +188,7 @@
mFakeExecutor.runAllReady();
// Verify startup lifecycle
- verify(mPluginInstance).onCreate(mContext, mMockListener);
+ verify(mPluginInstance).onCreate();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index bb9a1e9..d5e904c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -16,11 +16,9 @@
package com.android.systemui.shared.plugins;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static junit.framework.Assert.assertNull;
import android.content.ComponentName;
import android.content.Context;
@@ -31,6 +29,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginLifecycleManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.annotations.Requires;
@@ -38,46 +37,64 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import java.lang.ref.WeakReference;
import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PluginInstanceTest extends SysuiTestCase {
private static final String PRIVILEGED_PACKAGE = "com.android.systemui.plugins";
-
- @Mock
- private TestPluginImpl mMockPlugin;
- @Mock
- private PluginListener<TestPlugin> mMockListener;
- @Mock
- private VersionInfo mVersionInfo;
- ComponentName mTestPluginComponentName =
+ private static final ComponentName TEST_PLUGIN_COMPONENT_NAME =
new ComponentName(PRIVILEGED_PACKAGE, TestPluginImpl.class.getName());
+
+ private FakeListener mPluginListener;
+ private VersionInfo mVersionInfo;
+ private VersionInfo.InvalidVersionException mVersionException;
+ private PluginInstance.VersionChecker mVersionChecker;
+
+ private RefCounter mCounter;
private PluginInstance<TestPlugin> mPluginInstance;
private PluginInstance.Factory mPluginInstanceFactory;
-
private ApplicationInfo mAppInfo;
- private Context mPluginContext;
- @Mock
- private PluginInstance.VersionChecker mVersionChecker;
+
+ // Because we're testing memory in this file, we must be careful not to assert the target
+ // objects, or capture them via mockito if we expect the garbage collector to later free them.
+ // Both JUnit and Mockito will save references and prevent these objects from being cleaned up.
+ private WeakReference<TestPluginImpl> mPlugin;
+ private WeakReference<Context> mPluginContext;
@Before
public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
+ mCounter = new RefCounter();
mAppInfo = mContext.getApplicationInfo();
- mAppInfo.packageName = mTestPluginComponentName.getPackageName();
- when(mVersionChecker.checkVersion(any(), any(), any())).thenReturn(mVersionInfo);
+ mAppInfo.packageName = TEST_PLUGIN_COMPONENT_NAME.getPackageName();
+ mPluginListener = new FakeListener();
+ mVersionInfo = new VersionInfo();
+ mVersionChecker = new PluginInstance.VersionChecker() {
+ @Override
+ public <T extends Plugin> VersionInfo checkVersion(
+ Class<T> instanceClass,
+ Class<T> pluginClass,
+ Plugin plugin
+ ) {
+ if (mVersionException != null) {
+ throw mVersionException;
+ }
+ return mVersionInfo;
+ }
+ };
mPluginInstanceFactory = new PluginInstance.Factory(
this.getClass().getClassLoader(),
new PluginInstance.InstanceFactory<TestPlugin>() {
@Override
TestPlugin create(Class cls) {
- return mMockPlugin;
+ TestPluginImpl plugin = new TestPluginImpl(mCounter);
+ mPlugin = new WeakReference<>(plugin);
+ return plugin;
}
},
mVersionChecker,
@@ -85,8 +102,9 @@
false);
mPluginInstance = mPluginInstanceFactory.create(
- mContext, mAppInfo, mTestPluginComponentName, TestPlugin.class);
- mPluginContext = mPluginInstance.getPluginContext();
+ mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME,
+ TestPlugin.class, mPluginListener);
+ mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext());
}
@Test
@@ -96,29 +114,51 @@
@Test(expected = VersionInfo.InvalidVersionException.class)
public void testIncorrectVersion() throws Exception {
-
ComponentName wrongVersionTestPluginComponentName =
new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName());
- when(mVersionChecker.checkVersion(any(), any(), any())).thenThrow(
- new VersionInfo.InvalidVersionException("test", true));
+ mVersionException = new VersionInfo.InvalidVersionException("test", true);
mPluginInstanceFactory.create(
- mContext, mAppInfo, wrongVersionTestPluginComponentName, TestPlugin.class);
+ mContext, mAppInfo, wrongVersionTestPluginComponentName,
+ TestPlugin.class, mPluginListener);
}
@Test
public void testOnCreate() {
- mPluginInstance.onCreate(mContext, mMockListener);
- verify(mMockPlugin).onCreate(mContext, mPluginContext);
- verify(mMockListener).onPluginConnected(mMockPlugin, mPluginContext);
+ mPluginInstance.onCreate();
+ assertEquals(1, mPluginListener.mAttachedCount);
+ assertEquals(1, mPluginListener.mLoadCount);
+ assertEquals(mPlugin.get(), mPluginInstance.getPlugin());
+ assertInstances(1, 1);
}
@Test
public void testOnDestroy() {
- mPluginInstance.onDestroy(mMockListener);
- verify(mMockListener).onPluginDisconnected(mMockPlugin);
- verify(mMockPlugin).onDestroy();
+ mPluginInstance.onDestroy();
+ assertEquals(1, mPluginListener.mDetachedCount);
+ assertEquals(1, mPluginListener.mUnloadCount);
+ assertNull(mPluginInstance.getPlugin());
+ assertInstances(0, -1); // Destroyed but never created
+ }
+
+ @Test
+ public void testOnRepeatedlyLoadUnload_PluginFreed() {
+ mPluginInstance.onCreate();
+ mPluginInstance.loadPlugin();
+ assertInstances(1, 1);
+
+ mPluginInstance.unloadPlugin();
+ assertNull(mPluginInstance.getPlugin());
+ assertInstances(0, 0);
+
+ mPluginInstance.loadPlugin();
+ assertInstances(1, 1);
+
+ mPluginInstance.unloadPlugin();
+ mPluginInstance.onDestroy();
+ assertNull(mPluginInstance.getPlugin());
+ assertInstances(0, 0);
}
// This target class doesn't matter, it just needs to have a Requires to hit the flow where
@@ -129,10 +169,103 @@
String ACTION = "testAction";
}
+ public void assertInstances(Integer allocated, Integer created) {
+ // Run the garbage collector to finalize and deallocate outstanding
+ // instances. Since the GC doesn't always appear to want to run
+ // completely when we ask, we ask it 10 times in a short loop.
+ for (int i = 0; i < 10; i++) {
+ System.runFinalization();
+ System.gc();
+ }
+
+ mCounter.assertInstances(allocated, created);
+ }
+
+ public static class RefCounter {
+ public final AtomicInteger mAllocatedInstances = new AtomicInteger();
+ public final AtomicInteger mCreatedInstances = new AtomicInteger();
+
+ public void assertInstances(Integer allocated, Integer created) {
+ if (allocated != null) {
+ assertEquals(allocated.intValue(), mAllocatedInstances.get());
+ }
+ if (created != null) {
+ assertEquals(created.intValue(), mCreatedInstances.get());
+ }
+ }
+ }
+
@Requires(target = TestPlugin.class, version = TestPlugin.VERSION)
public static class TestPluginImpl implements TestPlugin {
+ public final RefCounter mCounter;
+ public TestPluginImpl(RefCounter counter) {
+ mCounter = counter;
+ mCounter.mAllocatedInstances.getAndIncrement();
+ }
+
+ @Override
+ public void finalize() {
+ mCounter.mAllocatedInstances.getAndDecrement();
+ }
+
@Override
public void onCreate(Context sysuiContext, Context pluginContext) {
+ mCounter.mCreatedInstances.getAndIncrement();
+ }
+
+ @Override
+ public void onDestroy() {
+ mCounter.mCreatedInstances.getAndDecrement();
+ }
+ }
+
+ public class FakeListener implements PluginListener<TestPlugin> {
+ public int mAttachedCount = 0;
+ public int mDetachedCount = 0;
+ public int mLoadCount = 0;
+ public int mUnloadCount = 0;
+
+ @Override
+ public void onPluginAttached(PluginLifecycleManager<TestPlugin> manager) {
+ mAttachedCount++;
+ assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ }
+
+ @Override
+ public void onPluginDetached(PluginLifecycleManager<TestPlugin> manager) {
+ mDetachedCount++;
+ assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ }
+
+ @Override
+ public void onPluginLoaded(
+ TestPlugin plugin,
+ Context pluginContext,
+ PluginLifecycleManager<TestPlugin> manager
+ ) {
+ mLoadCount++;
+ TestPlugin expectedPlugin = PluginInstanceTest.this.mPlugin.get();
+ if (expectedPlugin != null) {
+ assertEquals(expectedPlugin, plugin);
+ }
+ Context expectedContext = PluginInstanceTest.this.mPluginContext.get();
+ if (expectedContext != null) {
+ assertEquals(expectedContext, pluginContext);
+ }
+ assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ }
+
+ @Override
+ public void onPluginUnloaded(
+ TestPlugin plugin,
+ PluginLifecycleManager<TestPlugin> manager
+ ) {
+ mUnloadCount++;
+ TestPlugin expectedPlugin = PluginInstanceTest.this.mPlugin.get();
+ if (expectedPlugin != null) {
+ assertEquals(expectedPlugin, plugin);
+ }
+ assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index e6f272b..3327e42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -167,4 +167,13 @@
controller.setIsDreaming(false)
verify(listener).onDreamingChanged(false)
}
+
+ @Test
+ fun testSetDreamState_getterReturnsCurrentState() {
+ controller.setIsDreaming(true)
+ assertTrue(controller.isDreaming())
+
+ controller.setIsDreaming(false)
+ assertFalse(controller.isDreaming())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 8acf507..653b0c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -54,7 +54,6 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.service.dreams.IDreamManager;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -94,8 +93,6 @@
@Mock
PowerManager mPowerManager;
@Mock
- IDreamManager mDreamManager;
- @Mock
AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock
StatusBarStateController mStatusBarStateController;
@@ -133,7 +130,6 @@
new NotificationInterruptStateProviderImpl(
mContext.getContentResolver(),
mPowerManager,
- mDreamManager,
mAmbientDisplayConfiguration,
mBatteryController,
mStatusBarStateController,
@@ -157,7 +153,7 @@
when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mPowerManager.isScreenOn()).thenReturn(true);
}
@@ -359,7 +355,7 @@
// Also not in use if screen is on but we're showing screen saver / "dreaming"
when(mPowerManager.isDeviceIdleMode()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(true);
+ when(mStatusBarStateController.isDreaming()).thenReturn(true);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
@@ -539,7 +535,7 @@
public void testShouldNotFullScreen_notPendingIntent() throws RemoteException {
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
when(mPowerManager.isInteractive()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -558,7 +554,7 @@
.setSuppressedVisualEffects(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT)
.build();
when(mPowerManager.isInteractive()).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -577,7 +573,7 @@
.setSuppressedVisualEffects(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT)
.build();
when(mPowerManager.isInteractive()).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -599,7 +595,7 @@
public void testShouldNotFullScreen_notHighImportance() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -621,7 +617,7 @@
public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true);
when(mPowerManager.isInteractive()).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(true);
+ when(mStatusBarStateController.isDreaming()).thenReturn(true);
when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -651,7 +647,7 @@
public void testShouldFullScreen_notInteractive() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -673,7 +669,7 @@
public void testShouldFullScreen_isDreaming() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(true);
+ when(mStatusBarStateController.isDreaming()).thenReturn(true);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -695,7 +691,7 @@
public void testShouldFullScreen_onKeyguard() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -718,7 +714,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
@@ -735,7 +731,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
@@ -754,7 +750,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -775,7 +771,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -800,7 +796,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -821,7 +817,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -846,7 +842,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(false);
@@ -892,7 +888,7 @@
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 031c17f..7db2197 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -347,7 +347,6 @@
mNotificationInterruptStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mPowerManager,
- mDreamManager,
mAmbientDisplayConfiguration,
mStatusBarStateController,
mKeyguardStateController,
@@ -730,7 +729,7 @@
public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -753,7 +752,7 @@
public void testShouldHeadsUp_suppressedGroupSummary() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -776,7 +775,7 @@
public void testShouldHeadsUp_suppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -797,7 +796,7 @@
public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception {
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
- when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -1400,7 +1399,6 @@
TestableNotificationInterruptStateProviderImpl(
ContentResolver contentResolver,
PowerManager powerManager,
- IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
StatusBarStateController controller,
KeyguardStateController keyguardStateController,
@@ -1415,7 +1413,6 @@
super(
contentResolver,
powerManager,
- dreamManager,
ambientDisplayConfiguration,
batteryController,
controller,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 12e58c9..ee4e00b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -343,7 +343,6 @@
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mock(PowerManager.class),
- mock(IDreamManager.class),
mock(AmbientDisplayConfiguration.class),
mock(StatusBarStateController.class),
mock(KeyguardStateController.class),
@@ -1246,6 +1245,24 @@
// Show the menu
stackView.showManageMenu(true);
assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
+ assertTrue(stackView.isManageMenuSettingsVisible());
+ }
+
+ @Test
+ public void testShowManageMenuChangesSysuiState_appBubble() {
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0);
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Expand the stack
+ BubbleStackView stackView = mBubbleController.getStackView();
+ mBubbleData.setExpanded(true);
+ assertStackExpanded();
+ assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+
+ // Show the menu
+ stackView.showManageMenu(true);
+ assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
+ assertFalse(stackView.isManageMenuSettingsVisible());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index ceee0bc..4e14bbf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -20,7 +20,6 @@
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
-import android.service.dreams.IDreamManager;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -39,7 +38,6 @@
TestableNotificationInterruptStateProviderImpl(
ContentResolver contentResolver,
PowerManager powerManager,
- IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
StatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
@@ -53,7 +51,6 @@
UserTracker userTracker) {
super(contentResolver,
powerManager,
- dreamManager,
ambientDisplayConfiguration,
batteryController,
statusBarStateController,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7550196..b4e75e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9351,7 +9351,9 @@
String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
- int lines = Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
+ int lines = Build.IS_USER
+ ? 0
+ : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
int dropboxMaxSize = Settings.Global.getInt(
mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
int maxDataFileSize = dropboxMaxSize - sb.length()
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e403861..3e86c45 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1372,8 +1372,8 @@
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
if (mMonitorRotation) {
RotationHelper.init(mContext, mAudioHandler,
- rotationParam -> onRotationUpdate(rotationParam),
- foldParam -> onFoldUpdate(foldParam));
+ rotation -> onRotationUpdate(rotation),
+ foldState -> onFoldStateUpdate(foldState));
}
intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
@@ -1515,16 +1515,20 @@
//-----------------------------------------------------------------
// rotation/fold updates coming from RotationHelper
- void onRotationUpdate(String rotationParameter) {
+ void onRotationUpdate(Integer rotation) {
+ mSpatializerHelper.setDisplayOrientation((float) (rotation * Math.PI / 180.));
// use REPLACE as only the last rotation matters
+ final String rotationParameter = "rotation=" + rotation;
sendMsg(mAudioHandler, MSG_ROTATION_UPDATE, SENDMSG_REPLACE, /*arg1*/ 0, /*arg2*/ 0,
/*obj*/ rotationParameter, /*delay*/ 0);
}
- void onFoldUpdate(String foldParameter) {
+ void onFoldStateUpdate(Boolean foldState) {
+ mSpatializerHelper.setFoldState(foldState);
// use REPLACE as only the last fold state matters
+ final String foldStateParameter = "device_folded=" + (foldState ? "on" : "off");
sendMsg(mAudioHandler, MSG_FOLD_UPDATE, SENDMSG_REPLACE, /*arg1*/ 0, /*arg2*/ 0,
- /*obj*/ foldParameter, /*delay*/ 0);
+ /*obj*/ foldStateParameter, /*delay*/ 0);
}
//-----------------------------------------------------------------
@@ -1740,6 +1744,9 @@
mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
mSoundDoseHelper.reset();
+ // Restore rotation information.
+ RotationHelper.forceUpdate();
+
onIndicateSystemReady();
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
index 5cdf58b..394e4af 100644
--- a/services/core/java/com/android/server/audio/RotationHelper.java
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -55,14 +55,14 @@
private static AudioDisplayListener sDisplayListener;
private static FoldStateListener sFoldStateListener;
/** callback to send rotation updates to AudioSystem */
- private static Consumer<String> sRotationUpdateCb;
+ private static Consumer<Integer> sRotationCallback;
/** callback to send folded state updates to AudioSystem */
- private static Consumer<String> sFoldUpdateCb;
+ private static Consumer<Boolean> sFoldStateCallback;
private static final Object sRotationLock = new Object();
private static final Object sFoldStateLock = new Object();
- private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
- private static boolean sDeviceFold = true; // R/W synchronized on sFoldStateLock
+ private static Integer sRotation = null; // R/W synchronized on sRotationLock
+ private static Boolean sFoldState = null; // R/W synchronized on sFoldStateLock
private static Context sContext;
private static Handler sHandler;
@@ -73,15 +73,15 @@
* - sContext != null
*/
static void init(Context context, Handler handler,
- Consumer<String> rotationUpdateCb, Consumer<String> foldUpdateCb) {
+ Consumer<Integer> rotationCallback, Consumer<Boolean> foldStateCallback) {
if (context == null) {
throw new IllegalArgumentException("Invalid null context");
}
sContext = context;
sHandler = handler;
sDisplayListener = new AudioDisplayListener();
- sRotationUpdateCb = rotationUpdateCb;
- sFoldUpdateCb = foldUpdateCb;
+ sRotationCallback = rotationCallback;
+ sFoldStateCallback = foldStateCallback;
enable();
}
@@ -112,9 +112,9 @@
int newRotation = DisplayManagerGlobal.getInstance()
.getDisplayInfo(Display.DEFAULT_DISPLAY).rotation;
synchronized(sRotationLock) {
- if (newRotation != sDeviceRotation) {
- sDeviceRotation = newRotation;
- publishRotation(sDeviceRotation);
+ if (sRotation == null || sRotation != newRotation) {
+ sRotation = newRotation;
+ publishRotation(sRotation);
}
}
}
@@ -123,43 +123,52 @@
if (DEBUG_ROTATION) {
Log.i(TAG, "publishing device rotation =" + rotation + " (x90deg)");
}
- String rotationParam;
+ int rotationDegrees;
switch (rotation) {
case Surface.ROTATION_0:
- rotationParam = "rotation=0";
+ rotationDegrees = 0;
break;
case Surface.ROTATION_90:
- rotationParam = "rotation=90";
+ rotationDegrees = 90;
break;
case Surface.ROTATION_180:
- rotationParam = "rotation=180";
+ rotationDegrees = 180;
break;
case Surface.ROTATION_270:
- rotationParam = "rotation=270";
+ rotationDegrees = 270;
break;
default:
Log.e(TAG, "Unknown device rotation");
- rotationParam = null;
+ rotationDegrees = -1;
}
- if (rotationParam != null) {
- sRotationUpdateCb.accept(rotationParam);
+ if (rotationDegrees != -1) {
+ sRotationCallback.accept(rotationDegrees);
}
}
/**
* publish the change of device folded state if any.
*/
- static void updateFoldState(boolean newFolded) {
+ static void updateFoldState(boolean foldState) {
synchronized (sFoldStateLock) {
- if (sDeviceFold != newFolded) {
- sDeviceFold = newFolded;
- String foldParam;
- if (newFolded) {
- foldParam = "device_folded=on";
- } else {
- foldParam = "device_folded=off";
- }
- sFoldUpdateCb.accept(foldParam);
+ if (sFoldState == null || sFoldState != foldState) {
+ sFoldState = foldState;
+ sFoldStateCallback.accept(foldState);
+ }
+ }
+ }
+
+ /**
+ * forceUpdate is called when audioserver restarts.
+ */
+ static void forceUpdate() {
+ synchronized (sRotationLock) {
+ sRotation = null;
+ }
+ updateOrientation(); // We will get at least one orientation update now.
+ synchronized (sFoldStateLock) {
+ if (sFoldState != null) {
+ sFoldStateCallback.accept(sFoldState);
}
}
}
@@ -185,4 +194,4 @@
updateOrientation();
}
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 3ea4f4f..8f54e45 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -1066,7 +1066,7 @@
if (transform.length != 6) {
throw new IllegalArgumentException("invalid array size" + transform.length);
}
- if (!checkSpatForHeadTracking("setGlobalTransform")) {
+ if (!checkSpatializerForHeadTracking("setGlobalTransform")) {
return;
}
try {
@@ -1077,7 +1077,7 @@
}
synchronized void recenterHeadTracker() {
- if (!checkSpatForHeadTracking("recenterHeadTracker")) {
+ if (!checkSpatializerForHeadTracking("recenterHeadTracker")) {
return;
}
try {
@@ -1087,8 +1087,30 @@
}
}
+ synchronized void setDisplayOrientation(float displayOrientation) {
+ if (!checkSpatializer("setDisplayOrientation")) {
+ return;
+ }
+ try {
+ mSpat.setDisplayOrientation(displayOrientation);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setDisplayOrientation", e);
+ }
+ }
+
+ synchronized void setFoldState(boolean folded) {
+ if (!checkSpatializer("setFoldState")) {
+ return;
+ }
+ try {
+ mSpat.setFoldState(folded);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setFoldState", e);
+ }
+ }
+
synchronized void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
- if (!checkSpatForHeadTracking("setDesiredHeadTrackingMode")) {
+ if (!checkSpatializerForHeadTracking("setDesiredHeadTrackingMode")) {
return;
}
if (mode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
@@ -1186,7 +1208,7 @@
return mHeadTrackerAvailable;
}
- private boolean checkSpatForHeadTracking(String funcName) {
+ private boolean checkSpatializer(String funcName) {
switch (mState) {
case STATE_UNINITIALIZED:
case STATE_NOT_SUPPORTED:
@@ -1197,14 +1219,18 @@
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
// try to recover by resetting the native spatializer state
- Log.e(TAG, "checkSpatForHeadTracking(): "
- + "native spatializer should not be null in state: " + mState);
+ Log.e(TAG, "checkSpatializer(): called from " + funcName
+ + "(), native spatializer should not be null in state: " + mState);
postReset();
return false;
}
break;
}
- return mIsHeadTrackingSupported;
+ return true;
+ }
+
+ private boolean checkSpatializerForHeadTracking(String funcName) {
+ return checkSpatializer(funcName) && mIsHeadTrackingSupported;
}
private void dispatchActualHeadTrackingMode(int newMode) {
diff --git a/services/core/java/com/android/server/biometrics/TEST_MAPPING b/services/core/java/com/android/server/biometrics/TEST_MAPPING
index 8b80674..daca00b 100644
--- a/services/core/java/com/android/server/biometrics/TEST_MAPPING
+++ b/services/core/java/com/android/server/biometrics/TEST_MAPPING
@@ -6,5 +6,24 @@
{
"name": "CtsBiometricsHostTestCases"
}
- ]
-}
\ No newline at end of file
+ ],
+ "ironwood-postsubmit": [
+ {
+ "name": "BiometricsE2eTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.IwTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.platform.test.scenario.biometrics"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
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 dc00ffc..01ffc7e 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
@@ -314,7 +314,8 @@
final FingerprintSensorPropertiesInternal sensorProps =
provider.second.getSensorProperties(options.getSensorId());
if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
- && sensorProps != null && sensorProps.isAnyUdfpsType()) {
+ && sensorProps != null && (sensorProps.isAnyUdfpsType()
+ || sensorProps.isAnySidefpsType())) {
try {
return authenticateWithPrompt(operationId, sensorProps, callingUid,
callingUserId, receiver, opPackageName,
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 4e3de3c..e8af840 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -243,11 +243,13 @@
public List<CameraStreamStats> mStreamStats;
public String mUserTag;
public int mVideoStabilizationMode;
+ public final long mLogId;
private long mDurationOrStartTimeMs; // Either start time, or duration once completed
CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel,
- boolean isNdk, int action, int latencyMs, int operatingMode, boolean deviceError) {
+ boolean isNdk, int action, int latencyMs, int operatingMode, boolean deviceError,
+ long logId) {
mCameraId = cameraId;
mCameraFacing = facing;
mClientName = clientName;
@@ -259,6 +261,7 @@
mLatencyMs = latencyMs;
mOperatingMode = operatingMode;
mDeviceError = deviceError;
+ mLogId = logId;
}
public void markCompleted(int internalReconfigure, long requestCount,
@@ -840,7 +843,8 @@
+ ", deviceError " + e.mDeviceError
+ ", streamCount is " + streamCount
+ ", userTag is " + e.mUserTag
- + ", videoStabilizationMode " + e.mVideoStabilizationMode);
+ + ", videoStabilizationMode " + e.mVideoStabilizationMode
+ + ", logId " + e.mLogId);
}
// Convert from CameraStreamStats to CameraStreamProto
CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
@@ -900,7 +904,7 @@
MessageNano.toByteArray(streamProtos[2]),
MessageNano.toByteArray(streamProtos[3]),
MessageNano.toByteArray(streamProtos[4]),
- e.mUserTag, e.mVideoStabilizationMode);
+ e.mUserTag, e.mVideoStabilizationMode, e.mLogId);
}
}
@@ -1089,6 +1093,7 @@
List<CameraStreamStats> streamStats = cameraState.getStreamStats();
String userTag = cameraState.getUserTag();
int videoStabilizationMode = cameraState.getVideoStabilizationMode();
+ long logId = cameraState.getLogId();
synchronized(mLock) {
// Update active camera list and notify NFC if necessary
boolean wasEmpty = mActiveCameraUsage.isEmpty();
@@ -1110,7 +1115,7 @@
CameraUsageEvent openEvent = new CameraUsageEvent(
cameraId, facing, clientName, apiLevel, isNdk,
FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN,
- latencyMs, sessionType, deviceError);
+ latencyMs, sessionType, deviceError, logId);
mCameraUsageHistory.add(openEvent);
break;
case CameraSessionStats.CAMERA_STATE_ACTIVE:
@@ -1137,7 +1142,7 @@
CameraUsageEvent newEvent = new CameraUsageEvent(
cameraId, facing, clientName, apiLevel, isNdk,
FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__SESSION,
- latencyMs, sessionType, deviceError);
+ latencyMs, sessionType, deviceError, logId);
CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
if (oldEvent != null) {
Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
@@ -1181,7 +1186,7 @@
CameraUsageEvent closeEvent = new CameraUsageEvent(
cameraId, facing, clientName, apiLevel, isNdk,
FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE,
- latencyMs, sessionType, deviceError);
+ latencyMs, sessionType, deviceError, logId);
mCameraUsageHistory.add(closeEvent);
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index a589313..00af224 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -76,7 +76,13 @@
* This flag indicates that the corresponding state should be disabled when the device is
* overheating and reaching the critical status.
*/
- public static final int FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
+ public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
+
+ /**
+ * This flag indicates that the corresponding state should be disabled when power save mode
+ * is enabled.
+ */
+ public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
/** @hide */
@IntDef(prefix = {"FLAG_"}, flag = true, value = {
@@ -84,7 +90,8 @@
FLAG_APP_INACCESSIBLE,
FLAG_EMULATED_ONLY,
FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
- FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL
+ FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
+ FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceStateFlags {}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 43ee5e2..9645690 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -25,6 +25,7 @@
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
+import static com.android.server.devicestate.OverrideRequestController.FLAG_POWER_SAVE_ENABLED;
import static com.android.server.devicestate.OverrideRequestController.FLAG_THERMAL_CRITICAL;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -609,7 +610,8 @@
@GuardedBy("mLock")
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
- @OverrideRequestController.RequestStatus int status, int flags) {
+ @OverrideRequestController.RequestStatus int status,
+ @OverrideRequestController.StatusChangedFlag int flags) {
if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
switch (status) {
case STATUS_ACTIVE:
@@ -641,6 +643,10 @@
mDeviceStateNotificationController
.showThermalCriticalNotificationIfNeeded(
request.getRequestedState());
+ } else if ((flags & FLAG_POWER_SAVE_ENABLED) == FLAG_POWER_SAVE_ENABLED) {
+ mDeviceStateNotificationController
+ .showPowerSaveNotificationIfNeeded(
+ request.getRequestedState());
}
}
break;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index 9008740..ab261ac 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -16,6 +16,8 @@
package com.android.server.devicestate;
+import static android.provider.Settings.ACTION_BATTERY_SAVER_SETTINGS;
+
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -101,10 +103,16 @@
}
String requesterApplicationLabel = getApplicationLabel(requestingAppUid);
if (requesterApplicationLabel != null) {
+ final Intent intent = new Intent(INTENT_ACTION_CANCEL_STATE)
+ .setPackage(mContext.getPackageName());
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
showNotification(
info.name, info.activeNotificationTitle,
String.format(info.activeNotificationContent, requesterApplicationLabel),
- true /* ongoing */, R.drawable.ic_dual_screen
+ true /* ongoing */, R.drawable.ic_dual_screen,
+ pendingIntent,
+ mContext.getString(R.string.device_state_notification_turn_off_button)
);
} else {
Slog.e(TAG, "Cannot determine the requesting app name when showing state active "
@@ -126,7 +134,33 @@
showNotification(
info.name, info.thermalCriticalNotificationTitle,
info.thermalCriticalNotificationContent, false /* ongoing */,
- R.drawable.ic_thermostat
+ R.drawable.ic_thermostat,
+ null /* pendingIntent */,
+ null /* actionText */
+ );
+ }
+
+ /**
+ * Displays the notification indicating that the device state is canceled due to power
+ * save mode being enabled. Does nothing if the state does not have a power save mode
+ * notification.
+ *
+ * @param state the identifier of the device state being canceled.
+ */
+ void showPowerSaveNotificationIfNeeded(int state) {
+ NotificationInfo info = mNotificationInfos.get(state);
+ if (info == null || !info.hasPowerSaveModeNotification()) {
+ return;
+ }
+ final Intent intent = new Intent(ACTION_BATTERY_SAVER_SETTINGS);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+ showNotification(
+ info.name, info.powerSaveModeNotificationTitle,
+ info.powerSaveModeNotificationContent, false /* ongoing */,
+ R.drawable.ic_thermostat,
+ pendingIntent,
+ mContext.getString(R.string.device_state_notification_settings_button)
);
}
@@ -161,7 +195,8 @@
*/
private void showNotification(
@NonNull String name, @NonNull String title, @NonNull String content, boolean ongoing,
- @DrawableRes int iconRes) {
+ @DrawableRes int iconRes,
+ @Nullable PendingIntent pendingIntent, @Nullable String actionText) {
final NotificationChannel channel = new NotificationChannel(
CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH);
final Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID)
@@ -173,14 +208,10 @@
.setOngoing(ongoing)
.setCategory(Notification.CATEGORY_SYSTEM);
- if (ongoing) {
- final Intent intent = new Intent(INTENT_ACTION_CANCEL_STATE)
- .setPackage(mContext.getPackageName());
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(
- mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+ if (pendingIntent != null && actionText != null) {
final Notification.Action action = new Notification.Action.Builder(
null /* icon */,
- mContext.getString(R.string.device_state_notification_turn_off_button),
+ actionText,
pendingIntent)
.build();
builder.addAction(action);
@@ -215,12 +246,21 @@
final String[] thermalCriticalNotificationContents =
context.getResources().getStringArray(
R.array.device_state_notification_thermal_contents);
+ final String[] powerSaveModeNotificationTitles =
+ context.getResources().getStringArray(
+ R.array.device_state_notification_power_save_titles);
+ final String[] powerSaveModeNotificationContents =
+ context.getResources().getStringArray(
+ R.array.device_state_notification_power_save_contents);
+
if (stateIdentifiers.length != names.length
|| stateIdentifiers.length != activeNotificationTitles.length
|| stateIdentifiers.length != activeNotificationContents.length
|| stateIdentifiers.length != thermalCriticalNotificationTitles.length
|| stateIdentifiers.length != thermalCriticalNotificationContents.length
+ || stateIdentifiers.length != powerSaveModeNotificationTitles.length
+ || stateIdentifiers.length != powerSaveModeNotificationContents.length
) {
throw new IllegalStateException(
"The length of state identifiers and notification texts must match!");
@@ -237,7 +277,9 @@
new NotificationInfo(
names[i], activeNotificationTitles[i], activeNotificationContents[i],
thermalCriticalNotificationTitles[i],
- thermalCriticalNotificationContents[i])
+ thermalCriticalNotificationContents[i],
+ powerSaveModeNotificationTitles[i],
+ powerSaveModeNotificationContents[i])
);
}
@@ -272,16 +314,21 @@
public final String activeNotificationContent;
public final String thermalCriticalNotificationTitle;
public final String thermalCriticalNotificationContent;
+ public final String powerSaveModeNotificationTitle;
+ public final String powerSaveModeNotificationContent;
NotificationInfo(String name, String activeNotificationTitle,
String activeNotificationContent, String thermalCriticalNotificationTitle,
- String thermalCriticalNotificationContent) {
+ String thermalCriticalNotificationContent, String powerSaveModeNotificationTitle,
+ String powerSaveModeNotificationContent) {
this.name = name;
this.activeNotificationTitle = activeNotificationTitle;
this.activeNotificationContent = activeNotificationContent;
this.thermalCriticalNotificationTitle = thermalCriticalNotificationTitle;
this.thermalCriticalNotificationContent = thermalCriticalNotificationContent;
+ this.powerSaveModeNotificationTitle = powerSaveModeNotificationTitle;
+ this.powerSaveModeNotificationContent = powerSaveModeNotificationContent;
}
boolean hasActiveNotification() {
@@ -292,5 +339,10 @@
return thermalCriticalNotificationTitle != null
&& thermalCriticalNotificationTitle.length() > 0;
}
+
+ boolean hasPowerSaveModeNotification() {
+ return powerSaveModeNotificationTitle != null
+ && powerSaveModeNotificationTitle.length() > 0;
+ }
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index fecc13f..af33de0 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -52,11 +52,24 @@
*/
int SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL = 3;
+ /**
+ * Indicating that the supported device states have changed because power save mode was enabled.
+ */
+ int SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED = 4;
+
+ /**
+ * Indicating that the supported device states have changed because power save mode was
+ * disabled.
+ */
+ int SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED = 5;
+
@IntDef(prefix = { "SUPPORTED_DEVICE_STATES_CHANGED_" }, value = {
SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT,
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED,
SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL,
- SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
+ SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL,
+ SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED,
+ SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED
})
@Retention(RetentionPolicy.SOURCE)
@interface SupportedStatesUpdatedReason {}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 2ed4765..46f0bc0 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -64,6 +64,18 @@
*/
static final int FLAG_THERMAL_CRITICAL = 1 << 0;
+ /**
+ * A flag indicating that the status change was triggered by power save mode.
+ */
+ static final int FLAG_POWER_SAVE_ENABLED = 1 << 1;
+
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {
+ FLAG_THERMAL_CRITICAL,
+ FLAG_POWER_SAVE_ENABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface StatusChangedFlag {}
+
static String statusToString(@RequestStatus int status) {
switch (status) {
case STATUS_ACTIVE:
@@ -228,13 +240,18 @@
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
boolean isThermalCritical =
reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
+ boolean isPowerSaveEnabled =
+ reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
+ @StatusChangedFlag int flags = 0;
+ flags |= isThermalCritical ? FLAG_THERMAL_CRITICAL : 0;
+ flags |= isPowerSaveEnabled ? FLAG_POWER_SAVE_ENABLED : 0;
if (mBaseStateRequest != null && !contains(newSupportedStates,
mBaseStateRequest.getRequestedState())) {
- cancelCurrentBaseStateRequestLocked(isThermalCritical ? FLAG_THERMAL_CRITICAL : 0);
+ cancelCurrentBaseStateRequestLocked(flags);
}
if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
- cancelCurrentRequestLocked(isThermalCritical ? FLAG_THERMAL_CRITICAL : 0);
+ cancelCurrentRequestLocked(flags);
}
}
@@ -255,7 +272,8 @@
cancelRequestLocked(requestToCancel, 0 /* flags */);
}
- private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel, int flags) {
+ private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel,
+ @StatusChangedFlag int flags) {
mListener.onStatusChanged(requestToCancel, STATUS_CANCELED, flags);
}
@@ -267,7 +285,7 @@
cancelCurrentRequestLocked(0 /* flags */);
}
- private void cancelCurrentRequestLocked(int flags) {
+ private void cancelCurrentRequestLocked(@StatusChangedFlag int flags) {
if (mRequest == null) {
Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
return;
@@ -285,7 +303,7 @@
cancelCurrentBaseStateRequestLocked(0 /* flags */);
}
- private void cancelCurrentBaseStateRequestLocked(int flags) {
+ private void cancelCurrentBaseStateRequestLocked(@StatusChangedFlag int flags) {
if (mBaseStateRequest == null) {
Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
return;
@@ -312,6 +330,6 @@
* cancelled request.
*/
void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus,
- int flags);
+ @StatusChangedFlag int flags);
}
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index f873a1b..4d4a87e 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -65,6 +65,7 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.XmlUtils;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import libcore.io.Streams;
@@ -1226,9 +1227,15 @@
mContext.getSystemService(UserManager.class));
InputMethodManager inputMethodManager = Objects.requireNonNull(
mContext.getSystemService(InputMethodManager.class));
+ // Need to use InputMethodManagerInternal to call getEnabledInputMethodListAsUser()
+ // instead of using InputMethodManager which uses enforceCallingPermissions() that
+ // breaks when we are calling the method for work profile user ID since it doesn't check
+ // self permissions.
+ InputMethodManagerInternal inputMethodManagerInternal = InputMethodManagerInternal.get();
for (UserHandle userHandle : userManager.getUserHandles(true /* excludeDying */)) {
int userId = userHandle.getIdentifier();
- for (InputMethodInfo imeInfo : inputMethodManager.getEnabledInputMethodListAsUser(
+ for (InputMethodInfo imeInfo :
+ inputMethodManagerInternal.getEnabledInputMethodListAsUser(
userId)) {
for (InputMethodSubtype imeSubtype :
inputMethodManager.getEnabledInputMethodSubtypeList(
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 9f7ff31..0ae1e80 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -136,17 +136,16 @@
mWindowManagerInternal.showImePostLayout(windowToken, statsToken);
break;
case STATE_HIDE_IME:
- if (mService.mCurFocusedWindowClient != null) {
+ if (mService.hasAttachedClient()) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
// IMMS only knows of focused window, not the actual IME target.
// e.g. it isn't aware of any window that has both
// NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
- // Send it to window manager to hide IME from IME target window.
- // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
- // actual IME target.
+ // Send it to window manager to hide IME from the actual IME control target
+ // of the target display.
mWindowManagerInternal.hideIme(windowToken,
- mService.mCurFocusedWindowClient.mSelfReportedDisplayId, statsToken);
+ mService.getDisplayIdToShowImeLocked(), statsToken);
} else {
ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e32bf1b..b336b95 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2339,6 +2339,19 @@
}
}
+ /** {@code true} when a {@link ClientState} has attached from starting the input connection. */
+ @GuardedBy("ImfLock.class")
+ boolean hasAttachedClient() {
+ return mCurClient != null;
+ }
+
+ @VisibleForTesting
+ void setAttachedClientForTesting(@NonNull ClientState cs) {
+ synchronized (ImfLock.class) {
+ mCurClient = cs;
+ }
+ }
+
@GuardedBy("ImfLock.class")
void clearInputShownLocked() {
mVisibilityStateComputer.setInputShown(false);
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index 77cd673..a081dff 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -92,6 +92,8 @@
// Represents an HAL interface version. Instances of this class are created in the JNI layer
// and returned through native methods.
static class HalInterfaceVersion {
+ // mMajor being this value denotes AIDL HAL. In this case, mMinor denotes the AIDL version.
+ static final int AIDL_INTERFACE = 3;
final int mMajor;
final int mMinor;
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 6c4c829..041f11d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -21,6 +21,7 @@
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.location.GnssMeasurementRequest;
@@ -31,6 +32,7 @@
import android.stats.location.LocationStatsEnums;
import android.util.Log;
+import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
@@ -115,16 +117,6 @@
if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) {
return true;
}
- // The HAL doc does not specify if consecutive start() calls will be allowed.
- // Some vendors may ignore the 2nd start() call if stop() is not called.
- // Thus, here we always call stop() before calling start() to avoid being ignored.
- if (mGnssNative.stopMeasurementCollection()) {
- if (D) {
- Log.d(TAG, "stopping gnss measurements");
- }
- } else {
- Log.e(TAG, "error stopping gnss measurements");
- }
if (mGnssNative.startMeasurementCollection(request.isFullTracking(),
request.isCorrelationVectorOutputsEnabled(),
request.getIntervalMillis())) {
@@ -139,6 +131,28 @@
}
@Override
+ protected boolean reregisterWithService(GnssMeasurementRequest old,
+ GnssMeasurementRequest request,
+ @NonNull Collection<GnssListenerRegistration> registrations) {
+ if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) {
+ unregisterWithService();
+ return true;
+ }
+ HalInterfaceVersion halInterfaceVersion =
+ mGnssNative.getConfiguration().getHalInterfaceVersion();
+ boolean aidlV3Plus = halInterfaceVersion.mMajor == HalInterfaceVersion.AIDL_INTERFACE
+ && halInterfaceVersion.mMinor >= 3;
+ if (!aidlV3Plus) {
+ // The HAL doc does not specify if consecutive start() calls will be allowed.
+ // Some vendors may ignore the 2nd start() call if stop() is not called.
+ // Thus, here we always call stop() before calling start() to avoid being ignored.
+ // AIDL v3+ is free from this issue.
+ unregisterWithService();
+ }
+ return registerWithService(request, registrations);
+ }
+
+ @Override
protected void unregisterWithService() {
if (mGnssNative.stopMeasurementCollection()) {
if (D) {
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index c232b36..9748aba 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -41,6 +41,7 @@
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
@@ -499,7 +500,7 @@
String getInstallerPackageName(@NonNull String packageName, @UserIdInt int userId);
@Nullable
- InstallSourceInfo getInstallSourceInfo(@NonNull String packageName);
+ InstallSourceInfo getInstallSourceInfo(@NonNull String packageName, @UserIdInt int userId);
@PackageManager.EnabledState
int getApplicationEnabledSetting(@NonNull String packageName, @UserIdInt int userId);
@@ -519,14 +520,15 @@
* returns false.
*/
boolean isComponentEffectivelyEnabled(@NonNull ComponentInfo componentInfo,
- @UserIdInt int userId);
+ @NonNull UserHandle userHandle);
/**
* @return true if the runtime app user enabled state and the install-time app manifest enabled
* state are both effectively enabled for the given app. Or if the app cannot be found,
* returns false.
*/
- boolean isApplicationEffectivelyEnabled(@NonNull String packageName, @UserIdInt int userId);
+ boolean isApplicationEffectivelyEnabled(@NonNull String packageName,
+ @NonNull UserHandle userHandle);
@Nullable
KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 5984360..acd4a96 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4982,9 +4982,11 @@
@Override
@Nullable
- public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName) {
+ public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName,
+ @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(callingUid);
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "getInstallSourceInfo");
String installerPackageName;
String initiatingPackageName;
@@ -5129,9 +5131,10 @@
@Override
public boolean isComponentEffectivelyEnabled(@NonNull ComponentInfo componentInfo,
- @UserIdInt int userId) {
+ @NonNull UserHandle userHandle) {
try {
String packageName = componentInfo.packageName;
+ int userId = userHandle.getIdentifier();
int appEnabledSetting =
mSettings.getApplicationEnabledSetting(packageName, userId);
if (appEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
@@ -5154,9 +5157,10 @@
@Override
public boolean isApplicationEffectivelyEnabled(@NonNull String packageName,
- @UserIdInt int userId) {
+ @NonNull UserHandle userHandle) {
try {
- int appEnabledSetting = mSettings.getApplicationEnabledSetting(packageName, userId);
+ int appEnabledSetting = mSettings.getApplicationEnabledSetting(packageName,
+ userHandle.getIdentifier());
if (appEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
final AndroidPackage pkg = getPackage(packageName);
if (pkg == null) {
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index d39cac0..c29e4d7 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -463,8 +463,9 @@
@Override
@Nullable
@Deprecated
- public final InstallSourceInfo getInstallSourceInfo(@NonNull String packageName) {
- return snapshot().getInstallSourceInfo(packageName);
+ public final InstallSourceInfo getInstallSourceInfo(@NonNull String packageName,
+ @UserIdInt int userId) {
+ return snapshot().getInstallSourceInfo(packageName, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fa535c3..03e0d36 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -925,7 +925,7 @@
final int targetPackageUid = snapshot.getPackageUid(packageName, 0, userId);
final boolean isUpdate = targetPackageUid != -1 || isApexSession();
final InstallSourceInfo existingInstallSourceInfo = isUpdate
- ? snapshot.getInstallSourceInfo(packageName)
+ ? snapshot.getInstallSourceInfo(packageName, userId)
: null;
final String existingInstallerPackageName = existingInstallSourceInfo != null
? existingInstallSourceInfo.getInstallingPackageName()
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4d74567..6bc8760 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1328,7 +1328,8 @@
throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
}
- final InstallSourceInfo installSourceInfo = snapshot.getInstallSourceInfo(packageName);
+ final InstallSourceInfo installSourceInfo = snapshot.getInstallSourceInfo(packageName,
+ userId);
final String installerPackageName;
if (installSourceInfo != null) {
if (!TextUtils.isEmpty(installSourceInfo.getInitiatingPackageName())) {
@@ -2569,7 +2570,7 @@
if (best == null || cur.priority > best.priority) {
if (computer.isComponentEffectivelyEnabled(cur.getComponentInfo(),
- UserHandle.USER_SYSTEM)) {
+ UserHandle.SYSTEM)) {
best = cur;
} else {
Slog.w(TAG, "Domain verification agent found but not enabled");
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 5b967ec..f340f93 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -26,7 +26,6 @@
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
@@ -408,7 +407,7 @@
final int numRequiredVerifierPackages = requiredVerifierPackages.size();
for (int i = numRequiredVerifierPackages - 1; i >= 0; i--) {
if (!snapshot.isApplicationEffectivelyEnabled(requiredVerifierPackages.get(i),
- SYSTEM_UID)) {
+ verifierUser)) {
Slog.w(TAG,
"Required verifier: " + requiredVerifierPackages.get(i) + " is disabled");
requiredVerifierPackages.remove(i);
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 8d7f782..3644054 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -21,7 +21,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -101,8 +104,10 @@
private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
"FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
- private static final String FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL =
- "FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL";
+ private static final String FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
+ "FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
+ private static final String FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
+ "FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
/** Interface that allows reading the device state configuration. */
interface ReadableConfig {
@@ -162,9 +167,12 @@
case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
flags |= DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP;
break;
- case FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL:
- flags |= DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL;
+ case FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
+ flags |= DeviceState
+ .FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL;
break;
+ case FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
+ flags |= DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE;
default:
Slog.w(TAG, "Parsed unknown flag with name: "
+ configFlagString);
@@ -210,6 +218,9 @@
@GuardedBy("mLock")
private @PowerManager.ThermalStatus int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
+ @GuardedBy("mLock")
+ private boolean mPowerSaveModeEnabled;
+
private DeviceStateProviderImpl(@NonNull Context context,
@NonNull List<DeviceState> deviceStates,
@NonNull List<Conditions> stateConditions) {
@@ -224,14 +235,32 @@
setStateConditions(deviceStates, stateConditions);
- // If any of the device states are thermal sensitive, i.e. it should be disabled when the
- // device is overheating, then we will update the list of supported states when thermal
- // status changes.
- if (hasThermalSensitiveState(deviceStates)) {
- PowerManager powerManager = context.getSystemService(PowerManager.class);
- if (powerManager != null) {
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager != null) {
+ // If any of the device states are thermal sensitive, i.e. it should be disabled when
+ // the device is overheating, then we will update the list of supported states when
+ // thermal status changes.
+ if (hasThermalSensitiveState(deviceStates)) {
powerManager.addThermalStatusListener(this);
}
+
+ // If any of the device states are power sensitive, i.e. it should be disabled when
+ // power save mode is enabled, then we will update the list of supported states when
+ // power save mode is toggled.
+ if (hasPowerSaveSensitiveState(deviceStates)) {
+ IntentFilter filter = new IntentFilter(
+ PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL.equals(
+ intent.getAction())) {
+ onPowerSaveModeChanged(powerManager.isPowerSaveMode());
+ }
+ }
+ };
+ mContext.registerReceiver(receiver, filter);
+ }
}
}
@@ -382,7 +411,11 @@
for (DeviceState deviceState : mOrderedStates) {
if (isThermalStatusCriticalOrAbove(mThermalStatus)
&& deviceState.hasFlag(
- DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) {
+ DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ continue;
+ }
+ if (mPowerSaveModeEnabled && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
continue;
}
supportedStates.add(deviceState);
@@ -674,6 +707,18 @@
}
}
+ @VisibleForTesting
+ void onPowerSaveModeChanged(boolean isPowerSaveModeEnabled) {
+ synchronized (mLock) {
+ if (mPowerSaveModeEnabled != isPowerSaveModeEnabled) {
+ mPowerSaveModeEnabled = isPowerSaveModeEnabled;
+ notifySupportedStatesChanged(
+ isPowerSaveModeEnabled ? SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED
+ : SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED);
+ }
+ }
+ }
+
@Override
public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) {
int previousThermalStatus;
@@ -709,7 +754,16 @@
private static boolean hasThermalSensitiveState(List<DeviceState> deviceStates) {
for (DeviceState state : deviceStates) {
- if (state.hasFlag(DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) {
+ if (state.hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasPowerSaveSensitiveState(List<DeviceState> deviceStates) {
+ for (int i = 0; i < deviceStates.size(); i++) {
+ if (deviceStates.get(i).hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 324a0ad..e21c156 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4357,17 +4357,6 @@
@Override
void addWindow(WindowState w) {
super.addWindow(w);
-
- boolean gotReplacementWindow = false;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState candidate = mChildren.get(i);
- gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w);
- }
-
- // if we got a replacement window, reset the timeout to give drawing more time
- if (gotReplacementWindow) {
- mWmService.scheduleWindowReplacementTimeouts(this);
- }
checkKeyguardFlagsChanged();
}
@@ -4382,12 +4371,6 @@
updateLetterboxSurface(child);
}
- void onWindowReplacementTimeout() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- (mChildren.get(i)).onWindowReplacementTimeout();
- }
- }
-
void setAppLayoutChanges(int changes, String reason) {
if (!mChildren.isEmpty()) {
final DisplayContent dc = getDisplayContent();
@@ -4398,15 +4381,6 @@
}
}
- void removeReplacedWindowIfNeeded(WindowState replacement) {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- if (win.removeReplacedWindowIfNeeded(replacement)) {
- return;
- }
- }
- }
-
private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) {
final WindowState tStartingWindow = fromActivity.mStartingWindow;
if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
@@ -5633,10 +5607,7 @@
// * activity is transitioning visibility state
// * or the activity was marked as hidden and is exiting before we had a chance to play the
// transition animation
- // * or this is an opening app and windows are being replaced (e.g. freeform window to
- // normal window).
- return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting)
- || (visible && forAllWindows(WindowState::waitingForReplacement, true));
+ return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting);
}
/**
@@ -7380,35 +7351,6 @@
}
}
- void setWillReplaceWindows(boolean animate) {
- ProtoLog.d(WM_DEBUG_ADD_REMOVE,
- "Marking app token %s with replacing windows.", this);
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.setWillReplaceWindow(animate);
- }
- }
-
- void setWillReplaceChildWindows() {
- ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Marking app token %s"
- + " with replacing child windows.", this);
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.setWillReplaceChildWindows();
- }
- }
-
- void clearWillReplaceWindows() {
- ProtoLog.d(WM_DEBUG_ADD_REMOVE,
- "Resetting app token %s of replacing window marks.", this);
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.clearWillReplaceWindow();
- }
- }
-
void requestUpdateWallpaperIfNeeded() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
final WindowState w = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0b28ba2..bc9efc8 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -377,15 +377,10 @@
mDragWindowHandle.ownerUid = MY_UID;
mDragWindowHandle.scaleFactor = 1.0f;
- // InputConfig.PREVENT_SPLITTING: To keep the default behavior of this window to be
- // focusable, which allows the system to consume keys when dragging is active. This can
- // also be used to modify the drag state on key press. For example, cancel drag on
- // escape key.
// InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing
// touches to pass through to windows underneath. This allows user to interact with the
// UI to navigate while dragging.
- mDragWindowHandle.inputConfig =
- InputConfig.PREVENT_SPLITTING | InputConfig.TRUSTED_OVERLAY;
+ mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY;
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b2a4df1..fda2125 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -44,7 +44,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -432,13 +431,6 @@
}
};
- private static final Consumer<WindowState> sRemoveReplacedWindowsConsumer = w -> {
- final ActivityRecord activity = w.mActivityRecord;
- if (activity != null) {
- activity.removeReplacedWindowIfNeeded(w);
- }
- };
-
RootWindowContainer(WindowManagerService service) {
super(service);
mHandler = new MyHandler(service.mH.getLooper());
@@ -662,17 +654,6 @@
forAllWindows(mCloseSystemDialogsConsumer, false /* traverseTopToBottom */);
}
- void removeReplacedWindows() {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION removeReplacedWindows");
- mWmService.openSurfaceTransaction();
- try {
- forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
- } finally {
- mWmService.closeSurfaceTransaction("removeReplacedWindows");
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION removeReplacedWindows");
- }
- }
-
boolean hasPendingLayoutChanges(WindowAnimator animator) {
boolean hasChanges = false;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ce9bff8..78ee6f9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -232,11 +232,6 @@
}
@Override
- public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
- mService.setWillReplaceWindows(appToken, childrenOnly);
- }
-
- @Override
public boolean cancelDraw(IWindow window) {
return mService.cancelDraw(this, window);
}
@@ -862,7 +857,7 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type,
- IBinder windowToken, IBinder focusGrantToken, String inputHandleName,
+ int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName,
InputChannel outInputChannel) {
if (hostInputToken == null && !mCanAddInternalSystemWindow) {
// Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
@@ -874,7 +869,7 @@
try {
mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken,
flags, mCanAddInternalSystemWindow ? privateFlags : 0,
- type, windowToken, focusGrantToken, inputHandleName,
+ type, inputFeatures, windowToken, focusGrantToken, inputHandleName,
outInputChannel);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -883,11 +878,11 @@
@Override
public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
- int flags, int privateFlags, Region region) {
+ int flags, int privateFlags, int inputFeatures, Region region) {
final long identity = Binder.clearCallingIdentity();
try {
mService.updateInputChannel(channelToken, displayId, surface, flags,
- mCanAddInternalSystemWindow ? privateFlags : 0, region);
+ mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 67ca844..6882e4c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -866,23 +866,8 @@
return false;
}
- final int toRootTaskWindowingMode = toRootTask.getWindowingMode();
final ActivityRecord topActivity = getTopNonFinishingActivity();
- final boolean mightReplaceWindow = topActivity != null
- && replaceWindowsOnTaskMove(getWindowingMode(), toRootTaskWindowingMode);
- if (mightReplaceWindow) {
- // We are about to relaunch the activity because its configuration changed due to
- // being maximized, i.e. size change. The activity will first remove the old window
- // and then add a new one. This call will tell window manager about this, so it can
- // preserve the old window until the new one is drawn. This prevents having a gap
- // between the removal and addition, in which no window is visible. We also want the
- // entrance of the new window to be properly animated.
- // Note here we always set the replacing window first, as the flags might be needed
- // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
- windowManager.setWillReplaceWindow(topActivity.token, animate);
- }
-
mAtmService.deferWindowLayout();
boolean kept = true;
try {
@@ -926,17 +911,10 @@
mAtmService.continueWindowLayout();
}
- if (mightReplaceWindow) {
- // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
- // window), we need to clear the replace window settings. Otherwise, we schedule a
- // timeout to remove the old window if the replacing window is not coming in time.
- windowManager.scheduleClearWillReplaceWindows(topActivity.token, !kept);
- }
-
if (!deferResume) {
// The task might have already been running and its visibility needs to be synchronized
// with the visibility of the root task / windows.
- root.ensureActivitiesVisible(null, 0, !mightReplaceWindow);
+ root.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
root.resumeFocusedTasksTopActivities();
}
@@ -947,17 +925,6 @@
return (preferredRootTask == toRootTask);
}
- /**
- * @return {@code true} if the windows of tasks being moved to the target root task from the
- * source root task should be replaced, meaning that window manager will keep the old window
- * around until the new is ready.
- */
- private static boolean replaceWindowsOnTaskMove(
- int sourceWindowingMode, int targetWindowingMode) {
- return sourceWindowingMode == WINDOWING_MODE_FREEFORM
- || targetWindowingMode == WINDOWING_MODE_FREEFORM;
- }
-
void touchActiveTime() {
lastActiveTime = SystemClock.elapsedRealtime();
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 97f48b7..370d304 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1012,6 +1012,7 @@
dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
}
+ validateVisibility();
mState = STATE_FINISHED;
mController.mTransitionTracer.logState(this);
@@ -1920,6 +1921,7 @@
change.setLastParent(info.mStartParent.mRemoteToken.toWindowContainerToken());
}
change.setMode(info.getTransitMode(target));
+ info.mReadyMode = change.getMode();
change.setStartAbsBounds(info.mAbsoluteBounds);
change.setFlags(info.getChangeFlags(target));
change.setDisplayId(info.mDisplayId, getDisplayId(target));
@@ -2177,6 +2179,26 @@
return mainWin.getAttrs().rotationAnimation;
}
+ private void validateVisibility() {
+ for (int i = mTargets.size() - 1; i >= 0; --i) {
+ if (reduceMode(mTargets.get(i).mReadyMode) != TRANSIT_CLOSE) {
+ return;
+ }
+ }
+ // All modes are CLOSE. The surfaces may be hidden by the animation unexpectedly.
+ // If the window container should be visible, then recover it.
+ mController.mStateValidators.add(() -> {
+ for (int i = mTargets.size() - 1; i >= 0; --i) {
+ final ChangeInfo change = mTargets.get(i);
+ if (!change.mContainer.isVisibleRequested()) continue;
+ Slog.e(TAG, "Force show for visible " + change.mContainer
+ + " which may be hidden by transition unexpectedly");
+ change.mContainer.getSyncTransaction().show(change.mContainer.mSurfaceControl);
+ change.mContainer.scheduleAnimation();
+ }
+ });
+ }
+
/** Applies the new configuration for the changed displays. */
void applyDisplayChangeIfNeeded() {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
@@ -2262,6 +2284,10 @@
SurfaceControl mSnapshot;
float mSnapshotLuma;
+ /** The mode which is set when the transition is ready. */
+ @TransitionInfo.TransitionMode
+ int mReadyMode;
+
ChangeInfo(@NonNull WindowContainer origState) {
mContainer = origState;
mVisible = origState.isVisibleRequested();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 2b848d5..0b9ceea 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -163,14 +163,6 @@
if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
+ " mDrawState=" + w.mWinAnimator.mDrawState);
- if (w.mWillReplaceWindow && mWallpaperTarget == null
- && !mFindResults.useTopWallpaperAsTarget) {
- // When we are replacing a window and there was wallpaper before replacement, we want to
- // keep the window until the new windows fully appear and can determine the visibility,
- // to avoid flickering.
- mFindResults.setUseTopWallpaperAsTarget(true);
- }
-
final WindowContainer animatingContainer = w.mActivityRecord != null
? w.mActivityRecord.getAnimatingContainer() : null;
final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index c11391e..2596533 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -69,10 +69,6 @@
SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2);
private boolean mInitialized = false;
- // When set to true the animator will go over all windows after an animation frame is posted and
- // check if some got replaced and can be removed.
- private boolean mRemoveReplacedWindows = false;
-
private Choreographer mChoreographer;
/**
@@ -217,11 +213,6 @@
mService.closeSurfaceTransaction("WindowAnimator");
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
- if (mRemoveReplacedWindows) {
- root.removeReplacedWindows();
- mRemoveReplacedWindows = false;
- }
-
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
@@ -286,10 +277,6 @@
return displayAnimator;
}
- void requestRemovalOfReplacedWindows(WindowState win) {
- mRemoveReplacedWindows = true;
- }
-
void scheduleAnimation() {
if (!mAnimationFrameCallbackScheduled) {
mAnimationFrameCallbackScheduled = true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 2f3a70e..969afe5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -740,7 +740,7 @@
/**
* Show IME on imeTargetWindow once IME has finished layout.
*
- * @param imeTargetWindowToken token of the (IME target) window on which IME should be shown.
+ * @param imeTargetWindowToken token of the (IME target) window which IME should be shown.
* @param statsToken the token tracking the current IME show request or {@code null} otherwise.
*/
public abstract void showImePostLayout(IBinder imeTargetWindowToken,
@@ -749,7 +749,7 @@
/**
* Hide IME using imeTargetWindow when requested.
*
- * @param imeTargetWindowToken token of the (IME target) window on which IME should be hidden.
+ * @param imeTargetWindowToken token of the (IME target) window on which requests hiding IME.
* @param displayId the id of the display the IME is on.
* @param statsToken the token tracking the current IME hide request or {@code null} otherwise.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 42d23e7..be42f36 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -88,7 +88,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
import static android.view.WindowManagerGlobal.ADD_OKAY;
@@ -382,9 +381,6 @@
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
- /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
- static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
-
/** Amount of time to allow a last ANR message to exist before freeing the memory. */
static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
@@ -565,12 +561,6 @@
final WindowManagerGlobalLock mGlobalLock;
/**
- * List of app window tokens that are waiting for replacing windows. If the
- * replacement doesn't come in time the stale windows needs to be disposed of.
- */
- final ArrayList<ActivityRecord> mWindowReplacementTimeouts = new ArrayList<>();
-
- /**
* Windows that are being resized. Used so we can tell the client about
* the resize after closing the transaction in which we resized the
* underlying surface.
@@ -1774,15 +1764,6 @@
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
- // Check if we need to prepare a transition for replacing window first.
- if (!win.mTransitionController.isShellTransitionsEnabled()
- && activity != null && activity.isVisible()
- && !prepareWindowReplacementTransition(activity)) {
- // If not, check if need to set up a dummy transition during display freeze
- // so that the unfreeze wait for the apps to draw. This might be needed if
- // the app is relaunching.
- prepareNoneTransitionForRelaunching(activity);
- }
if (displayPolicy.areSystemBarsForcedConsumedLw()) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
@@ -1944,48 +1925,6 @@
}
/**
- * Returns true if we're done setting up any transitions.
- */
- private boolean prepareWindowReplacementTransition(ActivityRecord activity) {
- activity.clearAllDrawn();
- final WindowState replacedWindow = activity.getReplacingWindow();
- if (replacedWindow == null) {
- // We expect to already receive a request to remove the old window. If it did not
- // happen, let's just simply add a window.
- return false;
- }
- // We use the visible frame, because we want the animation to morph the window from what
- // was visible to the user to the final destination of the new window.
- final Rect frame = new Rect(replacedWindow.getFrame());
- final WindowManager.LayoutParams attrs = replacedWindow.mAttrs;
- frame.inset(replacedWindow.getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
- frame, attrs.type, replacedWindow.getWindowingMode(), attrs.softInputMode,
- attrs.flags));
- // We treat this as if this activity was opening, so we can trigger the app transition
- // animation and piggy-back on existing transition animation infrastructure.
- final DisplayContent dc = activity.getDisplayContent();
- dc.mOpeningApps.add(activity);
- dc.prepareAppTransition(TRANSIT_RELAUNCH);
- dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
- frame.width(), frame.height());
- dc.executeAppTransition();
- return true;
- }
-
- private void prepareNoneTransitionForRelaunching(ActivityRecord activity) {
- // Set up a none-transition and add the app to opening apps, so that the display
- // unfreeze wait for the apps to be drawn.
- // Note that if the display unfroze already because app unfreeze timed out,
- // we don't set up the transition anymore and just let it go.
- final DisplayContent dc = activity.getDisplayContent();
- if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) {
- dc.mOpeningApps.add(activity);
- dc.prepareAppTransition(TRANSIT_NONE);
- dc.executeAppTransition();
- }
- }
-
- /**
* Set whether screen capture is disabled for all windows of a specific user from
* the device policy cache.
*/
@@ -2390,12 +2329,6 @@
// If we are not currently running the exit animation, we need to see about starting
// one.
- // We don't want to animate visibility of windows which are pending replacement.
- // In the case of activity relaunch child windows could request visibility changes as
- // they are detached from the main application window during the tear down process.
- // If we satisfied these visibility changes though, we would cause a visual glitch
- // hiding the window before it's replacement was available. So we just do nothing on
- // our side.
// This must be called before the call to performSurfacePlacement.
if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
if (DEBUG_VISIBILITY) {
@@ -2403,20 +2336,18 @@
"Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit);
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
- if (!win.mWillReplaceWindow) {
- // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag
- // in DC#pendingLayoutChanges and update the wallpaper target later.
- // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window
- // when the window is about to exit, so we update the wallpaper target
- // immediately here. Otherwise this window will be stuck in exiting and its
- // surface remains on the screen.
- // TODO(b/189856716): Allow destroying surface even if it belongs to the
- // keyguard target.
- if (wallpaperMayMove) {
- displayContent.mWallpaperController.adjustWallpaperWindows();
- }
- tryStartExitingAnimation(win, winAnimator);
+ // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag
+ // in DC#pendingLayoutChanges and update the wallpaper target later.
+ // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window
+ // when the window is about to exit, so we update the wallpaper target
+ // immediately here. Otherwise this window will be stuck in exiting and its
+ // surface remains on the screen.
+ // TODO(b/189856716): Allow destroying surface even if it belongs to the
+ // keyguard target.
+ if (wallpaperMayMove) {
+ displayContent.mWallpaperController.adjustWallpaperWindows();
}
+ tryStartExitingAnimation(win, winAnimator);
}
// Create surfaceControl before surface placement otherwise layout will be skipped
@@ -5328,8 +5259,6 @@
public static final int UPDATE_MULTI_WINDOW_STACKS = 41;
- public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
-
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_HIDE_TIMEOUT = 52;
public static final int RESTORE_POINTER_ICON = 55;
@@ -5555,16 +5484,6 @@
}
break;
}
- case WINDOW_REPLACEMENT_TIMEOUT: {
- synchronized (mGlobalLock) {
- for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mWindowReplacementTimeouts.get(i);
- activity.onWindowReplacementTimeout();
- }
- mWindowReplacementTimeouts.clear();
- }
- break;
- }
case WINDOW_HIDE_TIMEOUT: {
final WindowState window = (WindowState) msg.obj;
synchronized (mGlobalLock) {
@@ -7083,98 +7002,6 @@
return mGlobalLock;
}
- /**
- * Hint to a token that its activity will relaunch, which will trigger removal and addition of
- * a window.
- *
- * @param token Application token for which the activity will be relaunched.
- */
- void setWillReplaceWindow(IBinder token, boolean animate) {
- final ActivityRecord activity = mRoot.getActivityRecord(token);
- if (activity == null) {
- ProtoLog.w(WM_ERROR, "Attempted to set replacing window on non-existing app token %s",
- token);
- return;
- }
- if (!activity.hasContentToDisplay()) {
- ProtoLog.w(WM_ERROR,
- "Attempted to set replacing window on app token with no content %s",
- token);
- return;
- }
- activity.setWillReplaceWindows(animate);
- }
-
- /**
- * Hint to a token that its windows will be replaced across activity relaunch.
- * The windows would otherwise be removed shortly following this as the
- * activity is torn down.
- * @param token Application token for which the activity will be relaunched.
- * @param childrenOnly Whether to mark only child windows for replacement
- * (for the case where main windows are being preserved/
- * reused rather than replaced).
- *
- */
- // TODO: The s at the end of the method name is the only difference with the name of the method
- // above. We should combine them or find better names.
- void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
- synchronized (mGlobalLock) {
- final ActivityRecord activity = mRoot.getActivityRecord(token);
- if (activity == null) {
- ProtoLog.w(WM_ERROR,
- "Attempted to set replacing window on non-existing app token %s",
- token);
- return;
- }
- if (!activity.hasContentToDisplay()) {
- ProtoLog.w(WM_ERROR,
- "Attempted to set replacing window on app token with no content %s",
- token);
- return;
- }
-
- if (childrenOnly) {
- activity.setWillReplaceChildWindows();
- } else {
- activity.setWillReplaceWindows(false /* animate */);
- }
-
- scheduleClearWillReplaceWindows(token, true /* replacing */);
- }
- }
-
- /**
- * If we're replacing the window, schedule a timer to clear the replaced window
- * after a timeout, in case the replacing window is not coming.
- *
- * If we're not replacing the window, clear the replace window settings of the app.
- *
- * @param token Application token for the activity whose window might be replaced.
- * @param replacing Whether the window is being replaced or not.
- */
- void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
- final ActivityRecord activity = mRoot.getActivityRecord(token);
- if (activity == null) {
- ProtoLog.w(WM_ERROR, "Attempted to reset replacing window on non-existing app token %s",
- token);
- return;
- }
- if (replacing) {
- scheduleWindowReplacementTimeouts(activity);
- } else {
- activity.clearWillReplaceWindows();
- }
- }
-
- void scheduleWindowReplacementTimeouts(ActivityRecord activity) {
- if (!mWindowReplacementTimeouts.contains(activity)) {
- mWindowReplacementTimeouts.add(activity);
- }
- mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
- mH.sendEmptyMessageDelayed(
- H.WINDOW_REPLACEMENT_TIMEOUT, WINDOW_REPLACEMENT_TIMEOUT_DURATION);
- }
-
@Override
public int getDockedStackSide() {
return 0;
@@ -8744,8 +8571,8 @@
*/
void grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
SurfaceControl surface, IWindow window, IBinder hostInputToken,
- int flags, int privateFlags, int type, IBinder windowToken, IBinder focusGrantToken,
- String inputHandleName, InputChannel outInputChannel) {
+ int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken,
+ IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) {
final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type);
final InputApplicationHandle applicationHandle;
final String name;
@@ -8762,7 +8589,7 @@
}
updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
- name, applicationHandle, flags, privateFlags, sanitizedType,
+ name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType,
null /* region */, window);
clientChannel.copyTo(outInputChannel);
@@ -8803,13 +8630,14 @@
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
InputApplicationHandle applicationHandle, int flags,
- int privateFlags, int type, Region region, IWindow window) {
+ int privateFlags, int inputFeatures, int type, Region region, IWindow window) {
final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
h.token = channelToken;
h.setWindowToken(window);
h.name = name;
flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
+ inputFeatures = sanitizeSpyWindow(inputFeatures, name, callingUid, callingPid);
final int sanitizedLpFlags =
(flags & (FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE))
@@ -8819,7 +8647,7 @@
// Do not allow any input features to be set without sanitizing them first.
h.inputConfig = InputConfigAdapter.getInputConfigFromWindowParams(
- type, sanitizedLpFlags, 0 /*inputFeatures*/);
+ type, sanitizedLpFlags, inputFeatures);
if ((flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
@@ -8856,7 +8684,7 @@
* is undefined.
*/
void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
- int flags, int privateFlags, Region region) {
+ int flags, int privateFlags, int inputFeatures, Region region) {
final InputApplicationHandle applicationHandle;
final String name;
final EmbeddedWindowController.EmbeddedWindow win;
@@ -8871,7 +8699,8 @@
}
updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
- applicationHandle, flags, privateFlags, win.mWindowType, region, win.mClient);
+ applicationHandle, flags, privateFlags, inputFeatures, win.mWindowType, region,
+ win.mClient);
}
/** Return whether layer tracing is enabled */
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d6c0311..8a083aa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -59,13 +59,11 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -632,22 +630,6 @@
boolean mHasSurface = false;
- // This window will be replaced due to relaunch. This allows window manager
- // to differentiate between simple removal of a window and replacement. In the latter case it
- // will preserve the old window until the new one is drawn.
- boolean mWillReplaceWindow = false;
- // If true, the replaced window was already requested to be removed.
- private boolean mReplacingRemoveRequested = false;
- // Whether the replacement of the window should trigger app transition animation.
- private boolean mAnimateReplacingWindow = false;
- // If not null, the window that will be used to replace the old one. This is being set when
- // the window is added and unset when this window reports its first draw.
- private WindowState mReplacementWindow = null;
- // For the new window in the replacement transition, if we have
- // requested to replace without animation, then we should
- // make sure we also don't apply an enter animation for
- // the new window.
- boolean mSkipEnterAnimationForSeamlessReplacement = false;
// Whether this window is being moved via the resize API
private boolean mMovedByResize;
@@ -1309,13 +1291,6 @@
}
boolean skipLayout() {
- if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
- // This window is being replaced and either already got information that it's being
- // removed or we are still waiting for some information. Because of this we don't
- // want to apply any more changes to it, so it remains in this state until new window
- // appears.
- return true;
- }
// Skip layout of the window when in transition to pip mode.
return mActivityRecord != null && mActivityRecord.mWaitForEnteringPinnedMode;
}
@@ -2354,24 +2329,6 @@
}
}
- void onWindowReplacementTimeout() {
- if (mWillReplaceWindow) {
- // Since the window already timed out, remove it immediately now.
- // Use WindowState#removeImmediately() instead of WindowState#removeIfPossible(), as
- // the latter delays removal on certain conditions, which will leave the stale window
- // in the root task and marked mWillReplaceWindow=false, so the window will never be
- // removed.
- //
- // Also removes child windows.
- removeImmediately();
- } else {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = mChildren.get(i);
- c.onWindowReplacementTimeout();
- }
- }
- }
-
@Override
void removeImmediately() {
if (mRemoved) {
@@ -2388,11 +2345,6 @@
mWinAnimator.destroySurfaceLocked(getSyncTransaction());
super.removeImmediately();
- mWillReplaceWindow = false;
- if (mReplacementWindow != null) {
- mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
- }
-
final DisplayContent dc = getDisplayContent();
if (isImeLayeringTarget()) {
// Remove the attached IME screenshot surface.
@@ -2472,12 +2424,11 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b "
+ "mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b "
- + "mWillReplaceWindow=%b mDisplayFrozen=%b callers=%s",
+ + "mDisplayFrozen=%b callers=%s",
this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
mHasSurface, mWinAnimator.getShown(),
isAnimating(TRANSITION | PARENTS),
mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
- mWillReplaceWindow,
mWmService.mDisplayFrozen, Debug.getCallers(6));
// Visibility of the removed window. Will be used later to update orientation later on.
@@ -2487,22 +2438,6 @@
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
if (mHasSurface && mToken.okToAnimate()) {
- if (mWillReplaceWindow) {
- // This window is going to be replaced. We need to keep it around until the new one
- // gets added, then we will get rid of this one.
- ProtoLog.v(WM_DEBUG_ADD_REMOVE,
- "Preserving %s until the new one is added", this);
- // TODO: We are overloading mAnimatingExit flag to prevent the window state from
- // been removed. We probably need another flag to indicate that window removal
- // should be deffered vs. overloading the flag that says we are playing an exit
- // animation.
- ProtoLog.v(WM_DEBUG_ANIM,
- "Set animatingExit: reason=remove/replaceWindow win=%s", this);
- mAnimatingExit = true;
- mReplacingRemoveRequested = true;
- return;
- }
-
// If we are not currently running the exit animation, we need to see about starting one
wasVisible = isVisible();
@@ -2714,53 +2649,6 @@
mInputWindowHandle.setToken(null);
}
- /** Returns true if the replacement window was removed. */
- boolean removeReplacedWindowIfNeeded(WindowState replacement) {
- if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawn()) {
- replacement.mSkipEnterAnimationForSeamlessReplacement = false;
- removeReplacedWindow();
- return true;
- }
-
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = mChildren.get(i);
- if (c.removeReplacedWindowIfNeeded(replacement)) {
- return true;
- }
- }
- return false;
- }
-
- private void removeReplacedWindow() {
- ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Removing replaced window: %s", this);
- mWillReplaceWindow = false;
- mAnimateReplacingWindow = false;
- mReplacingRemoveRequested = false;
- mReplacementWindow = null;
- if (mAnimatingExit || !mAnimateReplacingWindow) {
- removeImmediately();
- }
- }
-
- boolean setReplacementWindowIfNeeded(WindowState replacementCandidate) {
- boolean replacementSet = false;
-
- if (mWillReplaceWindow && mReplacementWindow == null
- && getWindowTag().toString().equals(replacementCandidate.getWindowTag().toString())) {
-
- mReplacementWindow = replacementCandidate;
- replacementCandidate.mSkipEnterAnimationForSeamlessReplacement = !mAnimateReplacingWindow;
- replacementSet = true;
- }
-
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = mChildren.get(i);
- replacementSet |= c.setReplacementWindowIfNeeded(replacementCandidate);
- }
-
- return replacementSet;
- }
-
void setDisplayLayoutNeeded() {
final DisplayContent dc = getDisplayContent();
if (dc != null) {
@@ -4391,49 +4279,6 @@
return parent != null && parent.isGoneForLayout();
}
- void setWillReplaceWindow(boolean animate) {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState c = mChildren.get(i);
- c.setWillReplaceWindow(animate);
- }
-
- if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0
- || mAttrs.type == TYPE_APPLICATION_STARTING) {
- // We don't set replacing on starting windows since they are added by window manager and
- // not the client so won't be replaced by the client.
- return;
- }
-
- mWillReplaceWindow = true;
- mReplacementWindow = null;
- mAnimateReplacingWindow = animate;
- }
-
- void clearWillReplaceWindow() {
- mWillReplaceWindow = false;
- mReplacementWindow = null;
- mAnimateReplacingWindow = false;
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState c = mChildren.get(i);
- c.clearWillReplaceWindow();
- }
- }
-
- boolean waitingForReplacement() {
- if (mWillReplaceWindow) {
- return true;
- }
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState c = mChildren.get(i);
- if (c.waitingForReplacement()) {
- return true;
- }
- }
- return false;
- }
-
void requestUpdateWallpaperIfNeeded() {
final DisplayContent dc = getDisplayContent();
if (dc != null && ((mIsWallpaper && !mLastConfigReportedToClient) || hasWallpaper())) {
@@ -4464,43 +4309,6 @@
return winY;
}
- // During activity relaunch due to resize, we sometimes use window replacement
- // for only child windows (as the main window is handled by window preservation)
- // and the big surface.
- //
- // Though windows of TYPE_APPLICATION or TYPE_DRAWN_APPLICATION (as opposed to
- // TYPE_BASE_APPLICATION) are not children in the sense of an attached window,
- // we also want to replace them at such phases, as they won't be covered by window
- // preservation, and in general we expect them to return following relaunch.
- boolean shouldBeReplacedWithChildren() {
- return mIsChildWindow || mAttrs.type == TYPE_APPLICATION
- || mAttrs.type == TYPE_DRAWN_APPLICATION;
- }
-
- void setWillReplaceChildWindows() {
- if (shouldBeReplacedWithChildren()) {
- setWillReplaceWindow(false /* animate */);
- }
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState c = mChildren.get(i);
- c.setWillReplaceChildWindows();
- }
- }
-
- WindowState getReplacingWindow() {
- if (mAnimatingExit && mWillReplaceWindow && mAnimateReplacingWindow) {
- return this;
- }
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState c = mChildren.get(i);
- final WindowState replacing = c.getReplacingWindow();
- if (replacing != null) {
- return replacing;
- }
- }
- return null;
- }
-
int getRotationAnimationHint() {
if (mActivityRecord != null) {
return mActivityRecord.mRotationAnimationHint;
@@ -4990,14 +4798,11 @@
boolean clearAnimatingFlags() {
boolean didSomething = false;
- // We don't want to clear it out for windows that get replaced, because the
- // animation depends on the flag to remove the replaced window.
- //
// We also don't clear the mAnimatingExit flag for windows which have the
// mRemoveOnExit flag. This indicates an explicit remove request has been issued
// by the client. We should let animation proceed and not clear this flag or
// they won't eventually be removed by WindowStateAnimator#finishExit.
- if (!mWillReplaceWindow && !mRemoveOnExit) {
+ if (!mRemoveOnExit) {
// Clear mAnimating flag together with mAnimatingExit. When animation
// changes from exiting to entering, we need to clear this flag until the
// new animation gets applied, so that isAnimationStarting() becomes true
@@ -5312,7 +5117,7 @@
return activity.needsZBoost();
}
}
- return mWillReplaceWindow;
+ return false;
}
private boolean isStartingWindowAssociatedToTask() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e8625bc..3aac816 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -449,7 +449,6 @@
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
mSurfaceController.showRobustly(t);
- mAnimator.requestRemovalOfReplacedWindows(w);
mLastHidden = false;
final DisplayContent displayContent = w.getDisplayContent();
if (!displayContent.getLastHasContent()) {
@@ -504,13 +503,6 @@
}
void applyEnterAnimationLocked() {
- // If we are the new part of a window replacement transition and we have requested
- // not to animate, we instead want to make it seamless, so we don't want to apply
- // an enter transition.
- if (mWin.mSkipEnterAnimationForSeamlessReplacement) {
- return;
- }
-
final int transit;
if (mEnterAnimationPending) {
mEnterAnimationPending = false;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index c6b7898..327483e 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -315,17 +315,6 @@
return mChildren.isEmpty();
}
- WindowState getReplacingWindow() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- final WindowState replacing = win.getReplacingWindow();
- if (replacing != null) {
- return replacing;
- }
- }
- return null;
- }
-
/** Return true if this token has a window that wants the wallpaper displayed behind it. */
boolean windowsCanBeWallpaperTarget() {
for (int j = mChildren.size() - 1; j >= 0; j--) {
diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp
index 3677641..b57e451 100644
--- a/services/core/jni/gnss/GnssConfiguration.cpp
+++ b/services/core/jni/gnss/GnssConfiguration.cpp
@@ -67,7 +67,7 @@
: mIGnssConfiguration(iGnssConfiguration) {}
jobject GnssConfiguration::getVersion(JNIEnv* env) {
- return createHalInterfaceVersionJavaObject(env, 3, 0);
+ return createHalInterfaceVersionJavaObject(env, 3, mIGnssConfiguration->getInterfaceVersion());
}
jboolean GnssConfiguration::setEmergencySuplPdn(jint enable) {
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 793d83e..4f8235a 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -168,7 +168,7 @@
private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
Log.i(TAG, "respondToClientWithErrorAndFinish");
-
+ // TODO add exception bit
if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
Log.i(TAG, "Request has already been completed. This is strange.");
return;
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 4c5c366..85a48d9 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -592,9 +592,13 @@
}
private void finalizeAndEmitInitialPhaseMetric(RequestSession session) {
- var initMetric = session.mInitialPhaseMetric;
- initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
- MetricUtilities.logApiCalled(initMetric);
+ try {
+ var initMetric = session.mInitialPhaseMetric;
+ initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
+ MetricUtilities.logApiCalled(initMetric);
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
}
@Override
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index ed139b5..99f3b3e 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -81,6 +81,34 @@
}
/**
+ * A logging utility used primarily for the candidate phase of the current metric setup.
+ *
+ * @param providers a map with known providers
+ * @param emitSequenceId an emitted sequence id for the current session
+ */
+ protected static void logApiCalled(Map<String, ProviderSession> providers,
+ int emitSequenceId) {
+ try {
+ var providerSessions = providers.values();
+ int providerSize = providerSessions.size();
+ int[] candidateUidList = new int[providerSize];
+ int[] candidateQueryRoundTripTimeList = new int[providerSize];
+ int[] candidateStatusList = new int[providerSize];
+ int index = 0;
+ for (var session : providerSessions) {
+ CandidatePhaseMetric metric = session.mCandidatePhasePerProviderMetric;
+ candidateUidList[index] = metric.getCandidateUid();
+ candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMicroseconds();
+ candidateStatusList[index] = metric.getProviderQueryStatus();
+ index++;
+ }
+ // TODO Handle the emit here
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
+ }
+
+ /**
* The most common logging helper, handles the overall status of the API request with the
* provider status and latencies. Other versions of this method may be more useful depending
* on the situation, as this is geared towards the logging of {@link ProviderSession} types.
@@ -90,6 +118,7 @@
* @param providers a map with known providers
* @param callingUid the calling UID of the client app
* @param chosenProviderFinalPhaseMetric the metric data type of the final chosen provider
+ * TODO remove soon
*/
protected static void logApiCalled(ApiName apiName, ApiStatus apiStatus,
Map<String, ProviderSession> providers, int callingUid,
@@ -133,6 +162,7 @@
* contain default values for all other optional parameters.
*
* TODO(b/271135048) - given space requirements, this may be a good candidate for another atom
+ * TODO immediately remove and carry over TODO to new log for this setup
*
* @param apiName the api name to log
* @param apiStatus the status to log
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index 950cf4f..b86daba 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -33,7 +33,7 @@
*
* @hide
*/
-public final class ProviderClearSession extends ProviderSession<ClearCredentialStateRequest,
+public final class ProviderClearSession extends ProviderSession<ClearCredentialStateRequest,
Void>
implements
RemoteCredentialService.ProviderCallbacks<Void> {
@@ -42,7 +42,8 @@
private ClearCredentialStateException mProviderException;
/** Creates a new provider session to be used by the request session. */
- @Nullable public static ProviderClearSession createNewSession(
+ @Nullable
+ public static ProviderClearSession createNewSession(
Context context,
@UserIdInt int userId,
CredentialProviderInfo providerInfo,
@@ -53,7 +54,7 @@
clearRequestSession.mClientRequest,
clearRequestSession.mClientAppInfo);
return new ProviderClearSession(context, providerInfo, clearRequestSession, userId,
- remoteCredentialService, providerRequest);
+ remoteCredentialService, providerRequest);
}
@Nullable
@@ -90,6 +91,7 @@
if (exception instanceof ClearCredentialStateException) {
mProviderException = (ClearCredentialStateException) exception;
}
+ captureCandidateFailure();
updateStatusAndInvokeCallback(toStatus(errorCode));
}
@@ -120,14 +122,7 @@
@Override
protected void invokeSession() {
if (mRemoteCredentialService != null) {
- /*
- InitialPhaseMetric initMetric = ((RequestSession)mCallbacks).initMetric;
- TODO immediately once the other change patched through
- mCandidateProviderMetric.setSessionId(initMetric
- .mInitialPhaseMetric.getSessionId());
- mCandidateProviderMetric.setStartTime(initMetric.getStartTime())
- */
- mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
+ startCandidateMetrics();
mRemoteCredentialService.onClearCredentialState(mProviderRequest, this);
}
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 3ec0fc0..bbbb156 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -40,6 +40,8 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.server.credentials.metrics.EntryEnum;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -65,7 +67,8 @@
private final ProviderResponseDataHandler mProviderResponseDataHandler;
/** Creates a new provider session to be used by the request session. */
- @Nullable public static ProviderCreateSession createNewSession(
+ @Nullable
+ public static ProviderCreateSession createNewSession(
Context context,
@UserIdInt int userId,
CredentialProviderInfo providerInfo,
@@ -155,6 +158,7 @@
// Store query phase exception for aggregation with final response
mProviderException = (CreateCredentialException) exception;
}
+ captureCandidateFailure();
updateStatusAndInvokeCallback(toStatus(errorCode));
}
@@ -175,14 +179,32 @@
mProviderResponseDataHandler.addResponseContent(response.getCreateEntries(),
response.getRemoteCreateEntry());
if (mProviderResponseDataHandler.isEmptyResponse(response)) {
+ gatheCandidateEntryMetrics(response);
updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
} else {
+ gatheCandidateEntryMetrics(response);
updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED);
}
}
+ private void gatheCandidateEntryMetrics(BeginCreateCredentialResponse response) {
+ try {
+ var createEntries = response.getCreateEntries();
+ int numCreateEntries = createEntries == null ? 0 : createEntries.size();
+ // TODO confirm how to get types from slice
+ if (numCreateEntries > 0) {
+ createEntries.forEach(c ->
+ mCandidatePhasePerProviderMetric.addEntry(EntryEnum.CREDENTIAL_ENTRY));
+ }
+ mCandidatePhasePerProviderMetric.setNumEntriesTotal(numCreateEntries);
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
+ }
+
@Override
- @Nullable protected CreateCredentialProviderData prepareUiData()
+ @Nullable
+ protected CreateCredentialProviderData prepareUiData()
throws IllegalArgumentException {
Log.i(TAG, "In prepareUiData");
if (!ProviderSession.isUiInvokingStatus(getStatus())) {
@@ -226,14 +248,7 @@
@Override
protected void invokeSession() {
if (mRemoteCredentialService != null) {
- /*
- InitialPhaseMetric initMetric = ((RequestSession)mCallbacks).initMetric;
- TODO immediately once the other change patched through
- mCandidateProviderMetric.setSessionId(initMetric
- .mInitialPhaseMetric.getSessionId());
- mCandidateProviderMetric.setStartTime(initMetric.getStartTime())
- */
- mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
+ startCandidateMetrics();
mRemoteCredentialService.onCreateCredential(mProviderRequest, this);
}
}
@@ -305,12 +320,14 @@
}
private class ProviderResponseDataHandler {
- @Nullable private final ComponentName mExpectedRemoteEntryProviderService;
+ @Nullable
+ private final ComponentName mExpectedRemoteEntryProviderService;
@NonNull
private final Map<String, Pair<CreateEntry, Entry>> mUiCreateEntries = new HashMap<>();
- @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;
+ @Nullable
+ private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;
ProviderResponseDataHandler(@Nullable ComponentName expectedRemoteEntryProviderService) {
mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService;
@@ -323,6 +340,7 @@
setRemoteEntry(remoteEntry);
}
}
+
public void addCreateEntry(CreateEntry createEntry) {
String id = generateUniqueId();
Entry entry = new Entry(SAVE_ENTRY_KEY,
@@ -373,6 +391,7 @@
private boolean isEmptyResponse() {
return mUiCreateEntries.isEmpty() && mUiRemoteEntry == null;
}
+
@Nullable
public RemoteEntry getRemoteEntry(String entryKey) {
return mUiRemoteEntry == null || mUiRemoteEntry
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index ec8bf22..bf1db37 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -43,6 +43,8 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.server.credentials.metrics.EntryEnum;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -82,7 +84,8 @@
private final ProviderResponseDataHandler mProviderResponseDataHandler;
/** Creates a new provider session to be used by the request session. */
- @Nullable public static ProviderGetSession createNewSession(
+ @Nullable
+ public static ProviderGetSession createNewSession(
Context context,
@UserIdInt int userId,
CredentialProviderInfo providerInfo,
@@ -113,6 +116,7 @@
Log.i(TAG, "Unable to create provider session");
return null;
}
+
private static BeginGetCredentialRequest constructQueryPhaseRequest(
android.credentials.GetCredentialRequest filteredRequest,
CallingAppInfo callingAppInfo,
@@ -169,7 +173,7 @@
CallingAppInfo callingAppInfo,
Map<String, CredentialOption> beginGetOptionToCredentialOptionMap,
String hybridService) {
- super(context, beginGetRequest, callbacks, info.getComponentName() ,
+ super(context, beginGetRequest, callbacks, info.getComponentName(),
userId, remoteCredentialService);
mCompleteRequest = completeGetRequest;
mCallingAppInfo = callingAppInfo;
@@ -191,6 +195,7 @@
if (exception instanceof GetCredentialException) {
mProviderException = (GetCredentialException) exception;
}
+ captureCandidateFailure();
updateStatusAndInvokeCallback(toStatus(errorCode));
}
@@ -269,20 +274,14 @@
@Override
protected void invokeSession() {
if (mRemoteCredentialService != null) {
- /*
- InitialPhaseMetric initMetric = ((RequestSession)mCallbacks).initMetric;
- TODO immediately once the other change patched through
- mCandidateProviderMetric.setSessionId(initMetric
- .mInitialPhaseMetric.getSessionId());
- mCandidateProviderMetric.setStartTime(initMetric.getStartTime())
- */
- mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
+ startCandidateMetrics();
mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this);
}
}
@Override // Call from request session to data to be shown on the UI
- @Nullable protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException {
+ @Nullable
+ protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException {
Log.i(TAG, "In prepareUiData");
if (!ProviderSession.isUiInvokingStatus(getStatus())) {
Log.i(TAG, "In prepareUiData - provider does not want to show UI: "
@@ -389,6 +388,7 @@
GetCredentialException exception = maybeGetPendingIntentException(
providerPendingIntentResponse);
if (exception != null) {
+ // TODO (b/271135048), for AuthenticationEntry callback selection, set error
invokeCallbackWithError(exception.getType(),
exception.getMessage());
// Additional content received is in the form of an exception which ends the flow.
@@ -439,11 +439,34 @@
updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
return;
}
- // TODO immediately, add to Candidate Phase counts, repeat across all sessions
- // Use sets to dedup type counts
+ gatherCandidateEntryMetrics(response);
updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
}
+ private void gatherCandidateEntryMetrics(BeginGetCredentialResponse response) {
+ try {
+ int numCredEntries = response.getCredentialEntries().size();
+ int numActionEntries = response.getActions().size();
+ int numAuthEntries = response.getAuthenticationActions().size();
+ // TODO immediately add remote entries
+ // TODO immediately confirm how to get types from slice to get unique type count via
+ // dedupe
+ response.getCredentialEntries().forEach(c ->
+ mCandidatePhasePerProviderMetric.addEntry(EntryEnum.CREDENTIAL_ENTRY));
+ response.getActions().forEach(c ->
+ mCandidatePhasePerProviderMetric.addEntry(EntryEnum.ACTION_ENTRY));
+ response.getAuthenticationActions().forEach(c ->
+ mCandidatePhasePerProviderMetric.addEntry(EntryEnum.AUTHENTICATION_ENTRY));
+ mCandidatePhasePerProviderMetric.setNumEntriesTotal(numCredEntries + numAuthEntries
+ + numActionEntries);
+ mCandidatePhasePerProviderMetric.setCredentialEntryCount(numCredEntries);
+ mCandidatePhasePerProviderMetric.setActionEntryCount(numActionEntries);
+ mCandidatePhasePerProviderMetric.setAuthenticationEntryCount(numAuthEntries);
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
+ }
+
/**
* When an invalid state occurs, e.g. entry mismatch or no response from provider,
* we send back a TYPE_NO_CREDENTIAL error as to the developer.
@@ -471,11 +494,12 @@
.STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT
|| e.second.getStatus()
== AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
- );
+ );
}
private class ProviderResponseDataHandler {
- @Nullable private final ComponentName mExpectedRemoteEntryProviderService;
+ @Nullable
+ private final ComponentName mExpectedRemoteEntryProviderService;
@NonNull
private final Map<String, Pair<CredentialEntry, Entry>> mUiCredentialEntries =
new HashMap<>();
@@ -485,7 +509,8 @@
private final Map<String, Pair<Action, AuthenticationEntry>> mUiAuthenticationEntries =
new HashMap<>();
- @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;
+ @Nullable
+ private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null;
ProviderResponseDataHandler(@Nullable ComponentName expectedRemoteEntryProviderService) {
mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService;
@@ -509,6 +534,7 @@
setRemoteEntry(remoteEntry);
}
}
+
public void addCredentialEntry(CredentialEntry credentialEntry) {
String id = generateUniqueId();
Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
@@ -559,7 +585,6 @@
}
-
public GetCredentialProviderData toGetCredentialProviderData() {
return new GetCredentialProviderData.Builder(
mComponentName.flattenToString()).setActionChips(prepareActionEntries())
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 77d4e77..faa91dc 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -32,6 +32,7 @@
import android.util.Log;
import com.android.server.credentials.metrics.CandidatePhaseMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.UUID;
@@ -72,8 +73,9 @@
@NonNull
protected Boolean mProviderResponseSet = false;
// Specific candidate provider metric for the provider this session handles
- @Nullable
- protected CandidatePhaseMetric mCandidatePhasePerProviderMetric;
+ @NonNull
+ protected final CandidatePhaseMetric mCandidatePhasePerProviderMetric =
+ new CandidatePhaseMetric();
@NonNull
private int mProviderSessionUid;
@@ -143,7 +145,6 @@
mUserId = userId;
mComponentName = componentName;
mRemoteCredentialService = remoteCredentialService;
- mCandidatePhasePerProviderMetric = new CandidatePhaseMetric();
mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName);
}
@@ -208,6 +209,12 @@
return mRemoteCredentialService;
}
+ protected void captureCandidateFailure() {
+ mCandidatePhasePerProviderMetric.setHasException(true);
+ // TODO(b/271135048) - this is a true exception, but what about the empty case?
+ // Add more nuance in next iteration.
+ }
+
/** Updates the status . */
protected void updateStatusAndInvokeCallback(@NonNull Status status) {
setStatus(status);
@@ -216,18 +223,37 @@
}
private void updateCandidateMetric(Status status) {
- mCandidatePhasePerProviderMetric.setCandidateUid(mProviderSessionUid);
- // TODO immediately update the candidate phase here to have more new data
- mCandidatePhasePerProviderMetric
- .setQueryFinishTimeNanoseconds(System.nanoTime());
- if (isTerminatingStatus(status)) {
- mCandidatePhasePerProviderMetric.setProviderQueryStatus(
- ProviderStatusForMetrics.QUERY_FAILURE
- .getMetricCode());
- } else if (isCompletionStatus(status)) {
- mCandidatePhasePerProviderMetric.setProviderQueryStatus(
- ProviderStatusForMetrics.QUERY_SUCCESS
- .getMetricCode());
+ try {
+ mCandidatePhasePerProviderMetric.setCandidateUid(mProviderSessionUid);
+ // TODO immediately update the candidate phase here to have more new data
+ mCandidatePhasePerProviderMetric
+ .setQueryFinishTimeNanoseconds(System.nanoTime());
+ if (isTerminatingStatus(status)) {
+ mCandidatePhasePerProviderMetric.setQueryReturned(false);
+ mCandidatePhasePerProviderMetric.setProviderQueryStatus(
+ ProviderStatusForMetrics.QUERY_FAILURE
+ .getMetricCode());
+ } else if (isCompletionStatus(status)) {
+ mCandidatePhasePerProviderMetric.setQueryReturned(true);
+ mCandidatePhasePerProviderMetric.setProviderQueryStatus(
+ ProviderStatusForMetrics.QUERY_SUCCESS
+ .getMetricCode());
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
+ }
+
+ // Common method to transfer metrics from the initial phase to the candidate phase per provider
+ protected void startCandidateMetrics() {
+ try {
+ InitialPhaseMetric initMetric = ((RequestSession) mCallbacks).mInitialPhaseMetric;
+ mCandidatePhasePerProviderMetric.setSessionId(initMetric.getSessionId());
+ mCandidatePhasePerProviderMetric.setServiceBeganTimeNanoseconds(
+ initMetric.getCredentialServiceStartedTimeNanoseconds());
+ mCandidatePhasePerProviderMetric.setStartQueryTimeNanoseconds(System.nanoTime());
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
}
}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 5dc7adf..3ac10c9 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -78,8 +78,8 @@
protected final CancellationSignal mCancellationSignal;
protected final Map<String, ProviderSession> mProviders = new HashMap<>();
- protected InitialPhaseMetric mInitialPhaseMetric = new InitialPhaseMetric();
- protected ChosenProviderFinalPhaseMetric
+ protected final InitialPhaseMetric mInitialPhaseMetric = new InitialPhaseMetric();
+ protected final ChosenProviderFinalPhaseMetric
mChosenProviderFinalPhaseMetric = new ChosenProviderFinalPhaseMetric();
// TODO(b/271135048) - Group metrics used in a scope together, such as here in RequestSession
@@ -123,9 +123,17 @@
mUserId, this);
mHybridService = context.getResources().getString(
R.string.config_defaultCredentialManagerHybridService);
- mInitialPhaseMetric.setCredentialServiceStartedTimeNanoseconds(timestampStarted);
- mInitialPhaseMetric.setSessionId(mRequestId.hashCode());
- mInitialPhaseMetric.setCallerUid(mCallingUid);
+ initialPhaseMetricSetup(timestampStarted);
+ }
+
+ private void initialPhaseMetricSetup(long timestampStarted) {
+ try {
+ mInitialPhaseMetric.setCredentialServiceStartedTimeNanoseconds(timestampStarted);
+ mInitialPhaseMetric.setSessionId(mRequestId.hashCode());
+ mInitialPhaseMetric.setCallerUid(mCallingUid);
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
}
public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
@@ -170,13 +178,17 @@
private void logBrowsingPhasePerSelect(UserSelectionDialogResult selection,
ProviderSession providerSession) {
- CandidateBrowsingPhaseMetric browsingPhaseMetric = new CandidateBrowsingPhaseMetric();
- browsingPhaseMetric.setSessionId(this.mInitialPhaseMetric.getSessionId());
- browsingPhaseMetric.setEntryEnum(
- EntryEnum.getMetricCodeFromString(selection.getEntryKey()));
- browsingPhaseMetric.setProviderUid(providerSession.mCandidatePhasePerProviderMetric
- .getCandidateUid());
- this.mCandidateBrowsingPhaseMetric.add(new CandidateBrowsingPhaseMetric());
+ try {
+ CandidateBrowsingPhaseMetric browsingPhaseMetric = new CandidateBrowsingPhaseMetric();
+ browsingPhaseMetric.setSessionId(this.mInitialPhaseMetric.getSessionId());
+ browsingPhaseMetric.setEntryEnum(
+ EntryEnum.getMetricCodeFromString(selection.getEntryKey()));
+ browsingPhaseMetric.setProviderUid(providerSession.mCandidatePhasePerProviderMetric
+ .getCandidateUid());
+ this.mCandidateBrowsingPhaseMetric.add(new CandidateBrowsingPhaseMetric());
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
}
protected void finishSession(boolean propagateCancellation) {
@@ -233,6 +245,7 @@
Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size());
if (isSessionCancelled()) {
+ MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -248,13 +261,8 @@
}
if (!providerDataList.isEmpty()) {
Log.i(TAG, "provider list not empty about to initiate ui");
- // TODO immediately Add paths to end it (say it fails)
- if (isSessionCancelled()) {
- Log.i(TAG, "In getProviderDataAndInitiateUi but session has been cancelled");
- // TODO immedaitely Add paths
- } else {
- launchUiWithProviderData(providerDataList);
- }
+ MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter);
+ launchUiWithProviderData(providerDataList);
}
}
@@ -264,22 +272,27 @@
* @param componentName the componentName to associate with a provider
*/
protected void setChosenMetric(ComponentName componentName) {
- CandidatePhaseMetric metric = this.mProviders.get(componentName.flattenToString())
- .mCandidatePhasePerProviderMetric;
+ try {
+ CandidatePhaseMetric metric = this.mProviders.get(componentName.flattenToString())
+ .mCandidatePhasePerProviderMetric;
- mChosenProviderFinalPhaseMetric.setSessionId(metric.getSessionId());
- mChosenProviderFinalPhaseMetric.setChosenUid(metric.getCandidateUid());
+ mChosenProviderFinalPhaseMetric.setSessionId(metric.getSessionId());
+ mChosenProviderFinalPhaseMetric.setChosenUid(metric.getCandidateUid());
- mChosenProviderFinalPhaseMetric.setQueryPhaseLatencyMicroseconds(
- metric.getQueryLatencyMicroseconds());
+ mChosenProviderFinalPhaseMetric.setQueryPhaseLatencyMicroseconds(
+ metric.getQueryLatencyMicroseconds());
- mChosenProviderFinalPhaseMetric.setServiceBeganTimeNanoseconds(
- metric.getServiceBeganTimeNanoseconds());
- mChosenProviderFinalPhaseMetric.setQueryStartTimeNanoseconds(
- metric.getStartQueryTimeNanoseconds());
+ mChosenProviderFinalPhaseMetric.setServiceBeganTimeNanoseconds(
+ metric.getServiceBeganTimeNanoseconds());
+ mChosenProviderFinalPhaseMetric.setQueryStartTimeNanoseconds(
+ metric.getStartQueryTimeNanoseconds());
- // TODO immediately update with the entry count numbers from the candidate metrics
+ // TODO immediately update with the entry count numbers from the candidate metrics
+ // TODO immediately add the exception bit for candidates and providers
- mChosenProviderFinalPhaseMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
+ mChosenProviderFinalPhaseMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
}
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
index c392d78..f00c7f4 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -20,6 +20,9 @@
import com.android.server.credentials.MetricUtilities;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* The central candidate provider metric object that mimics our defined metric setup.
* Some types are redundant across these metric collectors, but that has debug use-cases as
@@ -66,6 +69,8 @@
private int mRemoteEntryCount = -1;
// The count of authentication entries from this provider, defaults to -1
private int mAuthenticationEntryCount = -1;
+ // Gathered to pass on to chosen provider when required
+ private List<EntryEnum> mAvailableEntries = new ArrayList<>();
public CandidatePhaseMetric() {
}
@@ -236,4 +241,28 @@
public int getAuthenticationEntryCount() {
return mAuthenticationEntryCount;
}
+
+ /* -------------- The Entries Gathered ---------------- */
+
+ /**
+ * Allows adding an entry record to this metric collector, which can then be propagated to
+ * the final phase to retain information on the data available to the candidate.
+ *
+ * @param e the entry enum collected by the candidate provider associated with this metric
+ * collector
+ */
+ public void addEntry(EntryEnum e) {
+ this.mAvailableEntries.add(e);
+ }
+
+ /**
+ * Returns a safely copied list of the entries captured by this metric collector associated
+ * with a particular candidate provider.
+ *
+ * @return the full collection of entries encountered by the candidate provider associated with
+ * this metric
+ */
+ public List<EntryEnum> getAvailableEntries() {
+ return new ArrayList<>(this.mAvailableEntries); // no alias copy
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a1789b2..720533f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -95,6 +95,7 @@
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY;
import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_FINANCING_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
@@ -15440,7 +15441,8 @@
Slogf.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction());
broadcastIntentToCrossProfileManifestReceivers(
intent, parentHandle, requiresPermission);
- broadcastIntentToDevicePolicyManagerRoleHolder(intent, parentHandle);
+ broadcastExplicitIntentToRoleHolder(
+ intent, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, parentHandle);
}
@Override
@@ -15481,36 +15483,6 @@
}
}
- private void broadcastIntentToDevicePolicyManagerRoleHolder(
- Intent intent, UserHandle userHandle) {
- final int userId = userHandle.getIdentifier();
- final String packageName = getDevicePolicyManagementRoleHolderPackageName(mContext);
- if (packageName == null) {
- return;
- }
- try {
- final Intent packageIntent = new Intent(intent)
- .setPackage(packageName);
- final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers(
- packageIntent,
- /* resolvedType= */ null,
- STOCK_PM_FLAGS,
- userId).getList();
- if (receivers.isEmpty()) {
- return;
- }
- for (ResolveInfo receiver : receivers) {
- final Intent componentIntent = new Intent(packageIntent)
- .setComponent(receiver.getComponentInfo().getComponentName())
- .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mContext.sendBroadcastAsUser(componentIntent, userHandle);
- }
- } catch (RemoteException ex) {
- Slogf.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.",
- intent.getAction(), ex);
- }
- }
-
/**
* Checks whether the package {@code packageName} has the {@code MODIFY_QUIET_MODE}
* permission granted for the user {@code userId}.
@@ -20718,7 +20690,7 @@
private void maybeInstallDevicePolicyManagementRoleHolderInUser(int targetUserId) {
String devicePolicyManagerRoleHolderPackageName =
- getDevicePolicyManagementRoleHolderPackageName(mContext);
+ getRoleHolderPackageName(mContext, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
if (devicePolicyManagerRoleHolderPackageName == null) {
Slogf.d(LOG_TAG, "No device policy management role holder specified.");
return;
@@ -20744,14 +20716,24 @@
}
}
+ /**
+ * If multiple packages hold the role, returns the first package in the list.
+ */
+ @Nullable
+ private String getRoleHolderPackageName(Context context, String role) {
+ return getRoleHolderPackageNameOnUser(context, role, Process.myUserHandle());
+ }
- private String getDevicePolicyManagementRoleHolderPackageName(Context context) {
+ /**
+ * If multiple packages hold the role, returns the first package in the list.
+ */
+ @Nullable
+ private String getRoleHolderPackageNameOnUser(Context context, String role, UserHandle user) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
// Calling identity needs to be cleared as this method is used in the permissions checks.
return mInjector.binderWithCleanCallingIdentity(() -> {
- List<String> roleHolders =
- roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
+ List<String> roleHolders = roleManager.getRoleHoldersAsUser(role, user);
if (roleHolders.isEmpty()) {
return null;
}
@@ -20762,7 +20744,7 @@
private boolean isCallerDevicePolicyManagementRoleHolder(CallerIdentity caller) {
int callerUid = caller.getUid();
String devicePolicyManagementRoleHolderPackageName =
- getDevicePolicyManagementRoleHolderPackageName(mContext);
+ getRoleHolderPackageName(mContext, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT);
int roleHolderUid = mInjector.getPackageManagerInternal().getPackageUid(
devicePolicyManagementRoleHolderPackageName, 0, caller.getUserId());
@@ -21830,15 +21812,23 @@
}
public void register() {
- mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.SYSTEM);
+ mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL);
}
@Override
public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
- if (!RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) {
+ if (RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) {
+ handleDevicePolicyManagementRoleChange(user);
return;
}
- String newRoleHolder = getRoleHolder();
+ if (RoleManager.ROLE_FINANCED_DEVICE_KIOSK.equals(roleName)) {
+ handleFinancedDeviceKioskRoleChange();
+ return;
+ }
+ }
+
+ private void handleDevicePolicyManagementRoleChange(UserHandle user) {
+ String newRoleHolder = getDeviceManagementRoleHolder(user);
if (isDefaultRoleHolder(newRoleHolder)) {
Slogf.i(LOG_TAG,
"onRoleHoldersChanged: Default role holder is set, returning early");
@@ -21873,9 +21863,42 @@
}
}
- private String getRoleHolder() {
- return DevicePolicyManagerService.this.getDevicePolicyManagementRoleHolderPackageName(
- mContext);
+ private void handleFinancedDeviceKioskRoleChange() {
+ if (!isDevicePolicyEngineEnabled()) {
+ return;
+ }
+ Slog.i(LOG_TAG, "Handling action " + ACTION_DEVICE_FINANCING_STATE_CHANGED);
+ Intent intent = new Intent(ACTION_DEVICE_FINANCING_STATE_CHANGED);
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getUsers()) {
+ UserHandle user = userInfo.getUserHandle();
+ broadcastExplicitIntentToRoleHolder(
+ intent, RoleManager.ROLE_SYSTEM_SUPERVISION, user);
+ broadcastExplicitIntentToRoleHolder(
+ intent, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, user);
+ ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(user.getIdentifier());
+ if (admin == null) {
+ continue;
+ }
+ if (!isProfileOwnerOfOrganizationOwnedDevice(
+ admin.info.getComponent(), user.getIdentifier())
+ && !isDeviceOwner(admin)) {
+ continue;
+ }
+ // Don't send the broadcast twice if the DPC is the same package as the
+ // DMRH
+ if (admin.info.getPackageName().equals(getDeviceManagementRoleHolder(user))) {
+ continue;
+ }
+ broadcastExplicitIntentToPackage(
+ intent, admin.info.getPackageName(), admin.getUserHandle());
+ }
+ });
+ }
+
+ private String getDeviceManagementRoleHolder(UserHandle user) {
+ return DevicePolicyManagerService.this.getRoleHolderPackageNameOnUser(
+ mContext, RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, user);
}
private boolean isDefaultRoleHolder(String packageName) {
@@ -21935,6 +21958,40 @@
}
}
+ private void broadcastExplicitIntentToRoleHolder(
+ Intent intent, String role, UserHandle userHandle) {
+ String packageName = getRoleHolderPackageNameOnUser(mContext, role, userHandle);
+ if (packageName == null) {
+ return;
+ }
+ broadcastExplicitIntentToPackage(intent, packageName, userHandle);
+ }
+
+ private void broadcastExplicitIntentToPackage(
+ Intent intent, String packageName, UserHandle userHandle) {
+ int userId = userHandle.getIdentifier();
+ if (packageName == null) {
+ return;
+ }
+ Intent packageIntent = new Intent(intent)
+ .setPackage(packageName);
+ List<ResolveInfo> receivers = mContext.getPackageManager().queryBroadcastReceiversAsUser(
+ packageIntent,
+ PackageManager.ResolveInfoFlags.of(PackageManager.GET_RECEIVERS),
+ userId);
+ if (receivers.isEmpty()) {
+ Slog.i(LOG_TAG, "Found no receivers to handle intent " + intent
+ + " in package " + packageName);
+ return;
+ }
+ for (ResolveInfo receiver : receivers) {
+ Intent componentIntent = new Intent(packageIntent)
+ .setComponent(receiver.getComponentInfo().getComponentName())
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(componentIntent, userHandle);
+ }
+ }
+
@Override
public List<UserHandle> getPolicyManagedProfiles(@NonNull UserHandle user) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 7e44049..7d4f87d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -17,6 +17,7 @@
package com.android.server.inputmethod;
import static android.inputmethodservice.InputMethodService.IME_ACTIVE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT;
@@ -35,11 +36,16 @@
import static org.mockito.Mockito.verify;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,8 +66,8 @@
super.setUp();
mVisibilityApplier =
(DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
- mInputMethodManagerService.mCurFocusedWindowClient = mock(
- InputMethodManagerService.ClientState.class);
+ mInputMethodManagerService.setAttachedClientForTesting(
+ mock(InputMethodManagerService.ClientState.class));
}
@Test
@@ -119,4 +125,38 @@
mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT);
verifyShowSoftInput(true, true, InputMethodManager.SHOW_IMPLICIT);
}
+
+ @Test
+ public void testApplyImeVisibility_hideImeFromTargetOnSecondaryDisplay() {
+ // Init a IME target client on the secondary display to show IME.
+ mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection,
+ 10 /* selfReportedDisplayId */);
+ mInputMethodManagerService.setAttachedClientForTesting(null);
+ startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+
+ synchronized (ImfLock.class) {
+ final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ // Verify hideIme will apply the expected displayId when the default IME
+ // visibility applier app STATE_HIDE_IME.
+ mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME);
+ verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
+ eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ }
+ }
+
+ private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
+ return mInputMethodManagerService.startInputOrWindowGainedFocus(
+ StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
+ mMockInputMethodClient /* client */,
+ windowToken /* windowToken */,
+ StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR,
+ softInputMode /* softInputMode */,
+ 0 /* windowFlags */,
+ mEditorInfo /* editorInfo */,
+ mMockRemoteInputConnection /* inputConnection */,
+ mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
+ mTargetSdkVersion /* unverifiedTargetSdkVersion */,
+ mCallingUserId /* userId */,
+ mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 5377ee7..3a47b47 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -50,7 +50,6 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -624,8 +623,7 @@
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_INACTIVE);
- verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
+ verify(mDeviceIdleController).scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT));
}
@Test
@@ -643,8 +641,7 @@
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_INACTIVE);
- verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
+ verify(mDeviceIdleController).scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT));
// The device configuration doesn't require a motion sensor to proceed with idling.
// This should be the case on TVs or other such devices. We should set an alarm to move
// forward if the motion sensor is missing in this case.
@@ -669,8 +666,7 @@
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_INACTIVE);
- verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
+ verify(mDeviceIdleController).scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT));
// The device configuration requires a motion sensor to proceed with idling,
// so we should never set an alarm to move forward if the motion sensor is
// missing in this case.
@@ -699,7 +695,7 @@
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_INACTIVE);
inOrder.verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(timeUntilAlarm + mConstants.INACTIVE_TIMEOUT), eq(false));
+ .scheduleAlarmLocked(eq(timeUntilAlarm + mConstants.INACTIVE_TIMEOUT));
enterDeepState(STATE_ACTIVE);
setQuickDozeEnabled(true);
@@ -709,7 +705,7 @@
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
inOrder.verify(mDeviceIdleController).scheduleAlarmLocked(
- eq(timeUntilAlarm + mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
+ eq(timeUntilAlarm + mConstants.QUICK_DOZE_DELAY_TIMEOUT));
}
@Test
@@ -736,59 +732,56 @@
setScreenOn(false);
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
inOrder.verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
+ .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT));
enterDeepState(STATE_INACTIVE);
setQuickDozeEnabled(true);
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
inOrder.verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
+ .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT));
enterDeepState(STATE_IDLE_PENDING);
setQuickDozeEnabled(true);
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
inOrder.verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
+ .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT));
enterDeepState(STATE_SENSING);
setQuickDozeEnabled(true);
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
inOrder.verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
+ .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT));
enterDeepState(STATE_LOCATING);
setQuickDozeEnabled(true);
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
inOrder.verify(mDeviceIdleController)
- .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
+ .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT));
// IDLE should stay as IDLE.
enterDeepState(STATE_IDLE);
// Clear out any alarm setting from the order before checking for this section.
- inOrder.verify(mDeviceIdleController, atLeastOnce())
- .scheduleAlarmLocked(anyLong(), anyBoolean());
+ inOrder.verify(mDeviceIdleController, atLeastOnce()).scheduleAlarmLocked(anyLong());
setQuickDozeEnabled(true);
verifyStateConditions(STATE_IDLE);
- inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
+ inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong());
// IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE.
enterDeepState(STATE_IDLE_MAINTENANCE);
// Clear out any alarm setting from the order before checking for this section.
- inOrder.verify(mDeviceIdleController, atLeastOnce())
- .scheduleAlarmLocked(anyLong(), anyBoolean());
+ inOrder.verify(mDeviceIdleController, atLeastOnce()).scheduleAlarmLocked(anyLong());
setQuickDozeEnabled(true);
verifyStateConditions(STATE_IDLE_MAINTENANCE);
- inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
+ inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong());
// State is already QUICK_DOZE_DELAY. No work should be done.
enterDeepState(STATE_QUICK_DOZE_DELAY);
// Clear out any alarm setting from the order before checking for this section.
- inOrder.verify(mDeviceIdleController, atLeastOnce())
- .scheduleAlarmLocked(anyLong(), anyBoolean());
+ inOrder.verify(mDeviceIdleController, atLeastOnce()).scheduleAlarmLocked(anyLong());
setQuickDozeEnabled(true);
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_QUICK_DOZE_DELAY);
- inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
+ inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong());
}
@Test
@@ -2685,17 +2678,12 @@
if (ret == mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK) {
enterDeepState(STATE_IDLE);
long now = SystemClock.elapsedRealtime();
- long alarm = mDeviceIdleController.getNextAlarmTime();
mDeviceIdleController.setIdleStartTimeForTest(
now - (long) (mConstants.IDLE_TIMEOUT * 0.6));
- long newAlarm = mDeviceIdleController.getNextAlarmTime();
- assertTrue("maintenance not reschedule IDLE_TIMEOUT * 0.6",
- newAlarm == alarm);
+ verifyStateConditions(STATE_IDLE);
mDeviceIdleController.setIdleStartTimeForTest(
now - (long) (mConstants.IDLE_TIMEOUT * 1.2));
- newAlarm = mDeviceIdleController.getNextAlarmTime();
- assertTrue("maintenance not reschedule IDLE_TIMEOUT * 1.2",
- (newAlarm - now) < minuteInMillis);
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
mDeviceIdleController.resetPreIdleTimeoutMode();
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index e7e26a1..1741411 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -28,7 +28,6 @@
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
-import static android.app.AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT;
import static android.app.AlarmManager.WINDOW_EXACT;
import static android.app.AlarmManager.WINDOW_HEURISTIC;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -126,7 +125,6 @@
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
-import android.app.role.RoleManager;
import android.app.tare.EconomyManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
@@ -134,7 +132,6 @@
import android.content.Intent;
import android.content.PermissionChecker;
import android.content.pm.PackageManagerInternal;
-import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Bundle;
@@ -192,7 +189,6 @@
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -246,8 +242,6 @@
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
- private RoleManager mRoleManager;
- @Mock
private AppStateTrackerImpl mAppStateTracker;
@Mock
private AlarmManagerService.ClockReceiver mClockReceiver;
@@ -393,11 +387,6 @@
}
@Override
- void registerContentObserver(ContentObserver observer, Uri uri) {
- // Do nothing.
- }
-
- @Override
void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
// Do nothing.
// The tests become flaky with an error message of
@@ -484,10 +473,12 @@
doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
() -> PermissionChecker.checkPermissionForPreflight(any(),
eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), anyInt(), anyString()));
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkPermissionForPreflight(any(), eq(SCHEDULE_EXACT_ALARM),
+ anyInt(), anyInt(), anyString()));
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager);
- when(mMockContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager);
registerAppIds(new String[]{TEST_CALLING_PACKAGE},
new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
@@ -2181,40 +2172,6 @@
}
}
- @Test
- public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException {
- mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
-
- mockScheduleExactAlarmState(true, false, MODE_DEFAULT);
- assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
- assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmState(true, false, MODE_IGNORED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- }
-
- @Test
- public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException {
- mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
-
- mockScheduleExactAlarmState(true, true, MODE_ERRORED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmState(true, true, MODE_DEFAULT);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmState(true, true, MODE_IGNORED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmState(true, true, MODE_ALLOWED);
- assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- }
-
private void mockChangeEnabled(long changeId, boolean enabled) {
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
any(UserHandle.class)));
@@ -2222,16 +2179,62 @@
}
@Test
- public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException {
+ public void hasScheduleExactAlarmBinderCall() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
+
+ mockScheduleExactAlarmState(true);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmState(false);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmBinderCallNotDenyListedPreT() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmState(false, false, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_DEFAULT);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockScheduleExactAlarmState(false, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_IGNORED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmBinderCallDenyListedPreT() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+
+ mockScheduleExactAlarmStatePreT(true, true, MODE_ERRORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockScheduleExactAlarmState(false, true, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmStatePreT(true, true, MODE_IGNORED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmStatePreT(true, true, MODE_ALLOWED);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmBinderCallNotDeclaredPreT() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+
+ mockScheduleExactAlarmStatePreT(false, false, MODE_DEFAULT);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmStatePreT(false, false, MODE_ALLOWED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockScheduleExactAlarmStatePreT(false, true, MODE_ALLOWED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
@@ -2240,61 +2243,94 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
// canScheduleExactAlarms should be true regardless of any permission state.
- mockUseExactAlarmState(true);
+ // Both SEA and UEA are denied in setUp.
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
mockUseExactAlarmState(false);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
- mockScheduleExactAlarmState(false, true, MODE_DEFAULT);
- assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
-
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmState(false);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
}
@Test
- public void canScheduleExactAlarmsBinderCall() throws RemoteException {
+ public void canScheduleExactAlarmsBinderCallPreT() throws RemoteException {
// Policy permission is denied in setUp().
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
// No permission, no exemption.
- mockScheduleExactAlarmState(true, true, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// No permission, no exemption.
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// Policy permission only, no exemption.
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
mockUseExactAlarmState(true);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
mockUseExactAlarmState(false);
// User permission only, no exemption.
- mockScheduleExactAlarmState(true, false, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_DEFAULT);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// User permission only, no exemption.
- mockScheduleExactAlarmState(true, true, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, true, MODE_ALLOWED);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// No permission, exemption.
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// No permission, exemption.
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false);
doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID));
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// Both permissions and exemption.
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockUseExactAlarmState(true);
+ assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
+ }
+
+ @Test
+ public void canScheduleExactAlarmsBinderCall() throws RemoteException {
+ // Both permissions are denied in setUp().
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
+
+ // No permission, no exemption.
+ assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
+
+ // Policy permission only, no exemption.
+ mockUseExactAlarmState(true);
+ assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
+
+ mockUseExactAlarmState(false);
+
+ // User permission only, no exemption.
+ mockScheduleExactAlarmState(true);
+ assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
+
+ // No permission, exemption.
+ mockScheduleExactAlarmState(false);
+ when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true);
+ assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
+
+ // No permission, core uid exemption.
+ when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false);
+ doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID));
+ assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
+
+ // Both permissions and core uid exemption.
+ mockScheduleExactAlarmState(true);
mockUseExactAlarmState(true);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
}
@@ -2404,8 +2440,9 @@
@Test
public void alarmClockBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmState(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
@@ -2431,9 +2468,10 @@
public void alarmClockBinderCallWithUEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
mockUseExactAlarmState(true);
- mockScheduleExactAlarmState(false, false, MODE_ERRORED);
+ mockScheduleExactAlarmState(false);
final PendingIntent alarmPi = getNewMockPendingIntent();
final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
@@ -2455,7 +2493,7 @@
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
- private void mockScheduleExactAlarmState(boolean declared, boolean denyList, int mode) {
+ private void mockScheduleExactAlarmStatePreT(boolean declared, boolean denyList, int mode) {
String[] requesters = declared ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING;
when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM))
.thenReturn(requesters);
@@ -2470,6 +2508,20 @@
TEST_CALLING_PACKAGE)).thenReturn(mode);
}
+ private void mockScheduleExactAlarmState(boolean granted) {
+ String[] requesters = granted ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING;
+ when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM))
+ .thenReturn(requesters);
+ mService.refreshExactAlarmCandidates();
+
+ final int result = granted ? PermissionChecker.PERMISSION_GRANTED
+ : PermissionChecker.PERMISSION_HARD_DENIED;
+ doReturn(result).when(
+ () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext),
+ eq(SCHEDULE_EXACT_ALARM), anyInt(), eq(TEST_CALLING_UID),
+ eq(TEST_CALLING_PACKAGE)));
+ }
+
private void mockUseExactAlarmState(boolean granted) {
final int result = granted ? PermissionChecker.PERMISSION_GRANTED
: PermissionChecker.PERMISSION_HARD_DENIED;
@@ -2483,7 +2535,7 @@
public void alarmClockBinderCallWithoutPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2504,8 +2556,9 @@
@Test
public void exactBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmState(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
0, alarmPi, null, null, null, null);
@@ -2529,9 +2582,10 @@
public void exactBinderCallWithUEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
mockUseExactAlarmState(true);
- mockScheduleExactAlarmState(false, false, MODE_ERRORED);
+ mockScheduleExactAlarmState(false);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
0, alarmPi, null, null, null, null);
@@ -2555,7 +2609,7 @@
public void exactBinderCallWithAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
// If permission is denied, only then allowlist will be checked.
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2575,7 +2629,7 @@
public void exactAllowWhileIdleBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
@@ -2601,7 +2655,7 @@
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
mockUseExactAlarmState(true);
- mockScheduleExactAlarmState(false, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(false, false, MODE_ERRORED);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
@@ -2625,7 +2679,7 @@
public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
// If permission is denied, only then allowlist will be checked.
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2651,7 +2705,7 @@
public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2701,7 +2755,7 @@
public void binderCallWithUserAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true);
@@ -3026,7 +3080,7 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
@@ -3039,7 +3093,7 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
@@ -3052,7 +3106,7 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED);
- mockScheduleExactAlarmState(true, true, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
@@ -3068,7 +3122,7 @@
when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED);
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -3328,7 +3382,7 @@
.putExtra(Intent.EXTRA_REPLACING, true);
mockUseExactAlarmState(false);
- mockScheduleExactAlarmState(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent);
assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE);
@@ -3336,7 +3390,7 @@
assertEquals(5, mService.mAlarmStore.size());
mockUseExactAlarmState(true);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent);
assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE);
@@ -3344,7 +3398,7 @@
assertEquals(5, mService.mAlarmStore.size());
mockUseExactAlarmState(false);
- mockScheduleExactAlarmState(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent);
assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE);
@@ -3363,55 +3417,6 @@
}
@Test
- public void isScheduleExactAlarmAllowedByDefault() {
- final String package1 = "priv";
- final String package2 = "signed";
- final String package3 = "normal";
- final String package4 = "wellbeing";
- final int uid1 = 1294;
- final int uid2 = 8321;
- final int uid3 = 3412;
- final int uid4 = 4591;
-
- when(mPackageManagerInternal.isUidPrivileged(uid1)).thenReturn(true);
- when(mPackageManagerInternal.isUidPrivileged(uid2)).thenReturn(false);
- when(mPackageManagerInternal.isUidPrivileged(uid3)).thenReturn(false);
- when(mPackageManagerInternal.isUidPrivileged(uid4)).thenReturn(false);
-
- when(mPackageManagerInternal.isPlatformSigned(package1)).thenReturn(false);
- when(mPackageManagerInternal.isPlatformSigned(package2)).thenReturn(true);
- when(mPackageManagerInternal.isPlatformSigned(package3)).thenReturn(false);
- when(mPackageManagerInternal.isPlatformSigned(package4)).thenReturn(false);
-
- when(mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)).thenReturn(
- Arrays.asList(package4));
-
- mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false);
- mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{
- package1,
- package3,
- });
-
- // Deny listed packages will be false.
- assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
- assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
- assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
- assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));
-
- mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
- mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{
- package1,
- package3,
- });
-
- // Deny list doesn't matter now, only exemptions should be true.
- assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1));
- assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2));
- assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3));
- assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4));
- }
-
- @Test
public void alarmScheduledAtomPushed() {
for (int i = 0; i < 10; i++) {
final PendingIntent pi = getNewMockPendingIntent();
@@ -3510,7 +3515,7 @@
}
@Test
- public void hasUseExactAlarmPermission() {
+ public void hasUseExactAlarmInternal() {
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
mockUseExactAlarmState(true);
@@ -3521,7 +3526,7 @@
}
@Test
- public void hasUseExactAlarmPermissionChangeDisabled() {
+ public void hasUseExactAlarmInternalChangeDisabled() {
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, false);
mockUseExactAlarmState(true);
@@ -3532,6 +3537,49 @@
}
@Test
+ public void hasScheduleExactAlarmInternal() {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true);
+
+ mockScheduleExactAlarmState(false);
+ assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+
+ mockScheduleExactAlarmState(true);
+ assertTrue(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmInternalPreT() {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false);
+
+ mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
+ assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+
+ mockScheduleExactAlarmStatePreT(false, false, MODE_ALLOWED);
+ assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ assertTrue(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmInternalPreS() {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
+
+ mockScheduleExactAlarmState(true);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+
+ mockScheduleExactAlarmState(false);
+ mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+ }
+
+ @Test
public void temporaryQuotaReserve_hasQuota() {
final int quotaToFill = 5;
final String package1 = "package1";
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 8b420a3..e056417 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -50,6 +50,7 @@
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
+import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -91,6 +92,7 @@
public class JobSchedulerServiceTest {
private static final String TAG = JobSchedulerServiceTest.class.getSimpleName();
+ private static final int TEST_UID = 10123;
private JobSchedulerService mService;
@@ -177,6 +179,9 @@
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
+ mService.cancelJobsForUid(TEST_UID, true,
+ JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN,
+ "test cleanup");
}
private Clock getAdvancedClock(Clock clock, long incrementMs) {
@@ -257,9 +262,9 @@
ConnectivityController connectivityController = mService.getConnectivityController();
spyOn(connectivityController);
mService.mConstants.RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
- mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 1.5f;
- mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS = HOUR_IN_MILLIS;
- mService.mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS = 6 * HOUR_IN_MILLIS;
+ mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = 1.5f;
+ mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = HOUR_IN_MILLIS;
+ mService.mConstants.RUNTIME_UI_DATA_TRANSFER_LIMIT_MS = 6 * HOUR_IN_MILLIS;
assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS,
mService.getMinJobExecutionGuaranteeMs(ejMax));
@@ -279,26 +284,26 @@
assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMinJobExecutionGuaranteeMs(jobUIDT));
grantRunUserInitiatedJobsPermission(true); // With permission
- assertEquals(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
mService.getMinJobExecutionGuaranteeMs(jobUIDT));
doReturn(ConnectivityController.UNKNOWN_TIME)
.when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
mService.getMinJobExecutionGuaranteeMs(jobUIDT));
- doReturn(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS / 2)
+ doReturn(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS / 2)
.when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
mService.getMinJobExecutionGuaranteeMs(jobUIDT));
- doReturn(mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS * 2)
+ doReturn(mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS * 2)
.when(connectivityController).getEstimatedTransferTimeMs(any());
assertEquals(
- (long) (mService.mConstants.RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_MS
+ (long) (mService.mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS
* 2 * mService.mConstants
- .RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR),
+ .RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR),
mService.getMinJobExecutionGuaranteeMs(jobUIDT));
- doReturn(mService.mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS * 2)
+ doReturn(mService.mConstants.RUNTIME_UI_DATA_TRANSFER_LIMIT_MS * 2)
.when(connectivityController).getEstimatedTransferTimeMs(any());
- assertEquals(mService.mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_UI_DATA_TRANSFER_LIMIT_MS,
mService.getMinJobExecutionGuaranteeMs(jobUIDT));
}
@@ -320,7 +325,7 @@
.when(quotaController).getMaxJobExecutionTimeMsLocked(any());
grantRunUserInitiatedJobsPermission(true);
- assertEquals(mService.mConstants.RUNTIME_USER_INITIATED_DATA_TRANSFER_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_UI_DATA_TRANSFER_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUIDT));
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
@@ -1170,7 +1175,7 @@
i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE;
assertEquals("Got unexpected result for schedule #" + (i + 1),
expected,
- mService.scheduleAsPackage(job, null, 10123, null, 0, "JSSTest", ""));
+ mService.scheduleAsPackage(job, null, TEST_UID, null, 0, "JSSTest", ""));
}
}
@@ -1191,7 +1196,7 @@
for (int i = 0; i < 500; ++i) {
assertEquals("Got unexpected result for schedule #" + (i + 1),
JobScheduler.RESULT_SUCCESS,
- mService.scheduleAsPackage(job, null, 10123, null, 0, "JSSTest", ""));
+ mService.scheduleAsPackage(job, null, TEST_UID, null, 0, "JSSTest", ""));
}
}
@@ -1212,7 +1217,7 @@
for (int i = 0; i < 500; ++i) {
assertEquals("Got unexpected result for schedule #" + (i + 1),
JobScheduler.RESULT_SUCCESS,
- mService.scheduleAsPackage(job, null, 10123, "proxied.package", 0, "JSSTest",
+ mService.scheduleAsPackage(job, null, TEST_UID, "proxied.package", 0, "JSSTest",
""));
}
}
@@ -1236,11 +1241,63 @@
i < 300 ? JobScheduler.RESULT_SUCCESS : JobScheduler.RESULT_FAILURE;
assertEquals("Got unexpected result for schedule #" + (i + 1),
expected,
- mService.scheduleAsPackage(job, null, 10123, job.getService().getPackageName(),
+ mService.scheduleAsPackage(job, null, TEST_UID,
+ job.getService().getPackageName(),
0, "JSSTest", ""));
}
}
+ /**
+ * Tests that the number of persisted JobWorkItems is capped.
+ */
+ @Test
+ public void testScheduleLimiting_JobWorkItems_Nonpersisted() {
+ mService.mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 500;
+ mService.mConstants.ENABLE_API_QUOTAS = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
+ mService.updateQuotaTracker();
+
+ final JobInfo job = createJobInfo().setPersisted(false).build();
+ final JobWorkItem item = new JobWorkItem.Builder().build();
+ for (int i = 0; i < 1000; ++i) {
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ JobScheduler.RESULT_SUCCESS,
+ mService.scheduleAsPackage(job, item, TEST_UID,
+ job.getService().getPackageName(),
+ 0, "JSSTest", ""));
+ }
+ }
+
+ /**
+ * Tests that the number of persisted JobWorkItems is capped.
+ */
+ @Test
+ public void testScheduleLimiting_JobWorkItems_Persisted() {
+ mService.mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 500;
+ mService.mConstants.ENABLE_API_QUOTAS = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false;
+ mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
+ mService.updateQuotaTracker();
+
+ final JobInfo job = createJobInfo().setPersisted(true).build();
+ final JobWorkItem item = new JobWorkItem.Builder().build();
+ for (int i = 0; i < 500; ++i) {
+ assertEquals("Got unexpected result for schedule #" + (i + 1),
+ JobScheduler.RESULT_SUCCESS,
+ mService.scheduleAsPackage(job, item, TEST_UID,
+ job.getService().getPackageName(),
+ 0, "JSSTest", ""));
+ }
+ try {
+ mService.scheduleAsPackage(job, item, TEST_UID, job.getService().getPackageName(),
+ 0, "JSSTest", "");
+ fail("Added more items than allowed");
+ } catch (IllegalStateException expected) {
+ // Success
+ }
+ }
+
/** Tests that jobs are removed from the pending list if the user stops the app. */
@Test
public void testUserStopRemovesPending() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
new file mode 100644
index 0000000..fd9dfe8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023 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.location.gnss;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.location.GnssMeasurementRequest;
+import android.location.IGnssMeasurementsListener;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.util.identity.CallerIdentity;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.FakeUserInfoHelper;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Objects;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssMeasurementsProviderTest {
+ private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID;
+ private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1000,
+ "mypackage", "attribution", "listener");
+ private static final GnssConfiguration.HalInterfaceVersion HIDL_V2_1 =
+ new GnssConfiguration.HalInterfaceVersion(
+ 2, 1);
+ private static final GnssConfiguration.HalInterfaceVersion AIDL_V3 =
+ new GnssConfiguration.HalInterfaceVersion(
+ GnssConfiguration.HalInterfaceVersion.AIDL_INTERFACE, 3);
+ private static final GnssMeasurementRequest ACTIVE_REQUEST =
+ new GnssMeasurementRequest.Builder().build();
+ private static final GnssMeasurementRequest PASSIVE_REQUEST =
+ new GnssMeasurementRequest.Builder().setIntervalMillis(
+ GnssMeasurementRequest.PASSIVE_INTERVAL).build();
+ private @Mock Context mContext;
+ private @Mock LocationManagerInternal mInternal;
+ private @Mock GnssConfiguration mMockConfiguration;
+ private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
+ private @Mock IGnssMeasurementsListener mListener1;
+ private @Mock IGnssMeasurementsListener mListener2;
+ private @Mock IBinder mBinder1;
+ private @Mock IBinder mBinder2;
+
+ private GnssNative mGnssNative;
+
+ private GnssMeasurementsProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(mBinder1).when(mListener1).asBinder();
+ doReturn(mBinder2).when(mListener2).asBinder();
+ doReturn(true).when(mInternal).isProviderEnabledForUser(eq(LocationManager.GPS_PROVIDER),
+ anyInt());
+ LocalServices.addService(LocationManagerInternal.class, mInternal);
+ FakeGnssHal fakeGnssHal = new FakeGnssHal();
+ GnssNative.setGnssHalForTest(fakeGnssHal);
+ Injector injector = new TestInjector(mContext);
+ mGnssNative = spy(Objects.requireNonNull(
+ GnssNative.create(injector, mMockConfiguration)));
+ mGnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
+ mTestProvider = new GnssMeasurementsProvider(injector, mGnssNative);
+ mGnssNative.register();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(LocationManagerInternal.class);
+ }
+
+ @Test
+ public void testAddListener_active() {
+ // add the active request
+ mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, times(1)).startMeasurementCollection(
+ eq(ACTIVE_REQUEST.isFullTracking()),
+ eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()),
+ eq(ACTIVE_REQUEST.getIntervalMillis()));
+
+ // remove the active request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+ }
+
+ @Test
+ public void testAddListener_passive() {
+ // add the passive request
+ mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(),
+ anyInt());
+
+ // remove the passive request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+ }
+
+ @Test
+ public void testReregister_aidlV3Plus() {
+ doReturn(AIDL_V3).when(mMockConfiguration).getHalInterfaceVersion();
+
+ // add the passive request
+ mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(),
+ anyInt());
+
+ // add the active request, reregister with the active request
+ mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener2);
+ verify(mGnssNative, never()).stopMeasurementCollection();
+ verify(mGnssNative, times(1)).startMeasurementCollection(
+ eq(ACTIVE_REQUEST.isFullTracking()),
+ eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()),
+ eq(ACTIVE_REQUEST.getIntervalMillis()));
+
+ // remove the active request, reregister with the passive request
+ mTestProvider.removeListener(mListener2);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+
+ // remove the passive request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(2)).stopMeasurementCollection();
+ }
+
+ @Test
+ public void testReregister_preAidlV3() {
+ doReturn(HIDL_V2_1).when(mMockConfiguration).getHalInterfaceVersion();
+
+ // add the passive request
+ mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(),
+ anyInt());
+
+ // add the active request, reregister with the active request
+ mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener2);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+ verify(mGnssNative, times(1)).startMeasurementCollection(
+ eq(ACTIVE_REQUEST.isFullTracking()),
+ eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()),
+ eq(ACTIVE_REQUEST.getIntervalMillis()));
+
+ // remove the active request, reregister with the passive request
+ mTestProvider.removeListener(mListener2);
+ verify(mGnssNative, times(2)).stopMeasurementCollection();
+
+ // remove the passive request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(3)).stopMeasurementCollection();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index 71280ce..e81b63c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -403,6 +403,7 @@
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode()));
pkgInfo.applicationInfo = applicationInfo;
- mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(getContext(), pkgInfo));
+ mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(getContext(), userId,
+ pkgInfo));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index 8196d6a..e396263 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -52,7 +52,7 @@
private static final int STATE_WITHOUT_NOTIFICATION = 1;
private static final int STATE_WITH_ACTIVE_NOTIFICATION = 2;
- private static final int STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION = 3;
+ private static final int STATE_WITH_ALL_NOTIFICATION = 3;
private static final int VALID_APP_UID = 1000;
private static final int INVALID_APP_UID = 2000;
@@ -68,6 +68,8 @@
private static final String CONTENT_2 = "content2:%1$s";
private static final String THERMAL_TITLE_2 = "thermal_title2";
private static final String THERMAL_CONTENT_2 = "thermal_content2";
+ private static final String POWER_SAVE_TITLE_2 = "power_save_title2";
+ private static final String POWER_SAVE_CONTENT_2 = "power_save_content2";
private DeviceStateNotificationController mController;
@@ -88,11 +90,12 @@
notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION,
new DeviceStateNotificationController.NotificationInfo(
NAME_1, TITLE_1, CONTENT_1,
- "", ""));
- notificationInfos.put(STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION,
+ "", "", "", ""));
+ notificationInfos.put(STATE_WITH_ALL_NOTIFICATION,
new DeviceStateNotificationController.NotificationInfo(
NAME_2, TITLE_2, CONTENT_2,
- THERMAL_TITLE_2, THERMAL_CONTENT_2));
+ THERMAL_TITLE_2, THERMAL_CONTENT_2,
+ POWER_SAVE_TITLE_2, POWER_SAVE_CONTENT_2));
when(packageManager.getNameForUid(VALID_APP_UID)).thenReturn(VALID_APP_NAME);
when(packageManager.getNameForUid(INVALID_APP_UID)).thenReturn(INVALID_APP_NAME);
@@ -139,10 +142,46 @@
}
@Test
+ public void test_powerSaveNotification() {
+ // Verify that the active notification is created.
+ mController.showStateActiveNotificationIfNeeded(
+ STATE_WITH_ALL_NOTIFICATION, VALID_APP_UID);
+ verify(mNotificationManager).notify(
+ eq(DeviceStateNotificationController.NOTIFICATION_TAG),
+ eq(DeviceStateNotificationController.NOTIFICATION_ID),
+ mNotificationCaptor.capture());
+ Notification notification = mNotificationCaptor.getValue();
+ assertEquals(TITLE_2, notification.extras.getString(Notification.EXTRA_TITLE));
+ assertEquals(String.format(CONTENT_2, VALID_APP_LABEL),
+ notification.extras.getString(Notification.EXTRA_TEXT));
+ assertEquals(Notification.FLAG_ONGOING_EVENT,
+ notification.flags & Notification.FLAG_ONGOING_EVENT);
+ Mockito.clearInvocations(mNotificationManager);
+
+ // Verify that the thermal critical notification is created.
+ mController.showPowerSaveNotificationIfNeeded(
+ STATE_WITH_ALL_NOTIFICATION);
+ verify(mNotificationManager).notify(
+ eq(DeviceStateNotificationController.NOTIFICATION_TAG),
+ eq(DeviceStateNotificationController.NOTIFICATION_ID),
+ mNotificationCaptor.capture());
+ notification = mNotificationCaptor.getValue();
+ assertEquals(POWER_SAVE_TITLE_2, notification.extras.getString(Notification.EXTRA_TITLE));
+ assertEquals(POWER_SAVE_CONTENT_2, notification.extras.getString(Notification.EXTRA_TEXT));
+ assertEquals(0, notification.flags & Notification.FLAG_ONGOING_EVENT);
+
+ // Verify that the notification is canceled.
+ mController.cancelNotification(STATE_WITH_ALL_NOTIFICATION);
+ verify(mNotificationManager).cancel(
+ DeviceStateNotificationController.NOTIFICATION_TAG,
+ DeviceStateNotificationController.NOTIFICATION_ID);
+ }
+
+ @Test
public void test_thermalNotification() {
// Verify that the active notification is created.
mController.showStateActiveNotificationIfNeeded(
- STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION, VALID_APP_UID);
+ STATE_WITH_ALL_NOTIFICATION, VALID_APP_UID);
verify(mNotificationManager).notify(
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
eq(DeviceStateNotificationController.NOTIFICATION_ID),
@@ -157,7 +196,7 @@
// Verify that the thermal critical notification is created.
mController.showThermalCriticalNotificationIfNeeded(
- STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION);
+ STATE_WITH_ALL_NOTIFICATION);
verify(mNotificationManager).notify(
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
eq(DeviceStateNotificationController.NOTIFICATION_ID),
@@ -168,7 +207,7 @@
assertEquals(0, notification.flags & Notification.FLAG_ONGOING_EVENT);
// Verify that the notification is canceled.
- mController.cancelNotification(STATE_WITH_ACTIVE_NOTIFICATION);
+ mController.cancelNotification(STATE_WITH_ALL_NOTIFICATION);
verify(mNotificationManager).cancel(
DeviceStateNotificationController.NOTIFICATION_TAG,
DeviceStateNotificationController.NOTIFICATION_ID);
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 7125796..7e40f96 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -20,6 +20,8 @@
import static android.content.Context.SENSOR_SERVICE;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE;
@@ -327,7 +329,8 @@
+ " <name>THERMAL_TEST</name>\n"
+ " <flags>\n"
+ " <flag>FLAG_EMULATED_ONLY</flag>\n"
- + " <flag>FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL</flag>\n"
+ + " <flag>FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL</flag>\n"
+ + " <flag>FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE</flag>\n"
+ " </flags>\n"
+ " </device-state>\n"
+ "</device-state-config>\n";
@@ -354,7 +357,8 @@
new DeviceState(3, "OPENED", 0 /* flags */),
new DeviceState(4, "THERMAL_TEST",
DeviceState.FLAG_EMULATED_ONLY
- | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
mDeviceStateArrayCaptor.getValue());
// onStateChanged() should not be called because the provider has not yet been notified of
// the initial sensor state.
@@ -419,7 +423,8 @@
new DeviceState(3, "OPENED", 0 /* flags */),
new DeviceState(4, "THERMAL_TEST",
DeviceState.FLAG_EMULATED_ONLY
- | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
mDeviceStateArrayCaptor.getValue());
Mockito.clearInvocations(listener);
@@ -451,7 +456,65 @@
new DeviceState(3, "OPENED", 0 /* flags */),
new DeviceState(4, "THERMAL_TEST",
DeviceState.FLAG_EMULATED_ONLY
- | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void test_flagDisableWhenPowerSaveEnabled() throws Exception {
+ Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
+ DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
+
+ provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ Mockito.clearInvocations(listener);
+
+ provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ Mockito.clearInvocations(listener);
+
+ // The THERMAL_TEST state should be disabled due to power save being enabled.
+ provider.onPowerSaveModeChanged(true /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */) },
+ mDeviceStateArrayCaptor.getValue());
+ Mockito.clearInvocations(listener);
+
+ // The THERMAL_TEST state should be re-enabled.
+ provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
mDeviceStateArrayCaptor.getValue());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index c73237e..0ae579b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -376,46 +376,6 @@
}
@Test
- public void testGetAnimationTargets_windowsAreBeingReplaced() {
- // [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, visible)
- // +- [AppWindow1] (being-replaced)
- // +- [Task2] - [ActivityRecord2] (closing, invisible)
- // +- [AppWindow2] (being-replaced)
- final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
- final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
- TYPE_BASE_APPLICATION);
- attrs.setTitle("AppWindow1");
- final TestWindowState appWindow1 = createWindowState(attrs, activity1);
- appWindow1.mWillReplaceWindow = true;
- activity1.addWindow(appWindow1);
-
- final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
- activity2.setVisible(false);
- activity2.setVisibleRequested(false);
- attrs.setTitle("AppWindow2");
- final TestWindowState appWindow2 = createWindowState(attrs, activity2);
- appWindow2.mWillReplaceWindow = true;
- activity2.addWindow(appWindow2);
-
- final ArraySet<ActivityRecord> opening = new ArraySet<>();
- opening.add(activity1);
- final ArraySet<ActivityRecord> closing = new ArraySet<>();
- closing.add(activity2);
-
- // Animate opening apps even if it's already visible in case its windows are being replaced.
- // Don't animate closing apps if it's already invisible even though its windows are being
- // replaced.
- assertEquals(
- new ArraySet<>(new WindowContainer[]{activity1.getRootTask()}),
- AppTransitionController.getAnimationTargets(
- opening, closing, true /* visible */));
- assertEquals(
- new ArraySet<>(new WindowContainer[]{}),
- AppTransitionController.getAnimationTargets(
- opening, closing, false /* visible */));
- }
-
- @Test
public void testGetAnimationTargets_openingClosingInDifferentTask() {
// [DisplayContent] -+- [Task1] -+- [ActivityRecord1] (opening, invisible)
// | +- [ActivityRecord2] (invisible)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 7d6cf4a..677ec46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -26,7 +26,10 @@
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_OWN_FOCUS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -49,11 +52,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -67,12 +72,16 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.InputConfig;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
+import android.view.IWindow;
import android.view.IWindowSessionCallback;
+import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.Surface;
@@ -690,6 +699,102 @@
assertEquals(validRect, resultingArgs.mSourceCrop);
}
+ @Test
+ public void testGrantInputChannel_sanitizeSpyWindowForApplications() {
+ final Session session = mock(Session.class);
+ final int callingUid = Process.FIRST_APPLICATION_UID;
+ final int callingPid = 1234;
+ final SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final IWindow window = mock(IWindow.class);
+ final IBinder windowToken = mock(IBinder.class);
+ when(window.asBinder()).thenReturn(windowToken);
+ final IBinder focusGrantToken = mock(IBinder.class);
+
+ final InputChannel inputChannel = new InputChannel();
+ assertThrows(IllegalArgumentException.class, () ->
+ mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY,
+ surfaceControl, window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE,
+ PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, TYPE_APPLICATION,
+ null /* windowToken */, focusGrantToken, "TestInputChannel",
+ inputChannel));
+ }
+
+ @Test
+ public void testGrantInputChannel_allowSpyWindowForInputMonitorPermission() {
+ final Session session = mock(Session.class);
+ final int callingUid = Process.SYSTEM_UID;
+ final int callingPid = 1234;
+ final SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final IWindow window = mock(IWindow.class);
+ final IBinder windowToken = mock(IBinder.class);
+ when(window.asBinder()).thenReturn(windowToken);
+ final IBinder focusGrantToken = mock(IBinder.class);
+
+ final InputChannel inputChannel = new InputChannel();
+ mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
+ window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
+ INPUT_FEATURE_SPY, TYPE_APPLICATION, null /* windowToken */, focusGrantToken,
+ "TestInputChannel", inputChannel);
+
+ verify(mTransaction).setInputWindowInfo(
+ eq(surfaceControl),
+ argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY));
+ }
+
+ @Test
+ public void testUpdateInputChannel_sanitizeSpyWindowForApplications() {
+ final Session session = mock(Session.class);
+ final int callingUid = Process.FIRST_APPLICATION_UID;
+ final int callingPid = 1234;
+ final SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final IWindow window = mock(IWindow.class);
+ final IBinder windowToken = mock(IBinder.class);
+ when(window.asBinder()).thenReturn(windowToken);
+ final IBinder focusGrantToken = mock(IBinder.class);
+
+ final InputChannel inputChannel = new InputChannel();
+ mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
+ window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
+ 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken,
+ "TestInputChannel", inputChannel);
+ verify(mTransaction).setInputWindowInfo(
+ eq(surfaceControl),
+ argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
+ FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
+ null /* region */));
+ }
+
+ @Test
+ public void testUpdateInputChannel_allowSpyWindowForInputMonitorPermission() {
+ final Session session = mock(Session.class);
+ final int callingUid = Process.SYSTEM_UID;
+ final int callingPid = 1234;
+ final SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final IWindow window = mock(IWindow.class);
+ final IBinder windowToken = mock(IBinder.class);
+ when(window.asBinder()).thenReturn(windowToken);
+ final IBinder focusGrantToken = mock(IBinder.class);
+
+ final InputChannel inputChannel = new InputChannel();
+ mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
+ window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
+ 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken,
+ "TestInputChannel", inputChannel);
+ verify(mTransaction).setInputWindowInfo(
+ eq(surfaceControl),
+ argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
+
+ mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
+ FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
+ null /* region */);
+ verify(mTransaction).setInputWindowInfo(
+ eq(surfaceControl),
+ argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY));
+ }
+
private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
when(remoteToken.toWindowContainerToken()).thenReturn(wct);
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 2d382a2..79046db 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -37,6 +37,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -160,11 +161,20 @@
return null;
}
final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
- if (!isServiceAvailableForUser(serviceComponent)) {
+ boolean isServiceAvailableForUser;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ isServiceAvailableForUser = isServiceAvailableForUser(serviceComponent);
if (mMaster.verbose) {
- Slog.v(TAG, "ensureRemoteServiceLocked(): " + serviceComponent
- + " is not available,");
+ Slog.v(TAG, "ensureRemoteServiceLocked(): isServiceAvailableForUser="
+ + isServiceAvailableForUser);
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ if (!isServiceAvailableForUser) {
+ Slog.w(TAG, "ensureRemoteServiceLocked(): " + serviceComponent
+ + " is not available,");
return null;
}
mRemoteTranslationService = new RemoteTranslationService(getContext(), serviceComponent,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index 06fc416..dbc824c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -68,6 +68,7 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.service.voice.DetectorFailure;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetectionServiceFailure;
@@ -623,11 +624,9 @@
mRemoteDetectionService = remoteDetectionService;
}
- void reportErrorLocked(int errorCode, @NonNull String errorMessage) {
+ void reportErrorLocked(@NonNull DetectorFailure detectorFailure) {
try {
- // TODO: Use instanceof(this) to get different detector to set the right error source.
- mCallback.onDetectionFailure(
- new HotwordDetectionServiceFailure(errorCode, errorMessage));
+ mCallback.onDetectionFailure(detectorFailure);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report onError status: " + e);
if (getDetectorType() != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 025e1dc..4fd5979 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -51,11 +51,14 @@
import android.os.SharedMemory;
import android.provider.DeviceConfig;
import android.service.voice.HotwordDetectionService;
+import android.service.voice.HotwordDetectionServiceFailure;
import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.ISandboxedDetectionService;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
+import android.service.voice.UnknownFailure;
import android.service.voice.VisualQueryDetectionService;
+import android.service.voice.VisualQueryDetectionServiceFailure;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.speech.IRecognitionServiceManager;
import android.util.Slog;
@@ -109,6 +112,16 @@
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
+ /**
+ * Indicates the {@link HotwordDetectionService} is created.
+ */
+ private static final int DETECTION_SERVICE_TYPE_HOTWORD = 1;
+
+ /**
+ * Indicates the {@link VisualQueryDetectionService} is created.
+ */
+ private static final int DETECTION_SERVICE_TYPE_VISUAL_QUERY = 2;
+
// TODO: This may need to be a Handler(looper)
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
@@ -186,11 +199,11 @@
mHotwordDetectionServiceConnectionFactory =
new ServiceConnectionFactory(hotwordDetectionServiceIntent,
- bindInstantServiceAllowed);
+ bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_HOTWORD);
mVisualQueryDetectionServiceConnectionFactory =
new ServiceConnectionFactory(visualQueryDetectionServiceIntent,
- bindInstantServiceAllowed);
+ bindInstantServiceAllowed, DETECTION_SERVICE_TYPE_VISUAL_QUERY);
mLastRestartInstant = Instant.now();
@@ -604,17 +617,20 @@
private class ServiceConnectionFactory {
private final Intent mIntent;
private final int mBindingFlags;
+ private final int mDetectionServiceType;
- ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed) {
+ ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed,
+ int detectionServiceType) {
mIntent = intent;
mBindingFlags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0;
+ mDetectionServiceType = detectionServiceType;
}
ServiceConnection createLocked() {
ServiceConnection connection =
new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
ISandboxedDetectionService.Stub::asInterface,
- mRestartCount % MAX_ISOLATED_PROCESS_NUMBER);
+ mRestartCount % MAX_ISOLATED_PROCESS_NUMBER, mDetectionServiceType);
connection.connect();
updateAudioFlinger(connection, mAudioFlinger);
@@ -635,15 +651,17 @@
private boolean mRespectServiceConnectionStatusChanged = true;
private boolean mIsBound = false;
private boolean mIsLoggedFirstConnect = false;
+ private final int mDetectionServiceType;
ServiceConnection(@NonNull Context context,
@NonNull Intent serviceIntent, int bindingFlags, int userId,
@Nullable Function<IBinder, ISandboxedDetectionService> binderAsInterface,
- int instanceNumber) {
+ int instanceNumber, int detectionServiceType) {
super(context, serviceIntent, bindingFlags, userId, binderAsInterface);
this.mIntent = serviceIntent;
this.mBindingFlags = bindingFlags;
this.mInstanceNumber = instanceNumber;
+ this.mDetectionServiceType = detectionServiceType;
}
@Override // from ServiceConnector.Impl
@@ -660,14 +678,14 @@
mIsBound = connected;
if (!connected) {
- if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
+ if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__ON_DISCONNECTED,
mVoiceInteractionServiceUid);
}
} else if (!mIsLoggedFirstConnect) {
mIsLoggedFirstConnect = true;
- if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
+ if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED,
mVoiceInteractionServiceUid);
@@ -684,7 +702,7 @@
@Override
public void binderDied() {
super.binderDied();
- Slog.w(TAG, "binderDied");
+ Slog.w(TAG, "binderDied mDetectionServiceType = " + mDetectionServiceType);
synchronized (mLock) {
if (!mRespectServiceConnectionStatusChanged) {
Slog.v(TAG, "Ignored #binderDied event");
@@ -693,13 +711,10 @@
}
//TODO(b265535257): report error to either service only.
synchronized (HotwordDetectionConnection.this.mLock) {
- runForEachDetectorSessionLocked((session) -> {
- session.reportErrorLocked(DetectorSession.HOTWORD_DETECTION_SERVICE_DIED,
- "Detection service is dead.");
- });
+ runForEachDetectorSessionLocked(this::reportBinderDiedLocked);
}
// Can improve to log exit reason if needed
- if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
+ if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) {
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH,
@@ -711,7 +726,7 @@
protected boolean bindService(
@NonNull android.content.ServiceConnection serviceConnection) {
try {
- if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
+ if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE,
mVoiceInteractionServiceUid);
@@ -723,7 +738,12 @@
mExecutor,
serviceConnection);
if (!bindResult) {
- if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
+ Slog.w(TAG,
+ "bindService failure mDetectionServiceType = " + mDetectionServiceType);
+ synchronized (HotwordDetectionConnection.this.mLock) {
+ runForEachDetectorSessionLocked(this::reportBindServiceFailureLocked);
+ }
+ if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL,
mVoiceInteractionServiceUid);
@@ -731,7 +751,7 @@
}
return bindResult;
} catch (IllegalArgumentException e) {
- if (mDetectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
+ if (mDetectionServiceType != DETECTION_SERVICE_TYPE_VISUAL_QUERY) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL,
mVoiceInteractionServiceUid);
@@ -752,6 +772,42 @@
mRespectServiceConnectionStatusChanged = false;
}
}
+
+ private void reportBinderDiedLocked(DetectorSession detectorSession) {
+ if (mDetectionServiceType == DETECTION_SERVICE_TYPE_HOTWORD && (
+ detectorSession instanceof DspTrustedHotwordDetectorSession
+ || detectorSession instanceof SoftwareTrustedHotwordDetectorSession)) {
+ detectorSession.reportErrorLocked(new HotwordDetectionServiceFailure(
+ HotwordDetectionServiceFailure.ERROR_CODE_BINDING_DIED,
+ "Detection service is dead."));
+ } else if (mDetectionServiceType == DETECTION_SERVICE_TYPE_VISUAL_QUERY
+ && detectorSession instanceof VisualQueryDetectorSession) {
+ detectorSession.reportErrorLocked(new VisualQueryDetectionServiceFailure(
+ VisualQueryDetectionServiceFailure.ERROR_CODE_BINDING_DIED,
+ "Detection service is dead."));
+ } else {
+ detectorSession.reportErrorLocked(new UnknownFailure(
+ "Detection service is dead with unknown detection service type."));
+ }
+ }
+
+ private void reportBindServiceFailureLocked(DetectorSession detectorSession) {
+ if (mDetectionServiceType == DETECTION_SERVICE_TYPE_HOTWORD && (
+ detectorSession instanceof DspTrustedHotwordDetectorSession
+ || detectorSession instanceof SoftwareTrustedHotwordDetectorSession)) {
+ detectorSession.reportErrorLocked(new HotwordDetectionServiceFailure(
+ HotwordDetectionServiceFailure.ERROR_CODE_BIND_FAILURE,
+ "Bind detection service failure."));
+ } else if (mDetectionServiceType == DETECTION_SERVICE_TYPE_VISUAL_QUERY
+ && detectorSession instanceof VisualQueryDetectorSession) {
+ detectorSession.reportErrorLocked(new VisualQueryDetectionServiceFailure(
+ VisualQueryDetectionServiceFailure.ERROR_CODE_BIND_FAILURE,
+ "Bind detection service failure."));
+ } else {
+ detectorSession.reportErrorLocked(new UnknownFailure(
+ "Bind detection service failure with unknown detection service type."));
+ }
+ }
}
@SuppressWarnings("GuardedBy")
diff --git a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl
index 2442083..a81444d 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteTransmissionUpdateCallback.aidl
@@ -24,15 +24,22 @@
*/
oneway interface ISatelliteTransmissionUpdateCallback {
/**
- * Called when satellite datagram transfer state changed.
+ * Called when satellite datagram send state changed.
*
- * @param state The new datagram transfer state.
+ * @param state The new send datagram transfer state.
* @param sendPendingCount The number of datagrams that are currently being sent.
- * @param receivePendingCount The number of datagrams that are currently being received.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
- void onDatagramTransferStateChanged(in int state, in int sendPendingCount,
- in int receivePendingCount, in int errorCode);
+ void onSendDatagramStateChanged(in int state, in int sendPendingCount, in int errorCode);
+
+ /**
+ * Called when satellite datagram receive state changed.
+ *
+ * @param state The new receive datagram transfer state.
+ * @param receivePendingCount The number of datagrams that are currently pending to be received.
+ * @param errorCode If datagram transfer failed, the reason for failure.
+ */
+ void onReceiveDatagramStateChanged(in int state, in int receivePendingCount, in int errorCode);
/**
* Called when the satellite position changed.
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index d0abfbf..e32566d 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -647,6 +647,7 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface SatelliteDatagramTransferState {}
+ // TODO: Split into two enums for sending and receiving states
/**
* Satellite modem is in idle state.
@@ -750,20 +751,28 @@
};
ISatelliteTransmissionUpdateCallback internalCallback =
new ISatelliteTransmissionUpdateCallback.Stub() {
- @Override
- public void onDatagramTransferStateChanged(int state,
- int sendPendingCount, int receivePendingCount, int errorCode) {
- executor.execute(() -> Binder.withCleanCallingIdentity(
- () -> callback.onDatagramTransferStateChanged(
- state, sendPendingCount, receivePendingCount,
- errorCode)));
- }
@Override
public void onSatellitePositionChanged(PointingInfo pointingInfo) {
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> callback.onSatellitePositionChanged(pointingInfo)));
}
+
+ @Override
+ public void onSendDatagramStateChanged(int state, int sendPendingCount,
+ int errorCode) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onSendDatagramStateChanged(
+ state, sendPendingCount, errorCode)));
+ }
+
+ @Override
+ public void onReceiveDatagramStateChanged(int state,
+ int receivePendingCount, int errorCode) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onReceiveDatagramStateChanged(
+ state, receivePendingCount, errorCode)));
+ }
};
sSatelliteTransmissionUpdateCallbackMap.put(callback, internalCallback);
telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback,
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index 0efbd1f..d4fe57a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -33,14 +33,24 @@
void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
/**
- * Called when satellite datagram transfer state changed.
+ * Called when satellite datagram send state changed.
*
- * @param state The new datagram transfer state.
+ * @param state The new send datagram transfer state.
* @param sendPendingCount The number of datagrams that are currently being sent.
- * @param receivePendingCount The number of datagrams that are currently being received.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
- void onDatagramTransferStateChanged(@SatelliteManager.SatelliteDatagramTransferState int state,
- int sendPendingCount, int receivePendingCount,
+ void onSendDatagramStateChanged(
+ @SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
+ @SatelliteManager.SatelliteError int errorCode);
+
+ /**
+ * Called when satellite datagram receive state changed.
+ *
+ * @param state The new receive datagram transfer state.
+ * @param receivePendingCount The number of datagrams that are currently pending to be received.
+ * @param errorCode If datagram transfer failed, the reason for failure.
+ */
+ void onReceiveDatagramStateChanged(
+ @SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
@SatelliteManager.SatelliteError int errorCode);
}
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
index d2a6bf2..81efda1 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
+++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static org.junit.Assume.assumeFalse;
+
import android.app.AlarmManager;
import android.content.Context;
import android.os.Environment;
@@ -112,6 +114,7 @@
@Before
public void setUp() throws IOException {
+ assumeFalse(SystemProperties.getBoolean("dalvik.vm.useartservice", false));
File dataDir = getContext().getDataDir();
mBigFile = new File(dataDir, BIG_FILE);
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
new file mode 100644
index 0000000..3289bc6
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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.wm.flicker.launch
+
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
new file mode 100644
index 0000000..4aa78d4
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 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.wm.flicker.launch
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Postsubmit
+class OpenAppFromNotificationColdCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationCold(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
new file mode 100644
index 0000000..9679059
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.wm.flicker.launch
+
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@FlickerServiceCompatible
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppWarmTestCfArm(flicker: FlickerTest) : OpenAppWarmTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 83893ba..a4c48fd 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -13,6 +13,9 @@
"src/**/*.java",
"src/**/*.kt",
],
+ kotlincflags: [
+ "-Werror",
+ ],
platform_apis: true,
certificate: "platform",
static_libs: [
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 1d65cc3..0246426 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -73,7 +73,7 @@
val contentResolver = instrumentation.targetContext.contentResolver
hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
- PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage().getName()
+ PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName()
}
@After
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index d83a457..3a24406 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -45,7 +45,8 @@
private lateinit var mInputMonitor: InputMonitor
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId)
+ val inputManager = getSystemService(InputManager::class.java)
+ mInputMonitor = inputManager.monitorGestureInput(MONITOR_NAME, displayId)
mInputEventReceiver = UnresponsiveReceiver(
mInputMonitor.getInputChannel(), Looper.myLooper())
}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index f183a9b..b7f5b15 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -20,7 +20,6 @@
import android.service.voice.AlwaysOnHotwordDetector;
import android.service.voice.AlwaysOnHotwordDetector.Callback;
import android.service.voice.AlwaysOnHotwordDetector.EventPayload;
-import android.service.voice.HotwordDetector;
import android.service.voice.VoiceInteractionService;
import android.util.Log;
@@ -84,24 +83,16 @@
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
- try {
- Intent enroll = mHotwordDetector.createEnrollIntent();
- Log.i(TAG, "Need to enroll with " + enroll);
- } catch (HotwordDetector.IllegalDetectorStateException e) {
- Log.e(TAG, "createEnrollIntent failed", e);
- }
+ Intent enroll = mHotwordDetector.createEnrollIntent();
+ Log.i(TAG, "Need to enroll with " + enroll);
break;
case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
Log.i(TAG, "STATE_KEYPHRASE_ENROLLED - starting recognition");
- try {
- if (mHotwordDetector.startRecognition(
- AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE)) {
- Log.i(TAG, "startRecognition succeeded");
- } else {
- Log.i(TAG, "startRecognition failed");
- }
- } catch (HotwordDetector.IllegalDetectorStateException e) {
- Log.e(TAG, "startRecognition failed", e);
+ if (mHotwordDetector.startRecognition(
+ AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE)) {
+ Log.i(TAG, "startRecognition succeeded");
+ } else {
+ Log.i(TAG, "startRecognition failed");
}
break;
}
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 5fdfb66..2ce2167 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -209,6 +209,8 @@
AddOptionalFlag("--compile-sdk-version-name",
"Version name to inject into the AndroidManifest.xml if none is present.",
&options_.manifest_fixer_options.compile_sdk_version_codename);
+ AddOptionalFlagList("--fingerprint-prefix", "Fingerprint prefix to add to install constraints.",
+ &options_.manifest_fixer_options.fingerprint_prefixes);
AddOptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
&shared_lib_);
AddOptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib_);
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index c66f4e5..a43bf1b 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -2120,6 +2120,33 @@
}
};
+/** Represents <install-constraints> elements. **/
+class InstallConstraints : public ManifestExtractor::Element {
+ public:
+ InstallConstraints() = default;
+ std::vector<std::string> fingerprint_prefixes;
+
+ void Extract(xml::Element* element) override {
+ for (xml::Element* child : element->GetChildElements()) {
+ if (child->name == "fingerprint-prefix") {
+ xml::Attribute* attr = child->FindAttribute(kAndroidNamespace, "value");
+ if (attr) {
+ fingerprint_prefixes.push_back(attr->value);
+ }
+ }
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ if (!fingerprint_prefixes.empty()) {
+ printer->Print(StringPrintf("install-constraints:\n"));
+ for (const auto& prefix : fingerprint_prefixes) {
+ printer->Print(StringPrintf(" fingerprint-prefix='%s'\n", prefix.c_str()));
+ }
+ }
+ }
+};
+
/** Represents <original-package> elements. **/
class OriginalPackage : public ManifestExtractor::Element {
public:
@@ -2869,7 +2896,7 @@
constexpr const char* GetExpectedTagForType() {
// This array does not appear at runtime, as GetExpectedTagForType function is used by compiler
// to inject proper 'expected_tag' into ElementCast.
- std::array<std::pair<const char*, bool>, 37> tags = {
+ std::array<std::pair<const char*, bool>, 38> tags = {
std::make_pair("action", std::is_same<Action, T>::value),
std::make_pair("activity", std::is_same<Activity, T>::value),
std::make_pair("additional-certificate", std::is_same<AdditionalCertificate, T>::value),
@@ -2878,6 +2905,7 @@
std::make_pair("compatible-screens", std::is_same<CompatibleScreens, T>::value),
std::make_pair("feature-group", std::is_same<FeatureGroup, T>::value),
std::make_pair("input-type", std::is_same<InputType, T>::value),
+ std::make_pair("install-constraints", std::is_same<InstallConstraints, T>::value),
std::make_pair("intent-filter", std::is_same<IntentFilter, T>::value),
std::make_pair("meta-data", std::is_same<MetaData, T>::value),
std::make_pair("manifest", std::is_same<Manifest, T>::value),
@@ -2948,6 +2976,7 @@
{"compatible-screens", &CreateType<CompatibleScreens>},
{"feature-group", &CreateType<FeatureGroup>},
{"input-type", &CreateType<InputType>},
+ {"install-constraints", &CreateType<InstallConstraints>},
{"intent-filter", &CreateType<IntentFilter>},
{"manifest", &CreateType<Manifest>},
{"meta-data", &CreateType<MetaData>},
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 56d9075..53f0abe 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -749,6 +749,23 @@
attr->value = options_.compile_sdk_version_codename.value();
}
+ if (!options_.fingerprint_prefixes.empty()) {
+ xml::Element* install_constraints_el = root->FindChild({}, "install-constraints");
+ if (install_constraints_el == nullptr) {
+ std::unique_ptr<xml::Element> install_constraints = std::make_unique<xml::Element>();
+ install_constraints->name = "install-constraints";
+ install_constraints_el = install_constraints.get();
+ root->AppendChild(std::move(install_constraints));
+ }
+ for (const std::string& prefix : options_.fingerprint_prefixes) {
+ std::unique_ptr<xml::Element> prefix_el = std::make_unique<xml::Element>();
+ prefix_el->name = "fingerprint-prefix";
+ xml::Attribute* attr = prefix_el->FindOrCreateAttribute(xml::kSchemaAndroid, "value");
+ attr->value = prefix;
+ install_constraints_el->AppendChild(std::move(prefix_el));
+ }
+ }
+
xml::XmlActionExecutor executor;
if (!BuildRules(&executor, context->GetDiagnostics())) {
return false;
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 90f5177..175ab6f 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -18,11 +18,10 @@
#define AAPT_LINK_MANIFESTFIXER_H
#include <string>
+#include <vector>
#include "android-base/macros.h"
-
#include "process/IResourceTableConsumer.h"
-
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -75,6 +74,9 @@
// 'android:compileSdkVersionCodename' in the <manifest> tag.
std::optional<std::string> compile_sdk_version_codename;
+ // The fingerprint prefixes to be added to the <install-constraints> tag.
+ std::vector<std::string> fingerprint_prefixes;
+
// Whether validation errors should be treated only as warnings. If this is 'true', then an
// incorrect node will not result in an error, but only as a warning, and the parsing will
// continue.
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 7180ae6..1b8f05b 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -965,6 +965,63 @@
ASSERT_THAT(manifest, IsNull());
}
+TEST_F(ManifestFixerTest, InsertFingerprintPrefixIfNotExist) {
+ std::string input = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ </manifest>)";
+ ManifestFixerOptions options;
+ options.fingerprint_prefixes = {"foo", "bar"};
+
+ std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+ ASSERT_THAT(manifest, NotNull());
+ xml::Element* install_constraints = manifest->root.get()->FindChild({}, "install-constraints");
+ ASSERT_THAT(install_constraints, NotNull());
+ std::vector<xml::Element*> fingerprint_prefixes = install_constraints->GetChildElements();
+ EXPECT_EQ(fingerprint_prefixes.size(), 2);
+ xml::Attribute* attr;
+ EXPECT_THAT(fingerprint_prefixes[0]->name, StrEq("fingerprint-prefix"));
+ attr = fingerprint_prefixes[0]->FindAttribute(xml::kSchemaAndroid, "value");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, StrEq("foo"));
+ EXPECT_THAT(fingerprint_prefixes[1]->name, StrEq("fingerprint-prefix"));
+ attr = fingerprint_prefixes[1]->FindAttribute(xml::kSchemaAndroid, "value");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, StrEq("bar"));
+}
+
+TEST_F(ManifestFixerTest, AppendFingerprintPrefixIfExists) {
+ std::string input = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <install-constraints>
+ <fingerprint-prefix android:value="foo" />
+ </install-constraints>
+ </manifest>)";
+ ManifestFixerOptions options;
+ options.fingerprint_prefixes = {"bar", "baz"};
+
+ std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+ ASSERT_THAT(manifest, NotNull());
+ xml::Element* install_constraints = manifest->root.get()->FindChild({}, "install-constraints");
+ ASSERT_THAT(install_constraints, NotNull());
+ std::vector<xml::Element*> fingerprint_prefixes = install_constraints->GetChildElements();
+ EXPECT_EQ(fingerprint_prefixes.size(), 3);
+ xml::Attribute* attr;
+ EXPECT_THAT(fingerprint_prefixes[0]->name, StrEq("fingerprint-prefix"));
+ attr = fingerprint_prefixes[0]->FindAttribute(xml::kSchemaAndroid, "value");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, StrEq("foo"));
+ EXPECT_THAT(fingerprint_prefixes[1]->name, StrEq("fingerprint-prefix"));
+ attr = fingerprint_prefixes[1]->FindAttribute(xml::kSchemaAndroid, "value");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, StrEq("bar"));
+ EXPECT_THAT(fingerprint_prefixes[2]->name, StrEq("fingerprint-prefix"));
+ attr = fingerprint_prefixes[2]->FindAttribute(xml::kSchemaAndroid, "value");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, StrEq("baz"));
+}
+
TEST_F(ManifestFixerTest, UsesLibraryMustHaveNonEmptyName) {
std::string input = R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 423a684..b4f6a1c 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -36,7 +36,6 @@
CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
- RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG,
PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt
deleted file mode 100644
index c3e0428..0000000
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt
+++ /dev/null
@@ -1,927 +0,0 @@
-/*
- * 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.google.android.lint
-
-import com.android.tools.lint.checks.DataFlowAnalyzer
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.ConstantEvaluator
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.SourceCodeScanner
-import com.android.tools.lint.detector.api.UastLintUtils.Companion.findLastAssignment
-import com.android.tools.lint.detector.api.getMethodName
-import com.intellij.psi.PsiMethod
-import com.intellij.psi.PsiVariable
-import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
-import org.jetbrains.uast.UCallExpression
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.UExpression
-import org.jetbrains.uast.UParenthesizedExpression
-import org.jetbrains.uast.UQualifiedReferenceExpression
-import org.jetbrains.uast.getContainingUMethod
-import org.jetbrains.uast.isNullLiteral
-import org.jetbrains.uast.skipParenthesizedExprDown
-import org.jetbrains.uast.tryResolve
-
-/**
- * Detector that identifies `registerReceiver()` calls which are missing the `RECEIVER_EXPORTED` or
- * `RECEIVER_NOT_EXPORTED` flags on T+.
- *
- * TODO: Add API level conditions to better support non-platform code.
- * 1. Check if registerReceiver() call is reachable on T+.
- * 2. Check if targetSdkVersion is T+.
- *
- * eg: isWithinVersionCheckConditional(context, node, 31, false)
- * eg: isPrecededByVersionCheckExit(context, node, 31) ?
- */
-@Suppress("UnstableApiUsage")
-class RegisterReceiverFlagDetector : Detector(), SourceCodeScanner {
-
- override fun getApplicableMethodNames(): List<String> = listOf(
- "registerReceiver",
- "registerReceiverAsUser",
- "registerReceiverForAllUsers"
- )
-
- private fun checkIsProtectedReceiverAndReturnUnprotectedActions(
- filterArg: UExpression,
- node: UCallExpression,
- evaluator: ConstantEvaluator
- ): Pair<Boolean, List<String>> { // isProtected, unprotectedActions
- val actions = mutableSetOf<String>()
- val construction = findIntentFilterConstruction(filterArg, node)
-
- if (construction == null) return Pair(false, listOf<String>())
- val constructorActionArg = construction.getArgumentForParameter(0)
- (constructorActionArg?.let(evaluator::evaluate) as? String)?.let(actions::add)
-
- val actionCollectorVisitor =
- ActionCollectorVisitor(setOf(construction), node, evaluator)
-
- val parent = node.getContainingUMethod()
- parent?.accept(actionCollectorVisitor)
- actions.addAll(actionCollectorVisitor.actions)
-
- // If we failed to evaluate any actions, there will be a null action in the set.
- val isProtected =
- actions.all(PROTECTED_BROADCASTS::contains) &&
- !actionCollectorVisitor.intentFilterEscapesScope
- val unprotectedActionsList = actions.filterNot(PROTECTED_BROADCASTS::contains)
- return Pair(isProtected, unprotectedActionsList)
- }
-
- override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
- if (!context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) return
-
- // The parameter positions vary across the various registerReceiver*() methods, so rather
- // than hardcode them we simply look them up based on the parameter name and type.
- val receiverArg =
- findArgument(node, method, "android.content.BroadcastReceiver", "receiver")
- val filterArg = findArgument(node, method, "android.content.IntentFilter", "filter")
- val flagsArg = findArgument(node, method, "int", "flags")
-
- if (receiverArg == null || receiverArg.isNullLiteral() || filterArg == null) {
- return
- }
-
- val evaluator = ConstantEvaluator().allowFieldInitializers()
-
- val (isProtected, unprotectedActionsList) =
- checkIsProtectedReceiverAndReturnUnprotectedActions(filterArg, node, evaluator)
-
- val flags = evaluator.evaluate(flagsArg) as? Int
-
- if (!isProtected) {
- val actionsList = unprotectedActionsList.joinToString(", ", "", "", -1, "")
- val message = "$receiverArg is missing 'RECEIVED_EXPORTED` or 'RECEIVE_NOT_EXPORTED' " +
- "flag for unprotected broadcast(s) registered for $actionsList."
- if (flagsArg == null) {
- context.report(
- ISSUE_RECEIVER_EXPORTED_FLAG, node, context.getLocation(node), message)
- } else if (flags != null && (flags and RECEIVER_EXPORTED_FLAG_PRESENT_MASK) == 0) {
- context.report(
- ISSUE_RECEIVER_EXPORTED_FLAG, node, context.getLocation(flagsArg), message)
- }
- }
-
- if (DEBUG) {
- println(node.asRenderString())
- println("Unprotected Actions: $unprotectedActionsList")
- println("Protected: $isProtected")
- println("Flags: $flags")
- }
- }
-
- /** Finds the first argument of a method that matches the given parameter type and name. */
- private fun findArgument(
- node: UCallExpression,
- method: PsiMethod,
- type: String,
- name: String
- ): UExpression? {
- val psiParameter = method.parameterList.parameters.firstOrNull {
- it.type.canonicalText == type && it.name == name
- } ?: return null
- val argument = node.getArgumentForParameter(psiParameter.parameterIndex())
- return argument?.skipParenthesizedExprDown()
- }
-
- /**
- * For the supplied expression (eg. intent filter argument), attempts to find its construction.
- * This will be an `IntentFilter()` constructor, an `IntentFilter.create()` call, or `null`.
- */
- private fun findIntentFilterConstruction(
- expression: UExpression,
- node: UCallExpression
- ): UCallExpression? {
- val resolved = expression.tryResolve()
-
- if (resolved is PsiVariable) {
- val assignment = findLastAssignment(resolved, node) ?: return null
- return findIntentFilterConstruction(assignment, node)
- }
-
- if (expression is UParenthesizedExpression) {
- return findIntentFilterConstruction(expression.expression, node)
- }
-
- if (expression is UQualifiedReferenceExpression) {
- val call = expression.selector as? UCallExpression ?: return null
- return if (isReturningContext(call)) {
- // eg. filter.apply { addAction("abc") } --> use filter variable.
- findIntentFilterConstruction(expression.receiver, node)
- } else {
- // eg. IntentFilter.create("abc") --> use create("abc") UCallExpression.
- findIntentFilterConstruction(call, node)
- }
- }
-
- val method = resolved as? PsiMethod ?: return null
- return if (isIntentFilterFactoryMethod(method)) {
- expression as? UCallExpression
- } else {
- null
- }
- }
-
- private fun isIntentFilterFactoryMethod(method: PsiMethod) =
- (method.containingClass?.qualifiedName == "android.content.IntentFilter" &&
- (method.returnType?.canonicalText == "android.content.IntentFilter" ||
- method.isConstructor))
-
- /**
- * Returns true if the given call represents a Kotlin scope function where the return value is
- * the context object; see https://kotlinlang.org/docs/scope-functions.html#function-selection.
- */
- private fun isReturningContext(node: UCallExpression): Boolean {
- val name = getMethodName(node)
- if (name == "apply" || name == "also") {
- return isScopingFunction(node)
- }
- return false
- }
-
- /**
- * Returns true if the given node appears to be one of the scope functions. Only checks parent
- * class; caller should intend that it's actually one of let, with, apply, etc.
- */
- private fun isScopingFunction(node: UCallExpression): Boolean {
- val called = node.resolve() ?: return true
- // See libraries/stdlib/jvm/build/stdlib-declarations.json
- return called.containingClass?.qualifiedName == "kotlin.StandardKt__StandardKt"
- }
-
- inner class ActionCollectorVisitor(
- start: Collection<UElement>,
- val functionCall: UCallExpression,
- val evaluator: ConstantEvaluator,
- ) : DataFlowAnalyzer(start) {
- private var finished = false
- var intentFilterEscapesScope = false; private set
- val actions = mutableSetOf<String>()
-
- override fun argument(call: UCallExpression, reference: UElement) {
- // TODO: Remove this temporary fix for DataFlowAnalyzer bug (ag/15787550):
- if (reference !in call.valueArguments) return
- val methodNames = super@RegisterReceiverFlagDetector.getApplicableMethodNames()
- when {
- finished -> return
- // We've reached the registerReceiver*() call in question.
- call == functionCall -> finished = true
- // The filter 'intentFilterEscapesScope' to a method which could modify it.
- methodNames != null && getMethodName(call)!! !in methodNames ->
- intentFilterEscapesScope = true
- }
- }
-
- // Fixed in b/199163915: DataFlowAnalyzer doesn't call this for Kotlin properties.
- override fun field(field: UElement) {
- if (!finished) intentFilterEscapesScope = true
- }
-
- override fun receiver(call: UCallExpression) {
- if (!finished && getMethodName(call) == "addAction") {
- val actionArg = call.getArgumentForParameter(0)
- if (actionArg != null) {
- val action = evaluator.evaluate(actionArg) as? String
- if (action != null) actions.add(action)
- }
- }
- }
- }
-
- companion object {
- const val DEBUG = false
-
- private const val RECEIVER_EXPORTED = 0x2
- private const val RECEIVER_NOT_EXPORTED = 0x4
- private const val RECEIVER_EXPORTED_FLAG_PRESENT_MASK =
- RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED
-
- @JvmField
- val ISSUE_RECEIVER_EXPORTED_FLAG: Issue = Issue.create(
- id = "UnspecifiedRegisterReceiverFlag",
- briefDescription = "Missing `registerReceiver()` exported flag",
- explanation = """
- Apps targeting Android T (SDK 33) and higher must specify either `RECEIVER_EXPORTED` \
- or `RECEIVER_NOT_EXPORTED` when registering a broadcast receiver, unless the \
- receiver is only registered for protected system broadcast actions.
- """,
- category = Category.SECURITY,
- priority = 5,
- severity = Severity.WARNING,
- implementation = Implementation(
- RegisterReceiverFlagDetector::class.java,
- Scope.JAVA_FILE_SCOPE
- )
- )
-
- val PROTECTED_BROADCASTS = listOf(
- "android.intent.action.SCREEN_OFF",
- "android.intent.action.SCREEN_ON",
- "android.intent.action.USER_PRESENT",
- "android.intent.action.TIME_SET",
- "android.intent.action.TIME_TICK",
- "android.intent.action.TIMEZONE_CHANGED",
- "android.intent.action.DATE_CHANGED",
- "android.intent.action.PRE_BOOT_COMPLETED",
- "android.intent.action.BOOT_COMPLETED",
- "android.intent.action.PACKAGE_INSTALL",
- "android.intent.action.PACKAGE_ADDED",
- "android.intent.action.PACKAGE_REPLACED",
- "android.intent.action.MY_PACKAGE_REPLACED",
- "android.intent.action.PACKAGE_REMOVED",
- "android.intent.action.PACKAGE_REMOVED_INTERNAL",
- "android.intent.action.PACKAGE_FULLY_REMOVED",
- "android.intent.action.PACKAGE_CHANGED",
- "android.intent.action.PACKAGE_FULLY_LOADED",
- "android.intent.action.PACKAGE_ENABLE_ROLLBACK",
- "android.intent.action.CANCEL_ENABLE_ROLLBACK",
- "android.intent.action.ROLLBACK_COMMITTED",
- "android.intent.action.PACKAGE_RESTARTED",
- "android.intent.action.PACKAGE_DATA_CLEARED",
- "android.intent.action.PACKAGE_FIRST_LAUNCH",
- "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION",
- "android.intent.action.PACKAGE_NEEDS_VERIFICATION",
- "android.intent.action.PACKAGE_VERIFIED",
- "android.intent.action.PACKAGES_SUSPENDED",
- "android.intent.action.PACKAGES_UNSUSPENDED",
- "android.intent.action.PACKAGES_SUSPENSION_CHANGED",
- "android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY",
- "android.intent.action.DISTRACTING_PACKAGES_CHANGED",
- "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED",
- "android.intent.action.UID_REMOVED",
- "android.intent.action.QUERY_PACKAGE_RESTART",
- "android.intent.action.CONFIGURATION_CHANGED",
- "android.intent.action.SPLIT_CONFIGURATION_CHANGED",
- "android.intent.action.LOCALE_CHANGED",
- "android.intent.action.APPLICATION_LOCALE_CHANGED",
- "android.intent.action.BATTERY_CHANGED",
- "android.intent.action.BATTERY_LEVEL_CHANGED",
- "android.intent.action.BATTERY_LOW",
- "android.intent.action.BATTERY_OKAY",
- "android.intent.action.ACTION_POWER_CONNECTED",
- "android.intent.action.ACTION_POWER_DISCONNECTED",
- "android.intent.action.ACTION_SHUTDOWN",
- "android.intent.action.CHARGING",
- "android.intent.action.DISCHARGING",
- "android.intent.action.DEVICE_STORAGE_LOW",
- "android.intent.action.DEVICE_STORAGE_OK",
- "android.intent.action.DEVICE_STORAGE_FULL",
- "android.intent.action.DEVICE_STORAGE_NOT_FULL",
- "android.intent.action.NEW_OUTGOING_CALL",
- "android.intent.action.REBOOT",
- "android.intent.action.DOCK_EVENT",
- "android.intent.action.THERMAL_EVENT",
- "android.intent.action.MASTER_CLEAR_NOTIFICATION",
- "android.intent.action.USER_ADDED",
- "android.intent.action.USER_REMOVED",
- "android.intent.action.USER_STARTING",
- "android.intent.action.USER_STARTED",
- "android.intent.action.USER_STOPPING",
- "android.intent.action.USER_STOPPED",
- "android.intent.action.USER_BACKGROUND",
- "android.intent.action.USER_FOREGROUND",
- "android.intent.action.USER_SWITCHED",
- "android.intent.action.USER_INITIALIZE",
- "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION",
- "android.intent.action.DOMAINS_NEED_VERIFICATION",
- "android.intent.action.OVERLAY_ADDED",
- "android.intent.action.OVERLAY_CHANGED",
- "android.intent.action.OVERLAY_REMOVED",
- "android.intent.action.OVERLAY_PRIORITY_CHANGED",
- "android.intent.action.MY_PACKAGE_SUSPENDED",
- "android.intent.action.MY_PACKAGE_UNSUSPENDED",
- "android.os.action.POWER_SAVE_MODE_CHANGED",
- "android.os.action.DEVICE_IDLE_MODE_CHANGED",
- "android.os.action.POWER_SAVE_WHITELIST_CHANGED",
- "android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED",
- "android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL",
- "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED",
- "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED",
- "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED",
- "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL",
- "android.app.action.ENTER_CAR_MODE",
- "android.app.action.EXIT_CAR_MODE",
- "android.app.action.ENTER_CAR_MODE_PRIORITIZED",
- "android.app.action.EXIT_CAR_MODE_PRIORITIZED",
- "android.app.action.ENTER_DESK_MODE",
- "android.app.action.EXIT_DESK_MODE",
- "android.app.action.NEXT_ALARM_CLOCK_CHANGED",
- "android.app.action.USER_ADDED",
- "android.app.action.USER_REMOVED",
- "android.app.action.USER_STARTED",
- "android.app.action.USER_STOPPED",
- "android.app.action.USER_SWITCHED",
- "android.app.action.BUGREPORT_SHARING_DECLINED",
- "android.app.action.BUGREPORT_FAILED",
- "android.app.action.BUGREPORT_SHARE",
- "android.app.action.SHOW_DEVICE_MONITORING_DIALOG",
- "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED",
- "android.intent.action.INCIDENT_REPORT_READY",
- "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS",
- "android.appwidget.action.APPWIDGET_DELETED",
- "android.appwidget.action.APPWIDGET_DISABLED",
- "android.appwidget.action.APPWIDGET_ENABLED",
- "android.appwidget.action.APPWIDGET_HOST_RESTORED",
- "android.appwidget.action.APPWIDGET_RESTORED",
- "android.appwidget.action.APPWIDGET_ENABLE_AND_UPDATE",
- "android.os.action.SETTING_RESTORED",
- "android.app.backup.intent.CLEAR",
- "android.app.backup.intent.INIT",
- "android.bluetooth.intent.DISCOVERABLE_TIMEOUT",
- "android.bluetooth.adapter.action.STATE_CHANGED",
- "android.bluetooth.adapter.action.SCAN_MODE_CHANGED",
- "android.bluetooth.adapter.action.DISCOVERY_STARTED",
- "android.bluetooth.adapter.action.DISCOVERY_FINISHED",
- "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED",
- "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED",
- "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.device.action.UUID",
- "android.bluetooth.device.action.MAS_INSTANCE",
- "android.bluetooth.device.action.ALIAS_CHANGED",
- "android.bluetooth.device.action.FOUND",
- "android.bluetooth.device.action.CLASS_CHANGED",
- "android.bluetooth.device.action.ACL_CONNECTED",
- "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED",
- "android.bluetooth.device.action.ACL_DISCONNECTED",
- "android.bluetooth.device.action.NAME_CHANGED",
- "android.bluetooth.device.action.BOND_STATE_CHANGED",
- "android.bluetooth.device.action.NAME_FAILED",
- "android.bluetooth.device.action.PAIRING_REQUEST",
- "android.bluetooth.device.action.PAIRING_CANCEL",
- "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY",
- "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL",
- "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST",
- "android.bluetooth.device.action.SDP_RECORD",
- "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED",
- "android.bluetooth.devicepicker.action.LAUNCH",
- "android.bluetooth.devicepicker.action.DEVICE_SELECTED",
- "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED",
- "android.bluetooth.action.CSIS_DEVICE_AVAILABLE",
- "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE",
- "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED",
- "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY",
- "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY",
- "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED",
- "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED",
- "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED",
- "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED",
- "android.bluetooth.action.LE_AUDIO_CONF_CHANGED",
- "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED",
- "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED",
- "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED",
- "android.btopp.intent.action.INCOMING_FILE_NOTIFICATION",
- "android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT",
- "android.btopp.intent.action.LIST",
- "android.btopp.intent.action.OPEN_OUTBOUND",
- "android.btopp.intent.action.HIDE_COMPLETE",
- "android.btopp.intent.action.CONFIRM",
- "android.btopp.intent.action.HIDE",
- "android.btopp.intent.action.RETRY",
- "android.btopp.intent.action.OPEN",
- "android.btopp.intent.action.OPEN_INBOUND",
- "android.btopp.intent.action.TRANSFER_COMPLETE",
- "android.btopp.intent.action.ACCEPT",
- "android.btopp.intent.action.DECLINE",
- "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN",
- "com.android.bluetooth.pbap.authchall",
- "com.android.bluetooth.pbap.userconfirmtimeout",
- "com.android.bluetooth.pbap.authresponse",
- "com.android.bluetooth.pbap.authcancelled",
- "com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT",
- "com.android.bluetooth.sap.action.DISCONNECT_ACTION",
- "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED",
- "android.hardware.usb.action.USB_STATE",
- "android.hardware.usb.action.USB_PORT_CHANGED",
- "android.hardware.usb.action.USB_ACCESSORY_ATTACHED",
- "android.hardware.usb.action.USB_ACCESSORY_DETACHED",
- "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE",
- "android.hardware.usb.action.USB_DEVICE_ATTACHED",
- "android.hardware.usb.action.USB_DEVICE_DETACHED",
- "android.intent.action.HEADSET_PLUG",
- "android.media.action.HDMI_AUDIO_PLUG",
- "android.media.action.MICROPHONE_MUTE_CHANGED",
- "android.media.action.SPEAKERPHONE_STATE_CHANGED",
- "android.media.AUDIO_BECOMING_NOISY",
- "android.media.RINGER_MODE_CHANGED",
- "android.media.VIBRATE_SETTING_CHANGED",
- "android.media.VOLUME_CHANGED_ACTION",
- "android.media.MASTER_VOLUME_CHANGED_ACTION",
- "android.media.MASTER_MUTE_CHANGED_ACTION",
- "android.media.MASTER_MONO_CHANGED_ACTION",
- "android.media.MASTER_BALANCE_CHANGED_ACTION",
- "android.media.SCO_AUDIO_STATE_CHANGED",
- "android.media.ACTION_SCO_AUDIO_STATE_UPDATED",
- "android.intent.action.MEDIA_REMOVED",
- "android.intent.action.MEDIA_UNMOUNTED",
- "android.intent.action.MEDIA_CHECKING",
- "android.intent.action.MEDIA_NOFS",
- "android.intent.action.MEDIA_MOUNTED",
- "android.intent.action.MEDIA_SHARED",
- "android.intent.action.MEDIA_UNSHARED",
- "android.intent.action.MEDIA_BAD_REMOVAL",
- "android.intent.action.MEDIA_UNMOUNTABLE",
- "android.intent.action.MEDIA_EJECT",
- "android.net.conn.CAPTIVE_PORTAL",
- "android.net.conn.CONNECTIVITY_CHANGE",
- "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE",
- "android.net.conn.DATA_ACTIVITY_CHANGE",
- "android.net.conn.RESTRICT_BACKGROUND_CHANGED",
- "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED",
- "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED",
- "android.net.nsd.STATE_CHANGED",
- "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED",
- "android.nfc.action.ADAPTER_STATE_CHANGED",
- "android.nfc.action.PREFERRED_PAYMENT_CHANGED",
- "android.nfc.action.TRANSACTION_DETECTED",
- "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC",
- "com.android.nfc.action.LLCP_UP",
- "com.android.nfc.action.LLCP_DOWN",
- "com.android.nfc.cardemulation.action.CLOSE_TAP_DIALOG",
- "com.android.nfc.handover.action.ALLOW_CONNECT",
- "com.android.nfc.handover.action.DENY_CONNECT",
- "com.android.nfc.handover.action.TIMEOUT_CONNECT",
- "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED",
- "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED",
- "com.android.nfc_extras.action.AID_SELECTED",
- "android.btopp.intent.action.WHITELIST_DEVICE",
- "android.btopp.intent.action.STOP_HANDOVER_TRANSFER",
- "android.nfc.handover.intent.action.HANDOVER_SEND",
- "android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE",
- "com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER",
- "android.net.action.CLEAR_DNS_CACHE",
- "android.intent.action.PROXY_CHANGE",
- "android.os.UpdateLock.UPDATE_LOCK_CHANGED",
- "android.intent.action.DREAMING_STARTED",
- "android.intent.action.DREAMING_STOPPED",
- "android.intent.action.ANY_DATA_STATE",
- "com.android.server.stats.action.TRIGGER_COLLECTION",
- "com.android.server.WifiManager.action.START_SCAN",
- "com.android.server.WifiManager.action.START_PNO",
- "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP",
- "com.android.server.WifiManager.action.DEVICE_IDLE",
- "com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED",
- "com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED",
- "com.android.internal.action.EUICC_FACTORY_RESET",
- "com.android.server.usb.ACTION_OPEN_IN_APPS",
- "com.android.server.am.DELETE_DUMPHEAP",
- "com.android.server.net.action.SNOOZE_WARNING",
- "com.android.server.net.action.SNOOZE_RAPID",
- "com.android.server.wifi.ACTION_SHOW_SET_RANDOMIZATION_DETAILS",
- "com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP",
- "com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP",
- "com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED",
- "com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER",
- "com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER",
- "com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED",
- "com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION",
- "com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK",
- "com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK",
- "com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE",
- "com.android.server.wifi.wakeup.DISMISS_NOTIFICATION",
- "com.android.server.wifi.wakeup.OPEN_WIFI_PREFERENCES",
- "com.android.server.wifi.wakeup.OPEN_WIFI_SETTINGS",
- "com.android.server.wifi.wakeup.TURN_OFF_WIFI_WAKE",
- "android.net.wifi.WIFI_STATE_CHANGED",
- "android.net.wifi.WIFI_AP_STATE_CHANGED",
- "android.net.wifi.WIFI_CREDENTIAL_CHANGED",
- "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED",
- "android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED",
- "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED",
- "android.net.wifi.SCAN_RESULTS",
- "android.net.wifi.RSSI_CHANGED",
- "android.net.wifi.STATE_CHANGE",
- "android.net.wifi.LINK_CONFIGURATION_CHANGED",
- "android.net.wifi.CONFIGURED_NETWORKS_CHANGE",
- "android.net.wifi.action.NETWORK_SETTINGS_RESET",
- "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT",
- "android.net.wifi.action.PASSPOINT_ICON",
- "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST",
- "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION",
- "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW",
- "android.net.wifi.action.REFRESH_USER_PROVISIONING",
- "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION",
- "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED",
- "android.net.wifi.supplicant.CONNECTION_CHANGE",
- "android.net.wifi.supplicant.STATE_CHANGE",
- "android.net.wifi.p2p.STATE_CHANGED",
- "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE",
- "android.net.wifi.p2p.THIS_DEVICE_CHANGED",
- "android.net.wifi.p2p.PEERS_CHANGED",
- "android.net.wifi.p2p.CONNECTION_STATE_CHANGE",
- "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED",
- "android.net.conn.TETHER_STATE_CHANGED",
- "android.net.conn.INET_CONDITION_ACTION",
- "android.net.conn.NETWORK_CONDITIONS_MEASURED",
- "android.net.scoring.SCORE_NETWORKS",
- "android.net.scoring.SCORER_CHANGED",
- "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE",
- "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE",
- "android.intent.action.AIRPLANE_MODE",
- "android.intent.action.ADVANCED_SETTINGS",
- "android.intent.action.APPLICATION_RESTRICTIONS_CHANGED",
- "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES",
- "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT",
- "com.android.server.adb.WIRELESS_DEBUG_STATUS",
- "android.intent.action.ACTION_IDLE_MAINTENANCE_START",
- "android.intent.action.ACTION_IDLE_MAINTENANCE_END",
- "com.android.server.ACTION_TRIGGER_IDLE",
- "android.intent.action.HDMI_PLUGGED",
- "android.intent.action.PHONE_STATE",
- "android.intent.action.SUB_DEFAULT_CHANGED",
- "android.location.PROVIDERS_CHANGED",
- "android.location.MODE_CHANGED",
- "android.location.action.GNSS_CAPABILITIES_CHANGED",
- "android.net.proxy.PAC_REFRESH",
- "android.telecom.action.DEFAULT_DIALER_CHANGED",
- "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED",
- "android.provider.action.SMS_MMS_DB_CREATED",
- "android.provider.action.SMS_MMS_DB_LOST",
- "android.intent.action.CONTENT_CHANGED",
- "android.provider.Telephony.MMS_DOWNLOADED",
- "android.content.action.PERMISSION_RESPONSE_RECEIVED",
- "android.content.action.REQUEST_PERMISSION",
- "android.nfc.handover.intent.action.HANDOVER_STARTED",
- "android.nfc.handover.intent.action.TRANSFER_DONE",
- "android.nfc.handover.intent.action.TRANSFER_PROGRESS",
- "android.nfc.handover.intent.action.TRANSFER_DONE",
- "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED",
- "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED",
- "android.intent.action.ACTION_SET_RADIO_CAPABILITY_DONE",
- "android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED",
- "android.internal.policy.action.BURN_IN_PROTECTION",
- "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED",
- "android.app.action.RESET_PROTECTION_POLICY_CHANGED",
- "android.app.action.DEVICE_OWNER_CHANGED",
- "android.app.action.MANAGED_USER_CREATED",
- "android.intent.action.ANR",
- "android.intent.action.CALL",
- "android.intent.action.CALL_PRIVILEGED",
- "android.intent.action.DROPBOX_ENTRY_ADDED",
- "android.intent.action.INPUT_METHOD_CHANGED",
- "android.intent.action.internal_sim_state_changed",
- "android.intent.action.LOCKED_BOOT_COMPLETED",
- "android.intent.action.PRECISE_CALL_STATE",
- "android.intent.action.SUBSCRIPTION_PHONE_STATE",
- "android.intent.action.USER_INFO_CHANGED",
- "android.intent.action.USER_UNLOCKED",
- "android.intent.action.WALLPAPER_CHANGED",
- "android.app.action.DEVICE_POLICY_MANAGER_STATE_CHANGED",
- "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS",
- "android.app.action.DEVICE_ADMIN_DISABLED",
- "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED",
- "android.app.action.DEVICE_ADMIN_ENABLED",
- "android.app.action.LOCK_TASK_ENTERING",
- "android.app.action.LOCK_TASK_EXITING",
- "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE",
- "android.app.action.ACTION_PASSWORD_CHANGED",
- "android.app.action.ACTION_PASSWORD_EXPIRING",
- "android.app.action.ACTION_PASSWORD_FAILED",
- "android.app.action.ACTION_PASSWORD_SUCCEEDED",
- "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION",
- "com.android.server.ACTION_PROFILE_OFF_DEADLINE",
- "com.android.server.ACTION_TURN_PROFILE_ON_NOTIFICATION",
- "android.intent.action.MANAGED_PROFILE_ADDED",
- "android.intent.action.MANAGED_PROFILE_UNLOCKED",
- "android.intent.action.MANAGED_PROFILE_REMOVED",
- "android.app.action.MANAGED_PROFILE_PROVISIONED",
- "android.bluetooth.adapter.action.BLE_STATE_CHANGED",
- "com.android.bluetooth.map.USER_CONFIRM_TIMEOUT",
- "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT",
- "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY",
- "android.content.jobscheduler.JOB_DELAY_EXPIRED",
- "android.content.syncmanager.SYNC_ALARM",
- "android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION",
- "android.media.STREAM_DEVICES_CHANGED_ACTION",
- "android.media.STREAM_MUTE_CHANGED_ACTION",
- "android.net.sip.SIP_SERVICE_UP",
- "android.nfc.action.ADAPTER_STATE_CHANGED",
- "android.os.action.CHARGING",
- "android.os.action.DISCHARGING",
- "android.search.action.SEARCHABLES_CHANGED",
- "android.security.STORAGE_CHANGED",
- "android.security.action.TRUST_STORE_CHANGED",
- "android.security.action.KEYCHAIN_CHANGED",
- "android.security.action.KEY_ACCESS_CHANGED",
- "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED",
- "android.telecom.action.PHONE_ACCOUNT_REGISTERED",
- "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED",
- "android.telecom.action.POST_CALL",
- "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION",
- "android.telephony.action.CARRIER_CONFIG_CHANGED",
- "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED",
- "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED",
- "android.telephony.action.SECRET_CODE",
- "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION",
- "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED",
- "com.android.bluetooth.btservice.action.ALARM_WAKEUP",
- "com.android.server.action.NETWORK_STATS_POLL",
- "com.android.server.action.NETWORK_STATS_UPDATED",
- "com.android.server.timedetector.NetworkTimeUpdateService.action.POLL",
- "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY",
- "com.android.settings.location.MODE_CHANGING",
- "com.android.settings.bluetooth.ACTION_DISMISS_PAIRING",
- "com.android.settings.network.DELETE_SUBSCRIPTION",
- "com.android.settings.network.SWITCH_TO_SUBSCRIPTION",
- "com.android.settings.wifi.action.NETWORK_REQUEST",
- "NotificationManagerService.TIMEOUT",
- "NotificationHistoryDatabase.CLEANUP",
- "ScheduleConditionProvider.EVALUATE",
- "EventConditionProvider.EVALUATE",
- "SnoozeHelper.EVALUATE",
- "wifi_scan_available",
- "action.cne.started",
- "android.content.jobscheduler.JOB_DEADLINE_EXPIRED",
- "android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW",
- "android.net.conn.CONNECTIVITY_CHANGE_SUPL",
- "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED",
- "android.os.storage.action.VOLUME_STATE_CHANGED",
- "android.os.storage.action.DISK_SCANNED",
- "com.android.server.action.UPDATE_TWILIGHT_STATE",
- "com.android.server.action.RESET_TWILIGHT_AUTO",
- "com.android.server.device_idle.STEP_IDLE_STATE",
- "com.android.server.device_idle.STEP_LIGHT_IDLE_STATE",
- "com.android.server.Wifi.action.TOGGLE_PNO",
- "intent.action.ACTION_RF_BAND_INFO",
- "android.intent.action.MEDIA_RESOURCE_GRANTED",
- "android.app.action.NETWORK_LOGS_AVAILABLE",
- "android.app.action.SECURITY_LOGS_AVAILABLE",
- "android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED",
- "android.app.action.INTERRUPTION_FILTER_CHANGED",
- "android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL",
- "android.app.action.NOTIFICATION_POLICY_CHANGED",
- "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED",
- "android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED",
- "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED",
- "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED",
- "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED",
- "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED",
- "android.app.action.APP_BLOCK_STATE_CHANGED",
- "android.permission.GET_APP_GRANTED_URI_PERMISSIONS",
- "android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS",
- "android.intent.action.DYNAMIC_SENSOR_CHANGED",
- "android.accounts.LOGIN_ACCOUNTS_CHANGED",
- "android.accounts.action.ACCOUNT_REMOVED",
- "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED",
- "com.android.sync.SYNC_CONN_STATUS_CHANGED",
- "android.net.sip.action.SIP_INCOMING_CALL",
- "com.android.phone.SIP_ADD_PHONE",
- "android.net.sip.action.SIP_REMOVE_PROFILE",
- "android.net.sip.action.SIP_SERVICE_UP",
- "android.net.sip.action.SIP_CALL_OPTION_CHANGED",
- "android.net.sip.action.START_SIP",
- "android.bluetooth.adapter.action.BLE_ACL_CONNECTED",
- "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED",
- "android.bluetooth.input.profile.action.HANDSHAKE",
- "android.bluetooth.input.profile.action.REPORT",
- "android.intent.action.TWILIGHT_CHANGED",
- "com.android.server.fingerprint.ACTION_LOCKOUT_RESET",
- "android.net.wifi.PASSPOINT_ICON_RECEIVED",
- "com.android.server.notification.CountdownConditionProvider",
- "android.server.notification.action.ENABLE_NAS",
- "android.server.notification.action.DISABLE_NAS",
- "android.server.notification.action.LEARNMORE_NAS",
- "com.android.internal.location.ALARM_WAKEUP",
- "com.android.internal.location.ALARM_TIMEOUT",
- "android.intent.action.GLOBAL_BUTTON",
- "android.intent.action.MANAGED_PROFILE_AVAILABLE",
- "android.intent.action.MANAGED_PROFILE_UNAVAILABLE",
- "com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK",
- "android.intent.action.PROFILE_ACCESSIBLE",
- "android.intent.action.PROFILE_INACCESSIBLE",
- "com.android.server.retaildemo.ACTION_RESET_DEMO",
- "android.intent.action.DEVICE_LOCKED_CHANGED",
- "com.android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED",
- "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED",
- "com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION",
- "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED",
- "android.content.pm.action.SESSION_COMMITTED",
- "android.os.action.USER_RESTRICTIONS_CHANGED",
- "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT",
- "android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED",
- "android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED",
- "android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED",
- "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER",
- "com.android.intent.action.timezone.RULES_UPDATE_OPERATION",
- "com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK",
- "android.intent.action.GET_RESTRICTION_ENTRIES",
- "android.telephony.euicc.action.OTA_STATUS_CHANGED",
- "android.app.action.PROFILE_OWNER_CHANGED",
- "android.app.action.TRANSFER_OWNERSHIP_COMPLETE",
- "android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE",
- "android.app.action.STATSD_STARTED",
- "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET",
- "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET",
- "android.intent.action.DOCK_IDLE",
- "android.intent.action.DOCK_ACTIVE",
- "android.content.pm.action.SESSION_UPDATED",
- "android.settings.action.GRAYSCALE_CHANGED",
- "com.android.server.jobscheduler.GARAGE_MODE_ON",
- "com.android.server.jobscheduler.GARAGE_MODE_OFF",
- "com.android.server.jobscheduler.FORCE_IDLE",
- "com.android.server.jobscheduler.UNFORCE_IDLE",
- "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL",
- "android.intent.action.DEVICE_CUSTOMIZATION_READY",
- "android.app.action.RESET_PROTECTION_POLICY_CHANGED",
- "com.android.internal.intent.action.BUGREPORT_REQUESTED",
- "android.scheduling.action.REBOOT_READY",
- "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED",
- "android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED",
- "android.app.action.SHOW_NEW_USER_DISCLAIMER",
- "android.telecom.action.CURRENT_TTY_MODE_CHANGED",
- "android.intent.action.SERVICE_STATE",
- "android.intent.action.RADIO_TECHNOLOGY",
- "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED",
- "android.intent.action.EMERGENCY_CALL_STATE_CHANGED",
- "android.intent.action.SIG_STR",
- "android.intent.action.ANY_DATA_STATE",
- "android.intent.action.DATA_STALL_DETECTED",
- "android.intent.action.SIM_STATE_CHANGED",
- "android.intent.action.USER_ACTIVITY_NOTIFICATION",
- "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS",
- "android.intent.action.ACTION_MDN_STATE_CHANGED",
- "android.telephony.action.SERVICE_PROVIDERS_UPDATED",
- "android.provider.Telephony.SIM_FULL",
- "com.android.internal.telephony.carrier_key_download_alarm",
- "com.android.internal.telephony.data-restart-trysetup",
- "com.android.internal.telephony.data-stall",
- "com.android.internal.telephony.provisioning_apn_alarm",
- "android.intent.action.DATA_SMS_RECEIVED",
- "android.provider.Telephony.SMS_RECEIVED",
- "android.provider.Telephony.SMS_DELIVER",
- "android.provider.Telephony.SMS_REJECTED",
- "android.provider.Telephony.WAP_PUSH_DELIVER",
- "android.provider.Telephony.WAP_PUSH_RECEIVED",
- "android.provider.Telephony.SMS_CB_RECEIVED",
- "android.provider.action.SMS_EMERGENCY_CB_RECEIVED",
- "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED",
- "android.provider.Telephony.SECRET_CODE",
- "com.android.internal.stk.command",
- "com.android.internal.stk.session_end",
- "com.android.internal.stk.icc_status_change",
- "com.android.internal.stk.alpha_notify",
- "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED",
- "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED",
- "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE",
- "com.android.internal.telephony.CARRIER_SIGNAL_RESET",
- "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE",
- "com.android.internal.telephony.PROVISION",
- "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED",
- "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED",
- "com.android.intent.isim_refresh",
- "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE",
- "com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE",
- "com.android.ims.ACTION_RCS_SERVICE_DIED",
- "com.android.ims.ACTION_PRESENCE_CHANGED",
- "com.android.ims.ACTION_PUBLISH_STATUS_CHANGED",
- "com.android.ims.IMS_SERVICE_UP",
- "com.android.ims.IMS_SERVICE_DOWN",
- "com.android.ims.IMS_INCOMING_CALL",
- "com.android.ims.internal.uce.UCE_SERVICE_UP",
- "com.android.ims.internal.uce.UCE_SERVICE_DOWN",
- "com.android.imsconnection.DISCONNECTED",
- "com.android.intent.action.IMS_FEATURE_CHANGED",
- "com.android.intent.action.IMS_CONFIG_CHANGED",
- "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR",
- "com.android.phone.vvm.omtp.sms.REQUEST_SENT",
- "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT",
- "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED",
- "com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO",
- "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD",
- "com.android.internal.telephony.action.COUNTRY_OVERRIDE",
- "com.android.internal.telephony.OPEN_DEFAULT_SMS_APP",
- "com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID",
- "android.telephony.action.SIM_CARD_STATE_CHANGED",
- "android.telephony.action.SIM_APPLICATION_STATE_CHANGED",
- "android.telephony.action.SIM_SLOT_STATUS_CHANGED",
- "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED",
- "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED",
- "android.telephony.action.TOGGLE_PROVISION",
- "android.telephony.action.NETWORK_COUNTRY_CHANGED",
- "android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED",
- "android.telephony.action.MULTI_SIM_CONFIG_CHANGED",
- "android.telephony.action.CARRIER_SIGNAL_RESET",
- "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE",
- "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE",
- "android.telephony.action.CARRIER_SIGNAL_REDIRECTED",
- "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED",
- "com.android.phone.settings.CARRIER_PROVISIONING",
- "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING",
- "com.android.internal.telephony.ACTION_VOWIFI_ENABLED",
- "android.telephony.action.ANOMALY_REPORTED",
- "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED",
- "android.intent.action.ACTION_MANAGED_ROAMING_IND",
- "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE",
- "android.safetycenter.action.REFRESH_SAFETY_SOURCES",
- "android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED",
- "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED",
- "android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER",
- "android.service.autofill.action.DELAYED_FILL",
- "android.app.action.PROVISIONING_COMPLETED",
- "android.app.action.LOST_MODE_LOCATION_UPDATE",
- "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED",
- "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT",
- "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED",
- "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED",
- "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED",
- "android.bluetooth.headsetclient.profile.action.AG_EVENT",
- "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED",
- "android.bluetooth.headsetclient.profile.action.RESULT",
- "android.bluetooth.headsetclient.profile.action.LAST_VTAG",
- "android.bluetooth.headsetclient.profile.action.NETWORK_SERVICE_STATE_CHANGED",
- "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED",
- "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED",
- "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED",
- "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED",
- "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED",
- "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED",
- "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED",
- "android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED",
- "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST",
- "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT",
- "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED",
- "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED",
- "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS",
- "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED",
- "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT",
- "com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY",
- "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED",
- "android.bluetooth.action.TETHERING_STATE_CHANGED",
- "com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS",
- "android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED",
- "com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION",
- "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM"
- )
- }
-}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt
deleted file mode 100644
index 7c0ebca..0000000
--- a/tools/lint/framework/checks/src/test/java/com/google/android/lint/RegisterReceiverFlagDetectorTest.kt
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * 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.google.android.lint
-
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestLintTask
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Issue
-
-@Suppress("UnstableApiUsage")
-class RegisterReceiverFlagDetectorTest : LintDetectorTest() {
-
- override fun getDetector(): Detector = RegisterReceiverFlagDetector()
-
- override fun getIssues(): List<Issue> = listOf(
- RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG
- )
-
- override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
-
- fun testProtectedBroadcast() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- context.registerReceiver(receiver, filter);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- fun testProtectedBroadcastCreate() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- IntentFilter filter =
- IntentFilter.create(Intent.ACTION_BATTERY_CHANGED, "foo/bar");
- context.registerReceiver(receiver, filter);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- fun testMultipleProtectedBroadcasts() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_OKAY);
- context.registerReceiver(receiver, filter);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- // TODO(b/267510341): Reenable this test
- // fun testSubsequentFilterModification() {
- // lint().files(
- // java(
- // """
- // package test.pkg;
- // import android.content.BroadcastReceiver;
- // import android.content.Context;
- // import android.content.Intent;
- // import android.content.IntentFilter;
- // public class TestClass1 {
- // public void testMethod(Context context, BroadcastReceiver receiver) {
- // IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- // filter.addAction(Intent.ACTION_BATTERY_LOW);
- // filter.addAction(Intent.ACTION_BATTERY_OKAY);
- // context.registerReceiver(receiver, filter);
- // filter.addAction("querty");
- // context.registerReceiver(receiver, filter);
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.java:13: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter);
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- fun testNullReceiver() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context) {
- IntentFilter filter = new IntentFilter("qwerty");
- context.registerReceiver(null, filter);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- fun testExportedFlagPresent() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- IntentFilter filter = new IntentFilter("qwerty");
- context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- fun testNotExportedFlagPresent() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- IntentFilter filter = new IntentFilter("qwerty");
- context.registerReceiver(receiver, filter,
- Context.RECEIVER_NOT_EXPORTED);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- // TODO(b/267510341): Reenable this test
- // fun testFlagArgumentAbsent() {
- // lint().files(
- // java(
- // """
- // package test.pkg;
- // import android.content.BroadcastReceiver;
- // import android.content.Context;
- // import android.content.Intent;
- // import android.content.IntentFilter;
- // public class TestClass1 {
- // public void testMethod(Context context, BroadcastReceiver receiver) {
- // IntentFilter filter = new IntentFilter("qwerty");
- // context.registerReceiver(receiver, filter);
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.java:9: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter);
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- // TODO(b/267510341): Reenable this test
- // fun testExportedFlagsAbsent() {
- // lint().files(
- // java(
- // """
- // package test.pkg;
- // import android.content.BroadcastReceiver;
- // import android.content.Context;
- // import android.content.Intent;
- // import android.content.IntentFilter;
- // public class TestClass1 {
- // public void testMethod(Context context, BroadcastReceiver receiver) {
- // IntentFilter filter = new IntentFilter("qwerty");
- // context.registerReceiver(receiver, filter, 0);
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.java:9: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter, 0);
- // ~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- fun testExportedFlagVariable() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- IntentFilter filter = new IntentFilter("qwerty");
- var flags = Context.RECEIVER_EXPORTED;
- context.registerReceiver(receiver, filter, flags);
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- // TODO(b/267510341): Reenable this test
- // fun testUnknownFilter() {
- // lint().files(
- // java(
- // """
- // package test.pkg;
- // import android.content.BroadcastReceiver;
- // import android.content.Context;
- // import android.content.Intent;
- // import android.content.IntentFilter;
- // public class TestClass1 {
- // public void testMethod(Context context, BroadcastReceiver receiver,
- // IntentFilter filter) {
- // context.registerReceiver(receiver, filter);
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.java:9: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter);
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- // TODO(b/267510341): Reenable this test
- // fun testFilterEscapes() {
- // lint().files(
- // java(
- // """
- // package test.pkg;
- // import android.content.BroadcastReceiver;
- // import android.content.Context;
- // import android.content.Intent;
- // import android.content.IntentFilter;
- // public class TestClass1 {
- // public void testMethod(Context context, BroadcastReceiver receiver) {
- // IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- // updateFilter(filter);
- // context.registerReceiver(receiver, filter);
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.java:10: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter);
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- fun testInlineFilter() {
- lint().files(
- java(
- """
- package test.pkg;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- public class TestClass1 {
- public void testMethod(Context context, BroadcastReceiver receiver) {
- context.registerReceiver(receiver,
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- }
- }
- """
- ).indented(),
- *stubs
- )
- .run()
- .expectClean()
- }
-
- // TODO(b/267510341): Reenable this test
- // fun testInlineFilterApply() {
- // lint().files(
- // kotlin(
- // """
- // package test.pkg
- // import android.content.BroadcastReceiver
- // import android.content.Context
- // import android.content.Intent
- // import android.content.IntentFilter
- // class TestClass1 {
- // fun test(context: Context, receiver: BroadcastReceiver) {
- // context.registerReceiver(receiver,
- // IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply {
- // addAction("qwerty")
- // })
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.kt:8: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver,
- // ^
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- // TODO(b/267510341): Reenable this test
- // fun testFilterVariableApply() {
- // lint().files(
- // kotlin(
- // """
- // package test.pkg
- // import android.content.BroadcastReceiver
- // import android.content.Context
- // import android.content.Intent
- // import android.content.IntentFilter
- // class TestClass1 {
- // fun test(context: Context, receiver: BroadcastReceiver) {
- // val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply {
- // addAction("qwerty")
- // }
- // context.registerReceiver(receiver, filter)
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.kt:11: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter)
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- // TODO(b/267510341): Reenable this test
- // fun testFilterVariableApply2() {
- // lint().files(
- // kotlin(
- // """
- // package test.pkg
- // import android.content.BroadcastReceiver
- // import android.content.Context
- // import android.content.Intent
- // import android.content.IntentFilter
- // class TestClass1 {
- // fun test(context: Context, receiver: BroadcastReceiver) {
- // val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply {
- // addAction(Intent.ACTION_BATTERY_OKAY)
- // }
- // context.registerReceiver(receiver, filter.apply {
- // addAction("qwerty")
- // })
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.kt:11: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter.apply {
- // ^
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- // TODO(b/267510341): Reenable this test
- // fun testFilterComplexChain() {
- // lint().files(
- // kotlin(
- // """
- // package test.pkg
- // import android.content.BroadcastReceiver
- // import android.content.Context
- // import android.content.Intent
- // import android.content.IntentFilter
- // class TestClass1 {
- // fun test(context: Context, receiver: BroadcastReceiver) {
- // val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED).apply {
- // addAction(Intent.ACTION_BATTERY_OKAY)
- // }
- // val filter2 = filter
- // val filter3 = filter2.apply {
- // addAction(Intent.ACTION_BATTERY_LOW)
- // }
- // context.registerReceiver(receiver, filter3)
- // val filter4 = filter3.apply {
- // addAction("qwerty")
- // }
- // context.registerReceiver(receiver, filter4)
- // }
- // }
- // """
- // ).indented(),
- // *stubs
- // )
- // .run()
- // .expect("""
- // src/test/pkg/TestClass1.kt:19: Warning: Missing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED flag [UnspecifiedRegisterReceiverFlag]
- // context.registerReceiver(receiver, filter4)
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // 0 errors, 1 warnings
- // """.trimIndent())
- // }
-
- private val broadcastReceiverStub: TestFile = java(
- """
- package android.content;
- public class BroadcastReceiver {
- // Stub
- }
- """
- ).indented()
-
- private val contextStub: TestFile = java(
- """
- package android.content;
- public class Context {
- public static final int RECEIVER_EXPORTED = 0x2;
- public static final int RECEIVER_NOT_EXPORTED = 0x4;
- @Nullable
- public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
- IntentFilter filter,
- @RegisterReceiverFlags int flags);
- }
- """
- ).indented()
-
- private val intentStub: TestFile = java(
- """
- package android.content;
- public class Intent {
- public static final String ACTION_BATTERY_CHANGED =
- "android.intent.action.BATTERY_CHANGED";
- public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
- public static final String ACTION_BATTERY_OKAY =
- "android.intent.action.BATTERY_OKAY";
- }
- """
- ).indented()
-
- private val intentFilterStub: TestFile = java(
- """
- package android.content;
- public class IntentFilter {
- public IntentFilter() {
- // Stub
- }
- public IntentFilter(String action) {
- // Stub
- }
- public IntentFilter(String action, String dataType) {
- // Stub
- }
- public static IntentFilter create(String action, String dataType) {
- return null;
- }
- public final void addAction(String action) {
- // Stub
- }
- }
- """
- ).indented()
-
- private val stubs = arrayOf(broadcastReceiverStub, contextStub, intentStub, intentFilterStub)
-}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
index 9bfeb63..fe397d9 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -86,6 +87,7 @@
@Nullable
@SecurityType
private final ArraySet<Integer> mHotspotSecurityTypes;
+ private final Bundle mExtras;
/**
* Builder class for {@link HotspotNetwork}.
@@ -102,8 +104,8 @@
private String mHotspotBssid;
@Nullable
@SecurityType
- private final ArraySet<Integer> mHotspotSecurityTypes =
- new ArraySet<>();
+ private final ArraySet<Integer> mHotspotSecurityTypes = new ArraySet<>();
+ private Bundle mExtras = Bundle.EMPTY;
/**
* Set the remote device ID.
@@ -190,6 +192,17 @@
}
/**
+ * Sets the extras bundle
+ *
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds the {@link HotspotNetwork} object.
*
* @return Returns the built {@link HotspotNetwork} object.
@@ -203,7 +216,8 @@
mNetworkName,
mHotspotSsid,
mHotspotBssid,
- mHotspotSecurityTypes);
+ mHotspotSecurityTypes,
+ mExtras);
}
}
@@ -231,7 +245,8 @@
@NonNull String networkName,
@Nullable String hotspotSsid,
@Nullable String hotspotBssid,
- @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) {
+ @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes,
+ @NonNull Bundle extras) {
validate(deviceId,
networkType,
networkName,
@@ -243,6 +258,7 @@
mHotspotSsid = hotspotSsid;
mHotspotBssid = hotspotBssid;
mHotspotSecurityTypes = new ArraySet<>(hotspotSecurityTypes);
+ mExtras = extras;
}
/**
@@ -315,6 +331,16 @@
return mHotspotSecurityTypes;
}
+ /**
+ * Gets the extras Bundle.
+ *
+ * @return Returns a Bundle object.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof HotspotNetwork)) return false;
@@ -348,6 +374,7 @@
dest.writeString(mHotspotSsid);
dest.writeString(mHotspotBssid);
dest.writeArraySet(mHotspotSecurityTypes);
+ dest.writeBundle(mExtras);
}
/**
@@ -359,7 +386,7 @@
public static HotspotNetwork readFromParcel(@NonNull Parcel in) {
return new HotspotNetwork(in.readLong(), NetworkProviderInfo.readFromParcel(in),
in.readInt(), in.readString(), in.readString(), in.readString(),
- (ArraySet<Integer>) in.readArraySet(null));
+ (ArraySet<Integer>) in.readArraySet(null), in.readBundle());
}
@NonNull
@@ -385,6 +412,7 @@
.append(", hotspotSsid=").append(mHotspotSsid)
.append(", hotspotBssid=").append(mHotspotBssid)
.append(", hotspotSecurityTypes=").append(mHotspotSecurityTypes.toString())
+ .append(", extras=").append(mExtras.toString())
.append("]").toString();
}
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java
index 69767f3..72acf2c 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java
@@ -117,7 +117,7 @@
@ConnectionStatus
private int mStatus;
private HotspotNetwork mHotspotNetwork;
- private Bundle mExtras;
+ private Bundle mExtras = Bundle.EMPTY;
/**
* Sets the status of the connection
@@ -179,7 +179,7 @@
}
private HotspotNetworkConnectionStatus(@ConnectionStatus int status,
- HotspotNetwork hotspotNetwork, Bundle extras) {
+ HotspotNetwork hotspotNetwork, @NonNull Bundle extras) {
validate(status);
mStatus = status;
mHotspotNetwork = hotspotNetwork;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
index 64412bc..c390e42 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -73,6 +74,7 @@
@SecurityType
private final ArraySet<Integer> mSecurityTypes;
private final NetworkProviderInfo mNetworkProviderInfo;
+ private final Bundle mExtras;
/**
* Builder class for {@link KnownNetwork}.
@@ -84,6 +86,7 @@
@SecurityType
private final ArraySet<Integer> mSecurityTypes = new ArraySet<>();
private NetworkProviderInfo mNetworkProviderInfo;
+ private Bundle mExtras = Bundle.EMPTY;
/**
* Sets the indicated source of the known network.
@@ -135,6 +138,17 @@
}
/**
+ * Sets the extras bundle
+ *
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds the {@link KnownNetwork} object.
*
* @return Returns the built {@link KnownNetwork} object.
@@ -145,7 +159,8 @@
mNetworkSource,
mSsid,
mSecurityTypes,
- mNetworkProviderInfo);
+ mNetworkProviderInfo,
+ mExtras);
}
}
@@ -173,12 +188,14 @@
@NetworkSource int networkSource,
@NonNull String ssid,
@NonNull @SecurityType ArraySet<Integer> securityTypes,
- @Nullable NetworkProviderInfo networkProviderInfo) {
+ @Nullable NetworkProviderInfo networkProviderInfo,
+ @NonNull Bundle extras) {
validate(networkSource, ssid, securityTypes, networkProviderInfo);
mNetworkSource = networkSource;
mSsid = ssid;
mSecurityTypes = new ArraySet<>(securityTypes);
mNetworkProviderInfo = networkProviderInfo;
+ mExtras = extras;
}
/**
@@ -223,6 +240,16 @@
return mNetworkProviderInfo;
}
+ /**
+ * Gets the extras Bundle.
+ *
+ * @return Returns a Bundle object.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof KnownNetwork)) return false;
@@ -249,6 +276,7 @@
dest.writeString(mSsid);
dest.writeArraySet(mSecurityTypes);
mNetworkProviderInfo.writeToParcel(dest, flags);
+ dest.writeBundle(mExtras);
}
/**
@@ -260,7 +288,7 @@
public static KnownNetwork readFromParcel(@NonNull Parcel in) {
return new KnownNetwork(in.readInt(), in.readString(),
(ArraySet<Integer>) in.readArraySet(null),
- NetworkProviderInfo.readFromParcel(in));
+ NetworkProviderInfo.readFromParcel(in), in.readBundle());
}
@NonNull
@@ -283,6 +311,7 @@
.append(", ssid=").append(mSsid)
.append(", securityTypes=").append(mSecurityTypes.toString())
.append(", networkProviderInfo=").append(mNetworkProviderInfo.toString())
+ .append(", extras=").append(mExtras.toString())
.append("]").toString();
}
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java
index 6bd0a5e..b30dc3f 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatus.java
@@ -72,7 +72,7 @@
public static final class Builder {
@ConnectionStatus private int mStatus;
private KnownNetwork mKnownNetwork;
- private Bundle mExtras;
+ private Bundle mExtras = Bundle.EMPTY;
public Builder() {}
@@ -128,7 +128,7 @@
}
private KnownNetworkConnectionStatus(@ConnectionStatus int status, KnownNetwork knownNetwork,
- Bundle extras) {
+ @NonNull Bundle extras) {
validate(status);
mStatus = status;
mKnownNetwork = knownNetwork;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
index ed4d699..25fbabc 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.net.wifi.sharedconnectivity.service.SharedConnectivityService;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -89,6 +90,7 @@
private final String mModelName;
private final int mBatteryPercentage;
private final int mConnectionStrength;
+ private final Bundle mExtras;
/**
* Builder class for {@link NetworkProviderInfo}.
@@ -99,6 +101,7 @@
private String mModelName;
private int mBatteryPercentage;
private int mConnectionStrength;
+ private Bundle mExtras = Bundle.EMPTY;
public Builder(@NonNull String deviceName, @NonNull String modelName) {
Objects.requireNonNull(deviceName);
@@ -170,6 +173,17 @@
}
/**
+ * Sets the extras bundle
+ *
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds the {@link NetworkProviderInfo} object.
*
* @return Returns the built {@link NetworkProviderInfo} object.
@@ -177,7 +191,7 @@
@NonNull
public NetworkProviderInfo build() {
return new NetworkProviderInfo(mDeviceType, mDeviceName, mModelName, mBatteryPercentage,
- mConnectionStrength);
+ mConnectionStrength, mExtras);
}
}
@@ -197,13 +211,15 @@
}
private NetworkProviderInfo(@DeviceType int deviceType, @NonNull String deviceName,
- @NonNull String modelName, int batteryPercentage, int connectionStrength) {
+ @NonNull String modelName, int batteryPercentage, int connectionStrength,
+ @NonNull Bundle extras) {
validate(deviceType, deviceName, modelName, batteryPercentage, connectionStrength);
mDeviceType = deviceType;
mDeviceName = deviceName;
mModelName = modelName;
mBatteryPercentage = batteryPercentage;
mConnectionStrength = connectionStrength;
+ mExtras = extras;
}
/**
@@ -256,6 +272,16 @@
return mConnectionStrength;
}
+ /**
+ * Gets the extras Bundle.
+ *
+ * @return Returns a Bundle object.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NetworkProviderInfo)) return false;
@@ -280,6 +306,7 @@
dest.writeString(mModelName);
dest.writeInt(mBatteryPercentage);
dest.writeInt(mConnectionStrength);
+ dest.writeBundle(mExtras);
}
@Override
@@ -295,7 +322,7 @@
@NonNull
public static NetworkProviderInfo readFromParcel(@NonNull Parcel in) {
return new NetworkProviderInfo(in.readInt(), in.readString(), in.readString(), in.readInt(),
- in.readInt());
+ in.readInt(), in.readBundle());
}
@NonNull
@@ -319,6 +346,7 @@
.append(", modelName=").append(mModelName)
.append(", batteryPercentage=").append(mBatteryPercentage)
.append(", connectionStrength=").append(mConnectionStrength)
+ .append(", extras=").append(mExtras.toString())
.append("]").toString();
}
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java
index 4809bef..30bb989 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsState.java
@@ -17,7 +17,11 @@
package android.net.wifi.sharedconnectivity.app;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,6 +41,7 @@
public final class SharedConnectivitySettingsState implements Parcelable {
private final boolean mInstantTetherEnabled;
+ private final PendingIntent mInstantTetherSettingsPendingIntent;
private final Bundle mExtras;
/**
@@ -44,9 +49,13 @@
*/
public static final class Builder {
private boolean mInstantTetherEnabled;
- private Bundle mExtras;
+ private Intent mInstantTetherSettingsIntent;
+ private final Context mContext;
+ private Bundle mExtras = Bundle.EMPTY;
- public Builder() {}
+ public Builder(@NonNull Context context) {
+ mContext = context;
+ }
/**
* Sets the state of Instant Tether in settings
@@ -60,6 +69,20 @@
}
/**
+ * Sets the intent that will open the Instant Tether settings page.
+ * The intent will be stored as a {@link PendingIntent} in the settings object. The pending
+ * intent will be set as {@link PendingIntent#FLAG_IMMUTABLE} and
+ * {@link PendingIntent#FLAG_ONE_SHOT}.
+ *
+ * @return Returns the Builder object.
+ */
+ @NonNull
+ public Builder setInstantTetherSettingsPendingIntent(@NonNull Intent intent) {
+ mInstantTetherSettingsIntent = intent;
+ return this;
+ }
+
+ /**
* Sets the extras bundle
*
* @return Returns the Builder object.
@@ -77,12 +100,21 @@
*/
@NonNull
public SharedConnectivitySettingsState build() {
- return new SharedConnectivitySettingsState(mInstantTetherEnabled, mExtras);
+ if (mInstantTetherSettingsIntent != null) {
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
+ mInstantTetherSettingsIntent,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+ return new SharedConnectivitySettingsState(mInstantTetherEnabled,
+ pendingIntent, mExtras);
+ }
+ return new SharedConnectivitySettingsState(mInstantTetherEnabled, null, mExtras);
}
}
- private SharedConnectivitySettingsState(boolean instantTetherEnabled, Bundle extras) {
+ private SharedConnectivitySettingsState(boolean instantTetherEnabled,
+ PendingIntent pendingIntent, @NonNull Bundle extras) {
mInstantTetherEnabled = instantTetherEnabled;
+ mInstantTetherSettingsPendingIntent = pendingIntent;
mExtras = extras;
}
@@ -96,6 +128,16 @@
}
/**
+ * Gets the pending intent to open Instant Tether settings page.
+ *
+ * @return Returns the pending intent that opens the settings page, null if none.
+ */
+ @Nullable
+ public PendingIntent getInstantTetherSettingsPendingIntent() {
+ return mInstantTetherSettingsPendingIntent;
+ }
+
+ /**
* Gets the extras Bundle.
*
* @return Returns a Bundle object.
@@ -109,12 +151,14 @@
public boolean equals(Object obj) {
if (!(obj instanceof SharedConnectivitySettingsState)) return false;
SharedConnectivitySettingsState other = (SharedConnectivitySettingsState) obj;
- return mInstantTetherEnabled == other.isInstantTetherEnabled();
+ return mInstantTetherEnabled == other.isInstantTetherEnabled()
+ && Objects.equals(mInstantTetherSettingsPendingIntent,
+ other.getInstantTetherSettingsPendingIntent());
}
@Override
public int hashCode() {
- return Objects.hash(mInstantTetherEnabled);
+ return Objects.hash(mInstantTetherEnabled, mInstantTetherSettingsPendingIntent);
}
@Override
@@ -124,6 +168,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mInstantTetherSettingsPendingIntent.writeToParcel(dest, 0);
dest.writeBoolean(mInstantTetherEnabled);
dest.writeBundle(mExtras);
}
@@ -135,8 +180,10 @@
*/
@NonNull
public static SharedConnectivitySettingsState readFromParcel(@NonNull Parcel in) {
- return new SharedConnectivitySettingsState(in.readBoolean(),
- in.readBundle());
+ PendingIntent pendingIntent = PendingIntent.CREATOR.createFromParcel(in);
+ boolean instantTetherEnabled = in.readBoolean();
+ Bundle extras = in.readBundle();
+ return new SharedConnectivitySettingsState(instantTetherEnabled, pendingIntent, extras);
}
@NonNull
@@ -156,6 +203,7 @@
public String toString() {
return new StringBuilder("SharedConnectivitySettingsState[")
.append("instantTetherEnabled=").append(mInstantTetherEnabled)
+ .append("PendingIntent=").append(mInstantTetherSettingsPendingIntent.toString())
.append("extras=").append(mExtras.toString())
.append("]").toString();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index 57108e4..87ca99f 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -68,9 +68,7 @@
new RemoteCallbackList<>();
private List<HotspotNetwork> mHotspotNetworks = Collections.emptyList();
private List<KnownNetwork> mKnownNetworks = Collections.emptyList();
- private SharedConnectivitySettingsState mSettingsState =
- new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(false)
- .setExtras(Bundle.EMPTY).build();
+ private SharedConnectivitySettingsState mSettingsState = null;
private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus =
new HotspotNetworkConnectionStatus.Builder()
.setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
@@ -202,6 +200,13 @@
@Override
public SharedConnectivitySettingsState getSettingsState() {
checkPermissions();
+ // Done lazily since creating it needs a context.
+ if (mSettingsState == null) {
+ mSettingsState = new SharedConnectivitySettingsState
+ .Builder(getApplicationContext())
+ .setInstantTetherEnabled(false)
+ .setExtras(Bundle.EMPTY).build();
+ }
return mSettingsState;
}
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index c9105f7..7a29969 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -36,6 +36,7 @@
static_libs: [
"androidx.test.rules",
+ "androidx.test.core",
"frameworks-base-testutils",
"guava",
"mockito-target-minus-junit4",
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
index 8302094..0827ffa 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertThrows;
+import android.os.Bundle;
import android.os.Parcel;
import android.util.ArraySet;
@@ -55,6 +56,8 @@
private static final String HOTSPOT_SSID = "TEST_SSID";
private static final String HOTSPOT_BSSID = "TEST _BSSID";
private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
+ private static final String BUNDLE_KEY = "INT-KEY";
+ private static final int BUNDLE_VALUE = 1;
private static final long DEVICE_ID_1 = 111L;
private static final NetworkProviderInfo NETWORK_PROVIDER_INFO1 =
@@ -138,6 +141,7 @@
assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID);
assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID);
assertThat(network.getHotspotSecurityTypes()).containsExactlyElementsIn(securityTypes);
+ assertThat(network.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
}
@Test
@@ -161,11 +165,18 @@
.setHostNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID)
- .setHotspotBssid(HOTSPOT_BSSID);
+ .setHotspotBssid(HOTSPOT_BSSID)
+ .setExtras(buildBundle());
Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
if (withNetworkProviderInfo) {
builder.setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
}
return builder;
}
+
+ private Bundle buildBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
+ return bundle;
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
index 1ecba76..81d7b44 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
@@ -25,6 +25,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.os.Bundle;
import android.os.Parcel;
import android.util.ArraySet;
@@ -47,6 +48,9 @@
new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
.setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2)
.setBatteryPercentage(50).build();
+ private static final String BUNDLE_KEY = "INT-KEY";
+ private static final int BUNDLE_VALUE = 1;
+
private static final int NETWORK_SOURCE_1 = NETWORK_SOURCE_CLOUD_SELF;
private static final String SSID_1 = "TEST_SSID1";
private static final int[] SECURITY_TYPES_1 = {SECURITY_TYPE_PSK};
@@ -113,6 +117,7 @@
assertThat(network.getSsid()).isEqualTo(SSID);
assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes);
assertThat(network.getNetworkProviderInfo()).isEqualTo(NETWORK_PROVIDER_INFO);
+ assertThat(network.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
}
@Test
@@ -125,8 +130,15 @@
private KnownNetwork.Builder buildKnownNetworkBuilder() {
KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
+ .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
+ .setExtras(buildBundle());
Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
return builder;
}
+
+ private Bundle buildBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
+ return bundle;
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
index 8f35d8d..4aa9ca6 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.os.Bundle;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
@@ -38,6 +39,8 @@
private static final String DEVICE_MODEL = "TEST_MODEL";
private static final int BATTERY_PERCENTAGE = 50;
private static final int CONNECTION_STRENGTH = 2;
+ private static final String BUNDLE_KEY = "INT-KEY";
+ private static final int BUNDLE_VALUE = 1;
private static final int DEVICE_TYPE_1 = DEVICE_TYPE_LAPTOP;
private static final String DEVICE_NAME_1 = "TEST_NAME1";
@@ -105,6 +108,7 @@
assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL);
assertThat(info.getBatteryPercentage()).isEqualTo(BATTERY_PERCENTAGE);
assertThat(info.getConnectionStrength()).isEqualTo(CONNECTION_STRENGTH);
+ assertThat(info.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
}
@Test
@@ -118,6 +122,13 @@
private NetworkProviderInfo.Builder buildNetworkProviderInfoBuilder() {
return new NetworkProviderInfo.Builder(DEVICE_NAME, DEVICE_MODEL).setDeviceType(DEVICE_TYPE)
.setBatteryPercentage(BATTERY_PERCENTAGE)
- .setConnectionStrength(CONNECTION_STRENGTH);
+ .setConnectionStrength(CONNECTION_STRENGTH)
+ .setExtras(buildBundle());
+ }
+
+ private Bundle buildBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
+ return bundle;
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
index 7578dfd..71239087 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
@@ -497,8 +497,9 @@
@Test
public void getSettingsState_serviceConnected_shouldReturnState() throws RemoteException {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- SharedConnectivitySettingsState state = new SharedConnectivitySettingsState.Builder()
- .setInstantTetherEnabled(true).setExtras(new Bundle()).build();
+ SharedConnectivitySettingsState state =
+ new SharedConnectivitySettingsState.Builder(mContext).setInstantTetherEnabled(true)
+ .setExtras(new Bundle()).build();
manager.setService(mService);
when(mService.getSettingsState()).thenReturn(state);
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
index 752b749..5e17dfb 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
@@ -18,8 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
+import android.content.Intent;
import android.os.Parcel;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -30,8 +32,12 @@
@SmallTest
public class SharedConnectivitySettingsStateTest {
private static final boolean INSTANT_TETHER_STATE = true;
+ private static final String INTENT_ACTION = "instant.tether.settings";
private static final boolean INSTANT_TETHER_STATE_1 = false;
+ private static final String INTENT_ACTION_1 = "instant.tether.settings1";
+
+
/**
* Verifies parcel serialization/deserialization.
*/
@@ -39,16 +45,11 @@
public void testParcelOperation() {
SharedConnectivitySettingsState state = buildSettingsStateBuilder().build();
- Parcel parcelW = Parcel.obtain();
- state.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
+ Parcel parcel = Parcel.obtain();
+ state.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
SharedConnectivitySettingsState fromParcel =
- SharedConnectivitySettingsState.CREATOR.createFromParcel(parcelR);
+ SharedConnectivitySettingsState.CREATOR.createFromParcel(parcel);
assertThat(fromParcel).isEqualTo(state);
assertThat(fromParcel.hashCode()).isEqualTo(state.hashCode());
@@ -66,6 +67,10 @@
SharedConnectivitySettingsState.Builder builder = buildSettingsStateBuilder()
.setInstantTetherEnabled(INSTANT_TETHER_STATE_1);
assertThat(builder.build()).isNotEqualTo(state1);
+
+ builder = buildSettingsStateBuilder()
+ .setInstantTetherSettingsPendingIntent(new Intent(INTENT_ACTION_1));
+ assertThat(builder.build()).isNotEqualTo(state1);
}
/**
@@ -86,7 +91,9 @@
}
private SharedConnectivitySettingsState.Builder buildSettingsStateBuilder() {
- return new SharedConnectivitySettingsState.Builder()
- .setInstantTetherEnabled(INSTANT_TETHER_STATE);
+ return new SharedConnectivitySettingsState.Builder(
+ ApplicationProvider.getApplicationContext())
+ .setInstantTetherEnabled(INSTANT_TETHER_STATE)
+ .setInstantTetherSettingsPendingIntent(new Intent(INTENT_ACTION));
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index b8b6b767..514ba3c 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -75,9 +75,6 @@
.addSecurityType(SECURITY_TYPE_EAP).setNetworkProviderInfo(
NETWORK_PROVIDER_INFO).build();
private static final List<KnownNetwork> KNOWN_NETWORKS = List.of(KNOWN_NETWORK);
- private static final SharedConnectivitySettingsState SETTINGS_STATE =
- new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(true)
- .setExtras(Bundle.EMPTY).build();
private static final HotspotNetworkConnectionStatus TETHER_NETWORK_CONNECTION_STATUS =
new HotspotNetworkConnectionStatus.Builder().setStatus(CONNECTION_STATUS_UNKNOWN)
.setHotspotNetwork(HOTSPOT_NETWORK).setExtras(Bundle.EMPTY).build();
@@ -155,10 +152,11 @@
SharedConnectivityService service = createService();
ISharedConnectivityService.Stub binder =
(ISharedConnectivityService.Stub) service.onBind(new Intent());
+ when(mContext.getPackageName()).thenReturn("android.net.wifi.nonupdatable.test");
- service.setSettingsState(SETTINGS_STATE);
+ service.setSettingsState(buildSettingsState());
- assertThat(binder.getSettingsState()).isEqualTo(SETTINGS_STATE);
+ assertThat(binder.getSettingsState()).isEqualTo(buildSettingsState());
}
@Test
@@ -232,4 +230,10 @@
service.attachBaseContext(mContext);
return service;
}
+
+ private SharedConnectivitySettingsState buildSettingsState() {
+ return new SharedConnectivitySettingsState.Builder(mContext).setInstantTetherEnabled(true)
+ .setInstantTetherSettingsPendingIntent(new Intent())
+ .setExtras(Bundle.EMPTY).build();
+ }
}