Merge "Add Universal Game Mode Hint mode switching to GameManagerService."
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 64ba6d1..7bd5921 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -223,6 +223,27 @@
}
}
+ /** Tests creating and starting a new user. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void createAndStartUser_realistic() throws RemoteException {
+ while (mRunner.keepRunning()) {
+ Log.d(TAG, "Starting timer");
+ final int userId = createUserNoFlags();
+
+ // Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until
+ // ACTION_USER_STARTED.
+ runThenWaitForBroadcasts(userId, () -> {
+ mIam.startUserInBackground(userId);
+ }, Intent.ACTION_USER_STARTED);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/**
* Tests starting an uninitialized user.
* Measures the time until ACTION_USER_STARTED is received.
@@ -299,6 +320,29 @@
}
}
+ /**
+ * Tests starting & unlocking an uninitialized user.
+ * Measures the time until unlock listener is triggered and user is unlocked.
+ */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void startAndUnlockUser_realistic() throws RemoteException {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createUserNoFlags();
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ // Waits for UserState.mUnlockProgress.finish().
+ startUserInBackgroundAndWaitForUnlock(userId);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests switching to an uninitialized user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void switchUser() throws Exception {
@@ -527,7 +571,7 @@
removeUser(userId);
}
- /** Tests reaching LOOKED_BOOT_COMPLETE when switching to uninitialized user. */
+ /** Tests reaching LOCKED_BOOT_COMPLETE when switching to uninitialized user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void lockedBootCompleted() throws RemoteException {
while (mRunner.keepRunning()) {
@@ -550,6 +594,29 @@
}
}
+ /** Tests reaching LOCKED_BOOT_COMPLETE when switching to uninitialized user. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void lockedBootCompleted_realistic() throws RemoteException {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int startUser = ActivityManager.getCurrentUser();
+ final int userId = createUserNoFlags();
+
+ waitCoolDownPeriod();
+ mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+ mAm.switchUser(userId);
+ }, () -> fail("Failed to achieve onLockedBootComplete for user " + userId));
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ switchUserNoCheck(startUser);
+ removeUser(userId);
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests stopping an ephemeral foreground user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void ephemeralUserStopped() throws RemoteException {
@@ -579,6 +646,33 @@
}
}
+ /** Tests stopping an ephemeral foreground user. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void ephemeralUserStopped_realistic() throws RemoteException {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int startUser = ActivityManager.getCurrentUser();
+ final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
+ runThenWaitForBroadcasts(userId, () -> {
+ switchUser(userId);
+ }, Intent.ACTION_MEDIA_MOUNTED);
+
+ waitCoolDownPeriod();
+ mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
+ runThenWaitForBroadcasts(userId, () -> {
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ mAm.switchUser(startUser);
+ }, Intent.ACTION_USER_STOPPED);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ }, null);
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests creating a new profile. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileCreate() throws RemoteException {
@@ -596,6 +690,24 @@
}
}
+ /** Tests creating a new profile. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileCreate_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+
+ while (mRunner.keepRunning()) {
+ Log.d(TAG, "Starting timer");
+ final int userId = createManagedProfile();
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests starting (unlocking) an uninitialized profile. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlock() throws RemoteException {
@@ -616,6 +728,27 @@
}
}
+ /** Tests starting (unlocking) an uninitialized profile. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileUnlock_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createManagedProfile();
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ startUserInBackgroundAndWaitForUnlock(userId);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileUnlock_stopped() throws RemoteException {
@@ -639,6 +772,29 @@
}
}
+ /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileUnlock_stopped_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+ final int userId = createManagedProfile();
+ // Start the profile initially, then stop it. Similar to setQuietModeEnabled.
+ startUserInBackgroundAndWaitForUnlock(userId);
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ stopUserAfterWaitingForBroadcastIdle(userId, true);
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ startUserInBackgroundAndWaitForUnlock(userId);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ removeUser(userId);
+ }
+
/**
* Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
*/
@@ -665,6 +821,32 @@
}
/**
+ * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
+ */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileUnlockAndLaunchApp_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createManagedProfile();
+ WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+ installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ startUserInBackgroundAndWaitForUnlock(userId);
+ startApp(userId, DUMMY_PACKAGE_NAME);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
+ /**
* Tests starting (unlocking) and launching a previously-launched app
* in a previously-started, but no-longer-running, profile.
* A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
@@ -696,6 +878,39 @@
}
}
+ /**
+ * Tests starting (unlocking) and launching a previously-launched app
+ * in a previously-started, but no-longer-running, profile.
+ * A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
+ * {@link #managedProfileUnlock_stopped}}.
+ */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileUnlockAndLaunchApp_stopped_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createManagedProfile();
+ WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+ installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+ startUserInBackgroundAndWaitForUnlock(userId);
+ startApp(userId, DUMMY_PACKAGE_NAME);
+ stopUserAfterWaitingForBroadcastIdle(userId, true);
+ SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ startUserInBackgroundAndWaitForUnlock(userId);
+ startApp(userId, DUMMY_PACKAGE_NAME);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests installing a pre-existing app in an uninitialized profile. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileInstall() throws RemoteException {
@@ -716,6 +931,27 @@
}
}
+ /** Tests installing a pre-existing app in an uninitialized profile. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileInstall_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createManagedProfile();
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/**
* Tests creating a new profile, starting (unlocking) it, installing an app,
* and launching that app in it.
@@ -742,6 +978,33 @@
}
}
+ /**
+ * Tests creating a new profile, starting (unlocking) it, installing an app,
+ * and launching that app in it.
+ */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileCreateUnlockInstallAndLaunchApp_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ final int userId = createManagedProfile();
+ startUserInBackgroundAndWaitForUnlock(userId);
+ installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+ startApp(userId, DUMMY_PACKAGE_NAME);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests stopping a profile. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void managedProfileStopped() throws RemoteException {
@@ -766,6 +1029,30 @@
}
}
+ /** Tests stopping a profile. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void managedProfileStopped_realistic() throws RemoteException {
+ assumeTrue(mHasManagedUserFeature);
+ final int userId = createManagedProfile();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+
+ runThenWaitForBroadcasts(userId, () -> {
+ startUserInBackgroundAndWaitForUnlock(userId);
+ }, Intent.ACTION_MEDIA_MOUNTED);
+ waitCoolDownPeriod();
+ mRunner.resumeTiming();
+ Log.d(TAG, "Starting timer");
+
+ stopUser(userId, true);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ mRunner.resumeTimingForNextIteration();
+ }
+ removeUser(userId);
+ }
+
// TODO: This is just a POC. Do this properly and add more.
/** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index fab5b5f..ad406a1 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -158,17 +158,11 @@
boolean mForceAllAppStandbyForSmallBattery;
/**
- * True if the forced app standby feature is enabled in settings
- */
- @GuardedBy("mLock")
- boolean mForcedAppStandbyEnabled;
-
- /**
* A lock-free set of (uid, packageName) pairs in background restricted mode.
*
* <p>
- * It's bascially shadowing the {@link #mRunAnyRestrictedPackages} together with
- * the {@link #mForcedAppStandbyEnabled} - mutations on them would result in copy-on-write.
+ * It's basically shadowing the {@link #mRunAnyRestrictedPackages}, any mutations on it would
+ * result in copy-on-write.
* </p>
*/
volatile Set<Pair<Integer, String>> mBackgroundRestrictedUidPackages = Collections.emptySet();
@@ -200,10 +194,9 @@
int TEMP_EXEMPTION_LIST_CHANGED = 5;
int EXEMPTED_BUCKET_CHANGED = 6;
int FORCE_ALL_CHANGED = 7;
- int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
- int IS_UID_ACTIVE_CACHED = 9;
- int IS_UID_ACTIVE_RAW = 10;
+ int IS_UID_ACTIVE_CACHED = 8;
+ int IS_UID_ACTIVE_RAW = 9;
}
private final StatLogger mStatLogger = new StatLogger(new String[] {
@@ -215,7 +208,6 @@
"TEMP_EXEMPTION_LIST_CHANGED",
"EXEMPTED_BUCKET_CHANGED",
"FORCE_ALL_CHANGED",
- "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",
"IS_UID_ACTIVE_CACHED",
"IS_UID_ACTIVE_RAW",
@@ -228,18 +220,10 @@
}
void register() {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
- false, this);
-
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
}
- boolean isForcedAppStandbyEnabled() {
- return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
- }
-
boolean isForcedAppStandbyForSmallBatteryEnabled() {
return injectGetGlobalSettingInt(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
@@ -247,21 +231,7 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
- final boolean enabled = isForcedAppStandbyEnabled();
- synchronized (mLock) {
- if (mForcedAppStandbyEnabled == enabled) {
- return;
- }
- mForcedAppStandbyEnabled = enabled;
- updateBackgroundRestrictedUidPackagesLocked();
- if (DEBUG) {
- Slog.d(TAG, "Forced app standby feature flag changed: "
- + mForcedAppStandbyEnabled);
- }
- }
- mHandler.notifyForcedAppStandbyFeatureFlagChanged();
- } else if (Settings.Global.getUriFor(
+ if (Settings.Global.getUriFor(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
synchronized (mLock) {
@@ -515,7 +485,6 @@
mFlagsObserver = new FeatureFlagsObserver();
mFlagsObserver.register();
- mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
mForceAllAppStandbyForSmallBattery =
mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
mStandbyTracker = new StandbyTracker();
@@ -636,14 +605,10 @@
/**
* Update the {@link #mBackgroundRestrictedUidPackages} upon mutations on
- * {@link #mRunAnyRestrictedPackages} or {@link #mForcedAppStandbyEnabled}.
+ * {@link #mRunAnyRestrictedPackages}.
*/
@GuardedBy("mLock")
private void updateBackgroundRestrictedUidPackagesLocked() {
- if (!mForcedAppStandbyEnabled) {
- mBackgroundRestrictedUidPackages = Collections.emptySet();
- return;
- }
Set<Pair<Integer, String>> fasUidPkgs = new ArraySet<>();
for (int i = 0, size = mRunAnyRestrictedPackages.size(); i < size; i++) {
fasUidPkgs.add(mRunAnyRestrictedPackages.valueAt(i));
@@ -821,13 +786,14 @@
private class MyHandler extends Handler {
private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
+ // Unused ids 1, 2.
private static final int MSG_RUN_ANY_CHANGED = 3;
private static final int MSG_ALL_UNEXEMPTED = 4;
private static final int MSG_ALL_EXEMPTION_LIST_CHANGED = 5;
private static final int MSG_TEMP_EXEMPTION_LIST_CHANGED = 6;
private static final int MSG_FORCE_ALL_CHANGED = 7;
private static final int MSG_USER_REMOVED = 8;
- private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
+ // Unused id 9.
private static final int MSG_EXEMPTED_BUCKET_CHANGED = 10;
private static final int MSG_AUTO_RESTRICTED_BUCKET_FEATURE_FLAG_CHANGED = 11;
@@ -867,11 +833,6 @@
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
- public void notifyForcedAppStandbyFeatureFlagChanged() {
- removeMessages(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED);
- obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
- }
-
public void notifyExemptedBucketChanged() {
removeMessages(MSG_EXEMPTED_BUCKET_CHANGED);
obtainMessage(MSG_EXEMPTED_BUCKET_CHANGED).sendToTarget();
@@ -966,22 +927,6 @@
mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start);
return;
- case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
- // Feature flag for forced app standby changed.
- final boolean unblockAlarms;
- synchronized (mLock) {
- unblockAlarms = !mForcedAppStandbyEnabled;
- }
- for (Listener l : cloneListeners()) {
- l.updateAllJobs();
- if (unblockAlarms) {
- l.unblockAllUnrestrictedAlarms();
- }
- }
- mStatLogger.logDurationStat(
- Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start);
- return;
-
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
return;
@@ -1164,8 +1109,7 @@
// If apps will be put into restricted standby bucket automatically on user-forced
// app standby, instead of blocking alarms completely, let the restricted standby bucket
// policy take care of it.
- return (mForcedAppStandbyEnabled
- && !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
+ return (!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
&& isRunAnyRestrictedLocked(uid, packageName));
}
}
@@ -1210,8 +1154,7 @@
// If apps will be put into restricted standby bucket automatically on user-forced
// app standby, instead of blocking jobs completely, let the restricted standby bucket
// policy take care of it.
- if (mForcedAppStandbyEnabled
- && !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
+ if (!mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()
&& isRunAnyRestrictedLocked(uid, packageName)) {
return true;
}
@@ -1321,8 +1264,6 @@
pw.println("Current AppStateTracker State:");
pw.increaseIndent();
- pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled);
-
pw.print("Force all apps standby: ");
pw.println(isForceAllAppsStandbyEnabled());
@@ -1400,8 +1341,6 @@
synchronized (mLock) {
final long token = proto.start(fieldId);
- proto.write(AppStateTrackerProto.FORCED_APP_STANDBY_FEATURE_ENABLED,
- mForcedAppStandbyEnabled);
proto.write(AppStateTrackerProto.FORCE_ALL_APPS_STANDBY,
isForceAllAppsStandbyEnabled());
proto.write(AppStateTrackerProto.IS_SMALL_BATTERY_DEVICE, isSmallBatteryDevice());
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 e41eb00..37d190d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5379,9 +5379,7 @@
@Override
public void unblockAllUnrestrictedAlarms() {
- // Called when:
- // 1. Power exemption list changes,
- // 2. User FAS feature is disabled.
+ // Called when the power exemption list changes.
synchronized (mLock) {
sendAllUnrestrictedPendingBackgroundAlarmsLocked();
}
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 fb5d63e..c2168d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -361,7 +361,14 @@
boolean binding = false;
try {
final int bindFlags;
- if (job.shouldTreatAsExpeditedJob()) {
+ if (job.shouldTreatAsUserInitiatedJob()) {
+ // TODO (191785864, 261999509): add an appropriate flag so user-initiated jobs
+ // can bypass data saver
+ bindFlags = Context.BIND_AUTO_CREATE
+ | Context.BIND_ALMOST_PERCEPTIBLE
+ | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
+ | Context.BIND_NOT_APP_COMPONENT_USAGE;
+ } else if (job.shouldTreatAsExpeditedJob()) {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALMOST_PERCEPTIBLE
| Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 16f5c7f..b87dec1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -98,6 +98,13 @@
~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
| ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
| ConnectivityManager.BLOCKED_REASON_DOZE);
+ // TODO(261999509): allow bypassing data saver & user-restricted. However, when we allow a UI
+ // job to run while data saver restricts the app, we must ensure that we don't run regular
+ // jobs when we put a hole in the data saver wall for the UI job
+ private static final int UNBYPASSABLE_UI_BLOCKED_REASONS =
+ ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
+ | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
+ | ConnectivityManager.BLOCKED_REASON_DOZE);
private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS =
~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY
| ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER
@@ -1080,6 +1087,11 @@
Slog.d(TAG, "Using FG bypass for " + jobStatus.getSourceUid());
}
unbypassableBlockedReasons = UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS;
+ } else if (jobStatus.shouldTreatAsUserInitiatedJob()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Using UI bypass for " + jobStatus.getSourceUid());
+ }
+ unbypassableBlockedReasons = UNBYPASSABLE_UI_BLOCKED_REASONS;
} else if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.startedAsExpeditedJob) {
if (DEBUG) {
Slog.d(TAG, "Using EJ bypass for " + jobStatus.getSourceUid());
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 13a2a94..258680a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -965,17 +965,21 @@
Slog.d(TAG, " Checking idle state for " + packageName
+ " minBucket=" + standbyBucketToString(minBucket));
}
+ final boolean previouslyIdle, stillIdle;
if (minBucket <= STANDBY_BUCKET_ACTIVE) {
// No extra processing needed for ACTIVE or higher since apps can't drop into lower
// buckets.
synchronized (mAppIdleLock) {
+ previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
minBucket, REASON_MAIN_DEFAULT);
+ stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
minBucket, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
+ previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
final AppIdleHistory.AppUsageHistory app =
mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
@@ -1073,11 +1077,17 @@
if (oldBucket != newBucket || predictionLate) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
+ stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
maybeInformListeners(packageName, userId, elapsedRealtime,
newBucket, reason, false);
+ } else {
+ stillIdle = previouslyIdle;
}
}
}
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(packageName, userId, stillIdle);
+ }
}
/** Returns true if there hasn't been a prediction for the app in a while. */
@@ -1234,8 +1244,9 @@
appHistory.currentBucket, reason, userStartedInteracting);
}
- if (previouslyIdle) {
- notifyBatteryStats(pkg, userId, false);
+ final boolean stillIdle = appHistory.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(pkg, userId, stillIdle);
}
}
@@ -1808,8 +1819,14 @@
reason = REASON_MAIN_FORCED_BY_SYSTEM
| (app.bucketingReason & REASON_SUB_MASK)
| (reason & REASON_SUB_MASK);
+ final boolean previouslyIdle =
+ app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
newBucket, reason, resetTimeout);
+ final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(packageName, userId, stillIdle);
+ }
return;
}
@@ -1910,8 +1927,13 @@
// Make sure we don't put the app in a lower bucket than it's supposed to be in.
newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
+ final boolean previouslyIdle = app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason, resetTimeout);
+ final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
+ if (previouslyIdle != stillIdle) {
+ notifyBatteryStats(packageName, userId, stillIdle);
+ }
}
maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
}
diff --git a/core/api/current.txt b/core/api/current.txt
index c811a09..10578db 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1488,6 +1488,7 @@
field public static final int strokeLineJoin = 16843788; // 0x101040c
field public static final int strokeMiterLimit = 16843789; // 0x101040d
field public static final int strokeWidth = 16843783; // 0x1010407
+ field public static final int stylusHandwritingSettingsActivity;
field public static final int subMenuArrow = 16844019; // 0x10104f3
field public static final int submitBackground = 16843912; // 0x1010488
field public static final int subtitle = 16843473; // 0x10102d1
@@ -12880,16 +12881,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
}
- public final class UserProperties implements android.os.Parcelable {
- method public int describeContents();
- method public int getShowInLauncher();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
- field public static final int SHOW_IN_LAUNCHER_NO = 2; // 0x2
- field public static final int SHOW_IN_LAUNCHER_SEPARATE = 1; // 0x1
- field public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0; // 0x0
- }
-
public final class VersionedPackage implements android.os.Parcelable {
ctor public VersionedPackage(@NonNull String, int);
ctor public VersionedPackage(@NonNull String, long);
@@ -19044,15 +19035,15 @@
method public int getSurfaceGroupId();
method @NonNull public java.util.List<android.view.Surface> getSurfaces();
method public int getTimestampBase();
- method public boolean isReadoutTimestampUsed();
+ method public boolean isReadoutTimestampEnabled();
method public void removeSensorPixelModeUsed(int);
method public void removeSurface(@NonNull android.view.Surface);
method public void setDynamicRangeProfile(long);
method public void setMirrorMode(int);
method public void setPhysicalCameraId(@Nullable String);
+ method public void setReadoutTimestampEnabled(boolean);
method public void setStreamUseCase(long);
method public void setTimestampBase(int);
- method public void useReadoutTimestamp(boolean);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int MIRROR_MODE_AUTO = 0; // 0x0
@@ -33224,7 +33215,6 @@
method public android.os.UserHandle getUserForSerialNumber(long);
method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS", "android.permission.QUERY_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public String getUserName();
method public java.util.List<android.os.UserHandle> getUserProfiles();
- method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.QUERY_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
method public android.os.Bundle getUserRestrictions();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
@@ -35375,7 +35365,7 @@
}
public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final String ADDRESS = "data1";
field public static final android.net.Uri CONTENT_FILTER_URI;
@@ -35396,7 +35386,7 @@
}
public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeResource(Integer);
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35429,7 +35419,7 @@
public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
method public static CharSequence getProtocolLabel(android.content.res.Resources, int, CharSequence);
method public static int getProtocolLabelResource(int);
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
field public static final String CUSTOM_PROTOCOL = "data6";
@@ -35475,7 +35465,7 @@
}
public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final String COMPANY = "data1";
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
@@ -35494,7 +35484,7 @@
}
public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final android.net.Uri CONTENT_FILTER_URI;
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
@@ -35541,7 +35531,7 @@
}
public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35565,7 +35555,7 @@
}
public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -35596,7 +35586,7 @@
}
public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, CharSequence);
+ method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
method public static int getTypeLabelResource(int);
field public static final String CITY = "data7";
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2";
@@ -44545,6 +44535,7 @@
field public static final int RESULT_SMS_SEND_RETRY_FAILED = 30; // 0x1e
field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf
field public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28; // 0x1c
+ field public static final int RESULT_USER_NOT_ALLOWED = 33; // 0x21
field public static final int SMS_RP_CAUSE_CALL_BARRING = 10; // 0xa
field public static final int SMS_RP_CAUSE_CONGESTION = 42; // 0x2a
field public static final int SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER = 27; // 0x1b
@@ -52961,10 +52952,10 @@
method public void onRestrictedCaptionAreaChanged(android.graphics.Rect);
}
- public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.view.WindowAnimationFrameStats> CREATOR;
+ @Deprecated public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.view.WindowAnimationFrameStats> CREATOR;
}
public final class WindowContentFrameStats extends android.view.FrameStats implements android.os.Parcelable {
@@ -54927,6 +54918,7 @@
public final class InputMethodInfo implements android.os.Parcelable {
ctor public InputMethodInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
ctor public InputMethodInfo(String, String, CharSequence, String);
+ method @Nullable public android.content.Intent createStylusHandwritingSettingsActivityIntent();
method public int describeContents();
method public void dump(android.util.Printer, String);
method public android.content.ComponentName getComponent();
@@ -54945,6 +54937,7 @@
method public boolean supportsStylusHandwriting();
method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final String ACTION_STYLUS_HANDWRITING_SETTINGS = "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d3775ad..d3a655c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3908,6 +3908,14 @@
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@NonNull String);
}
+ public final class UserProperties implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean isCredentialShareableWithParent();
+ method public boolean isMediaSharedWithParent();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+ }
+
}
package android.content.pm.dex {
@@ -8759,6 +8767,7 @@
field public static final int TYPE_DVBC = 4; // 0x4
field public static final int TYPE_DVBS = 5; // 0x5
field public static final int TYPE_DVBT = 6; // 0x6
+ field public static final int TYPE_IPTV = 11; // 0xb
field public static final int TYPE_ISDBS = 7; // 0x7
field public static final int TYPE_ISDBS3 = 8; // 0x8
field public static final int TYPE_ISDBT = 9; // 0x9
@@ -8781,6 +8790,11 @@
method public int getHierarchy();
method public long getInnerFec();
method @NonNull public int[] getInterleaving();
+ method @IntRange(from=0) public int getIptvAverageJitterMillis();
+ method @NonNull public String getIptvContentUrl();
+ method @IntRange(from=0) public long getIptvPacketsLost();
+ method @IntRange(from=0) public long getIptvPacketsReceived();
+ method @IntRange(from=0) public int getIptvWorstJitterMillis();
method public int getIsdbtMode();
method public int getIsdbtPartialReceptionFlag();
method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
@@ -8824,6 +8838,11 @@
field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS = 46; // 0x2e
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL = 42; // 0x2a
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST = 43; // 0x2b
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED = 44; // 0x2c
+ field public static final int FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS = 45; // 0x2d
field public static final int FRONTEND_STATUS_TYPE_ISDBT_MODE = 37; // 0x25
field public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG = 38; // 0x26
field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
@@ -8869,6 +8888,60 @@
field public static final int FRONTEND_STATUS_READINESS_UNSUPPORTED = 4; // 0x4
}
+ public class IptvFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+ ctor public IptvFrontendSettings(@NonNull byte[], @NonNull byte[], int, int, @NonNull android.media.tv.tuner.frontend.IptvFrontendSettingsFec, int, int, long, @NonNull String);
+ method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettings.Builder builder();
+ method @IntRange(from=0) public long getBitrate();
+ method @NonNull public String getContentUrl();
+ method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
+ method public int getDstPort();
+ method @Nullable public android.media.tv.tuner.frontend.IptvFrontendSettingsFec getFec();
+ method public int getIgmp();
+ method public int getProtocol();
+ method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
+ method public int getSrcPort();
+ method public int getType();
+ field public static final int IGMP_UNDEFINED = 0; // 0x0
+ field public static final int IGMP_V1 = 1; // 0x1
+ field public static final int IGMP_V2 = 2; // 0x2
+ field public static final int IGMP_V3 = 4; // 0x4
+ field public static final int PROTOCOL_RTP = 2; // 0x2
+ field public static final int PROTOCOL_UDP = 1; // 0x1
+ field public static final int PROTOCOL_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class IptvFrontendSettings.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setBitrate(@IntRange(from=0) long);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setContentUrl(@NonNull String);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setDstIpAddress(@NonNull byte[]);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setDstPort(int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setFec(@Nullable android.media.tv.tuner.frontend.IptvFrontendSettingsFec);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setIgmp(int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setProtocol(int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcIpAddress(@NonNull byte[]);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcPort(int);
+ }
+
+ public class IptvFrontendSettingsFec {
+ ctor public IptvFrontendSettingsFec(int, int, int);
+ method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder builder();
+ method @IntRange(from=0) public int getFecColNum();
+ method @IntRange(from=0) public int getFecRowNum();
+ method public int getFecType();
+ field public static final int FEC_TYPE_COLUMN = 1; // 0x1
+ field public static final int FEC_TYPE_COLUMN_ROW = 4; // 0x4
+ field public static final int FEC_TYPE_ROW = 2; // 0x2
+ field public static final int FEC_TYPE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class IptvFrontendSettingsFec.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec build();
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecColNum(@IntRange(from=0) int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecRowNum(@IntRange(from=0) int);
+ method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecType(int);
+ }
+
public class Isdbs3FrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
method public int getCodeRateCapability();
method public int getModulationCapability();
@@ -10415,6 +10488,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public int getUserRestrictionSource(String, android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int getUserSwitchability();
@@ -10423,11 +10497,11 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
method public boolean isCloneProfile();
- method public boolean isCredentialSharableWithParent();
+ method @Deprecated public boolean isCredentialSharableWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isMainUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
- method public boolean isMediaSharedWithParent();
+ method @Deprecated public boolean isMediaSharedWithParent();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser();
method public static boolean isRemoveResultSuccessful(int);
method public boolean isRestrictedProfile();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3a6173c..8dc7dc3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -759,6 +759,7 @@
method public int getUserId();
method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
+ method public void updateDeviceId(int);
field public static final String ATTENTION_SERVICE = "attention";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
@@ -945,6 +946,13 @@
field public String userType;
}
+ public final class UserProperties implements android.os.Parcelable {
+ method public int getShowInLauncher();
+ field public static final int SHOW_IN_LAUNCHER_NO = 2; // 0x2
+ field public static final int SHOW_IN_LAUNCHER_SEPARATE = 1; // 0x1
+ field public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0; // 0x0
+ }
+
}
package android.content.res {
@@ -974,6 +982,59 @@
}
+package android.credentials {
+
+ public final class CredentialDescription implements android.os.Parcelable {
+ ctor public CredentialDescription(@NonNull String, @NonNull String, @NonNull java.util.List<android.service.credentials.CredentialEntry>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.service.credentials.CredentialEntry> getCredentialEntries();
+ method @NonNull public String getFlattenedRequestString();
+ method @NonNull public String getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialDescription> CREATOR;
+ }
+
+ public final class CredentialManager {
+ method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.RegisterCredentialDescriptionException>);
+ method public void unRegisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.UnregisterCredentialDescriptionException>);
+ }
+
+ public class RegisterCredentialDescriptionException extends java.lang.Exception {
+ ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String);
+ ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
+ ctor public RegisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
+ ctor public RegisterCredentialDescriptionException(@NonNull String);
+ field @NonNull public final String errorType;
+ }
+
+ public final class RegisterCredentialDescriptionRequest implements android.os.Parcelable {
+ ctor public RegisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+ ctor public RegisterCredentialDescriptionRequest(@NonNull java.util.List<android.credentials.CredentialDescription>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.credentials.CredentialDescription> getCredentialDescriptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.RegisterCredentialDescriptionRequest> CREATOR;
+ field public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
+ }
+
+ public class UnregisterCredentialDescriptionException extends java.lang.Exception {
+ ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String);
+ ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable String, @Nullable Throwable);
+ ctor public UnregisterCredentialDescriptionException(@NonNull String, @Nullable Throwable);
+ ctor public UnregisterCredentialDescriptionException(@NonNull String);
+ field @NonNull public final String errorType;
+ }
+
+ public final class UnregisterCredentialDescriptionRequest implements android.os.Parcelable {
+ ctor public UnregisterCredentialDescriptionRequest(@NonNull android.credentials.CredentialDescription);
+ method public int describeContents();
+ method @NonNull public android.credentials.CredentialDescription getCredentialDescription();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.UnregisterCredentialDescriptionRequest> CREATOR;
+ }
+
+}
+
package android.credentials.ui {
public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable {
@@ -3367,6 +3428,7 @@
}
public final class InputMethodInfo implements android.os.Parcelable {
+ ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, boolean, @NonNull String);
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 89740af..12899f2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3062,7 +3062,14 @@
@Override
public boolean isDeviceContext() {
- return mIsExplicitDeviceId || isAssociatedWithDisplay();
+ if (mIsExplicitDeviceId) {
+ if (mDeviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+ return true;
+ }
+ VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
+ return vdm.isValidVirtualDeviceId(mDeviceId);
+ }
+ return isAssociatedWithDisplay();
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e5523a..9843c8f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11049,6 +11049,7 @@
* @throws SecurityException if the caller is not a profile owner on an organization-owned
* managed profile.
* @throws IllegalStateException if called after the device setup has been completed.
+ * @throws UnsupportedOperationException if the api is not enabled.
* @see ManagedSubscriptionsPolicy
*/
public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 1bc3091..d585e8f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -361,8 +361,12 @@
private final Context mContext;
private final IVirtualDeviceManager mService;
private final IVirtualDevice mVirtualDevice;
+ private final Object mActivityListenersLock = new Object();
+ @GuardedBy("mActivityListenersLock")
private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
new ArrayMap<>();
+ private final Object mIntentInterceptorListenersLock = new Object();
+ @GuardedBy("mIntentInterceptorListenersLock")
private final ArrayMap<IntentInterceptorCallback,
VirtualIntentInterceptorDelegate> mIntentInterceptorListeners =
new ArrayMap<>();
@@ -377,9 +381,11 @@
public void onTopActivityChanged(int displayId, ComponentName topActivity) {
final long token = Binder.clearCallingIdentity();
try {
- for (int i = 0; i < mActivityListeners.size(); i++) {
- mActivityListeners.valueAt(i)
- .onTopActivityChanged(displayId, topActivity);
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i)
+ .onTopActivityChanged(displayId, topActivity);
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -390,8 +396,10 @@
public void onDisplayEmpty(int displayId) {
final long token = Binder.clearCallingIdentity();
try {
- for (int i = 0; i < mActivityListeners.size(); i++) {
- mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -960,7 +968,11 @@
*/
public void addActivityListener(
@CallbackExecutor @NonNull Executor executor, @NonNull ActivityListener listener) {
- mActivityListeners.put(listener, new ActivityListenerDelegate(listener, executor));
+ final ActivityListenerDelegate delegate = new ActivityListenerDelegate(
+ Objects.requireNonNull(listener), Objects.requireNonNull(executor));
+ synchronized (mActivityListenersLock) {
+ mActivityListeners.put(listener, delegate);
+ }
}
/**
@@ -971,7 +983,9 @@
* @see #addActivityListener(Executor, ActivityListener)
*/
public void removeActivityListener(@NonNull ActivityListener listener) {
- mActivityListeners.remove(listener);
+ synchronized (mActivityListenersLock) {
+ mActivityListeners.remove(Objects.requireNonNull(listener));
+ }
}
/**
@@ -999,7 +1013,7 @@
*/
public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) {
synchronized (mSoundEffectListenersLock) {
- mSoundEffectListeners.remove(soundEffectListener);
+ mSoundEffectListeners.remove(Objects.requireNonNull(soundEffectListener));
}
}
@@ -1029,7 +1043,9 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- mIntentInterceptorListeners.put(interceptorCallback, delegate);
+ synchronized (mIntentInterceptorListenersLock) {
+ mIntentInterceptorListeners.put(interceptorCallback, delegate);
+ }
}
/**
@@ -1040,14 +1056,17 @@
public void unregisterIntentInterceptor(
@NonNull IntentInterceptorCallback interceptorCallback) {
Objects.requireNonNull(interceptorCallback);
- final VirtualIntentInterceptorDelegate delegate =
- mIntentInterceptorListeners.get(interceptorCallback);
- try {
- mVirtualDevice.unregisterIntentInterceptor(delegate);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ final VirtualIntentInterceptorDelegate delegate;
+ synchronized (mIntentInterceptorListenersLock) {
+ delegate = mIntentInterceptorListeners.remove(interceptorCallback);
}
- mIntentInterceptorListeners.remove(interceptorCallback);
+ if (delegate != null) {
+ try {
+ mVirtualDevice.unregisterIntentInterceptor(delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7310138..5f1502f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7342,6 +7342,7 @@
* @see #createDeviceContext(int)
* @hide
*/
+ @TestApi
public void updateDeviceId(int deviceId) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
@@ -7378,10 +7379,12 @@
/**
* Indicates whether the value of {@link Context#getDeviceId()} can be relied upon for
* this instance. It will return {@code true} for Contexts created by
- * {@link Context#createDeviceContext(int)}, as well as for UI and Display Contexts.
+ * {@link Context#createDeviceContext(int)} which reference a valid device ID, as well as for
+ * UI and Display Contexts.
* <p>
* Contexts created with {@link Context#createDeviceContext(int)} will have an explicit
- * device association, which will never change. UI Contexts and Display Contexts are
+ * device association, which will never change, even if the underlying device is closed or is
+ * removed. UI Contexts and Display Contexts are
* already associated with a display, so if the device association is not explicitly
* given, {@link Context#getDeviceId()} will return the ID of the device associated with
* the associated display. The system can assign an arbitrary device id value for Contexts not
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 51662af..824d15c 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -19,6 +19,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Slog;
@@ -36,7 +38,10 @@
/**
* Class holding the properties of a user that derive mostly from its user type.
+ *
+ * @hide
*/
+@SystemApi
public final class UserProperties implements Parcelable {
private static final String LOG_TAG = UserProperties.class.getSimpleName();
@@ -52,6 +57,10 @@
"crossProfileIntentFilterAccessControl";
private static final String ATTR_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY =
"crossProfileIntentResolutionStrategy";
+ private static final String ATTR_MEDIA_SHARED_WITH_PARENT =
+ "mediaSharedWithParent";
+ private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
+ "credentialShareableWithParent";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
@@ -62,7 +71,9 @@
INDEX_USE_PARENTS_CONTACTS,
INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA,
INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
- INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY
+ INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
+ INDEX_MEDIA_SHARED_WITH_PARENT,
+ INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -75,6 +86,8 @@
private static final int INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA = 5;
private static final int INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL = 6;
private static final int INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY = 7;
+ private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
+ private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -95,16 +108,22 @@
* Suggests that the launcher should show this user's apps in the main tab.
* That is, either this user is a full user, so its apps should be presented accordingly, or, if
* this user is a profile, then its apps should be shown alongside its parent's apps.
+ * @hide
*/
+ @TestApi
public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0;
/**
* Suggests that the launcher should show this user's apps, but separately from the apps of this
* user's parent.
+ * @hide
*/
+ @TestApi
public static final int SHOW_IN_LAUNCHER_SEPARATE = 1;
/**
* Suggests that the launcher should not show this user.
+ * @hide
*/
+ @TestApi
public static final int SHOW_IN_LAUNCHER_NO = 2;
/**
@@ -304,6 +323,8 @@
}
// Add items that have no permission requirements at all.
setShowInLauncher(orig.getShowInLauncher());
+ setMediaSharedWithParent(orig.isMediaSharedWithParent());
+ setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
}
/**
@@ -337,7 +358,9 @@
* and {@link #SHOW_IN_LAUNCHER_NO}.
*
* @return whether, and how, a profile should be shown in the Launcher.
+ * @hide
*/
+ @TestApi
public @ShowInLauncher int getShowInLauncher() {
if (isPresent(INDEX_SHOW_IN_LAUNCHER)) return mShowInLauncher;
if (mDefaultProperties != null) return mDefaultProperties.mShowInLauncher;
@@ -463,13 +486,47 @@
throw new SecurityException("You don't have permission to query "
+ "updateCrossProfileIntentFiltersOnOTA");
}
-
/** @hide */
public void setUpdateCrossProfileIntentFiltersOnOTA(boolean val) {
this.mUpdateCrossProfileIntentFiltersOnOTA = val;
setPresent(INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA);
}
+ /**
+ * Returns whether a profile shares media with its parent user.
+ * This only applies for users that have parents (i.e. for profiles).
+ */
+ public boolean isMediaSharedWithParent() {
+ if (isPresent(INDEX_MEDIA_SHARED_WITH_PARENT)) return mMediaSharedWithParent;
+ if (mDefaultProperties != null) return mDefaultProperties.mMediaSharedWithParent;
+ throw new SecurityException("You don't have permission to query mediaSharedWithParent");
+ }
+ /** @hide */
+ public void setMediaSharedWithParent(boolean val) {
+ this.mMediaSharedWithParent = val;
+ setPresent(INDEX_MEDIA_SHARED_WITH_PARENT);
+ }
+ private boolean mMediaSharedWithParent;
+
+ /**
+ * Returns whether a profile can have shared lockscreen credential with its parent user.
+ * This only applies for users that have parents (i.e. for profiles).
+ */
+ public boolean isCredentialShareableWithParent() {
+ if (isPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT)) {
+ return mCredentialShareableWithParent;
+ }
+ if (mDefaultProperties != null) return mDefaultProperties.mCredentialShareableWithParent;
+ throw new SecurityException(
+ "You don't have permission to query credentialShareableWithParent");
+ }
+ /** @hide */
+ public void setCredentialShareableWithParent(boolean val) {
+ this.mCredentialShareableWithParent = val;
+ setPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT);
+ }
+ private boolean mCredentialShareableWithParent;
+
/*
Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during
OTA update between user-parent
@@ -550,6 +607,8 @@
+ getCrossProfileIntentFilterAccessControl()
+ ", mCrossProfileIntentResolutionStrategy="
+ getCrossProfileIntentResolutionStrategy()
+ + ", mMediaSharedWithParent=" + isMediaSharedWithParent()
+ + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+ "}";
}
@@ -572,6 +631,9 @@
+ getCrossProfileIntentFilterAccessControl());
pw.println(prefix + " mCrossProfileIntentResolutionStrategy="
+ getCrossProfileIntentResolutionStrategy());
+ pw.println(prefix + " mMediaSharedWithParent=" + isMediaSharedWithParent());
+ pw.println(prefix + " mCredentialShareableWithParent="
+ + isCredentialShareableWithParent());
}
/**
@@ -629,6 +691,12 @@
case ATTR_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY:
setCrossProfileIntentResolutionStrategy(parser.getAttributeInt(i));
break;
+ case ATTR_MEDIA_SHARED_WITH_PARENT:
+ setMediaSharedWithParent(parser.getAttributeBoolean(i));
+ break;
+ case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
+ setCredentialShareableWithParent(parser.getAttributeBoolean(i));
+ break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -676,6 +744,14 @@
serializer.attributeInt(null, ATTR_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
mCrossProfileIntentResolutionStrategy);
}
+ if (isPresent(INDEX_MEDIA_SHARED_WITH_PARENT)) {
+ serializer.attributeBoolean(null, ATTR_MEDIA_SHARED_WITH_PARENT,
+ mMediaSharedWithParent);
+ }
+ if (isPresent(INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT)) {
+ serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
+ mCredentialShareableWithParent);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -690,6 +766,8 @@
dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA);
dest.writeInt(mCrossProfileIntentFilterAccessControl);
dest.writeInt(mCrossProfileIntentResolutionStrategy);
+ dest.writeBoolean(mMediaSharedWithParent);
+ dest.writeBoolean(mCredentialShareableWithParent);
}
/**
@@ -708,6 +786,8 @@
mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean();
mCrossProfileIntentFilterAccessControl = source.readInt();
mCrossProfileIntentResolutionStrategy = source.readInt();
+ mMediaSharedWithParent = source.readBoolean();
+ mCredentialShareableWithParent = source.readBoolean();
}
@Override
@@ -743,6 +823,8 @@
CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_ALL;
private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy =
CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
+ private boolean mMediaSharedWithParent = false;
+ private boolean mCredentialShareableWithParent = false;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
@@ -794,6 +876,16 @@
return this;
}
+ public Builder setMediaSharedWithParent(boolean mediaSharedWithParent) {
+ mMediaSharedWithParent = mediaSharedWithParent;
+ return this;
+ }
+
+ public Builder setCredentialShareableWithParent(boolean credentialShareableWithParent) {
+ mCredentialShareableWithParent = credentialShareableWithParent;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated. */
public UserProperties build() {
return new UserProperties(
@@ -804,7 +896,9 @@
mUseParentsContacts,
mUpdateCrossProfileIntentFiltersOnOTA,
mCrossProfileIntentFilterAccessControl,
- mCrossProfileIntentResolutionStrategy);
+ mCrossProfileIntentResolutionStrategy,
+ mMediaSharedWithParent,
+ mCredentialShareableWithParent);
}
} // end Builder
@@ -816,7 +910,9 @@
@InheritDevicePolicy int inheritDevicePolicy,
boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA,
@CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
- @CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy) {
+ @CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
+ boolean mediaSharedWithParent,
+ boolean credentialShareableWithParent) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
@@ -827,5 +923,7 @@
setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
setCrossProfileIntentFilterAccessControl(crossProfileIntentFilterAccessControl);
setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
+ setMediaSharedWithParent(mediaSharedWithParent);
+ setCredentialShareableWithParent(credentialShareableWithParent);
}
}
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index b4310f2..ec6a396 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -17,6 +17,7 @@
package android.credentials;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.credentials.CredentialEntry;
@@ -32,6 +33,7 @@
* Represents the type and contained data fields of a {@link Credential}.
* @hide
*/
+@TestApi
public final class CredentialDescription implements Parcelable {
/**
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index e15cec8..232d063 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -329,6 +330,7 @@
*
* @hide
*/
+ @TestApi
public void registerCredentialDescription(
@NonNull RegisterCredentialDescriptionRequest request,
@Nullable CancellationSignal cancellationSignal,
@@ -376,6 +378,7 @@
*
* @hide
*/
+ @TestApi
public void unRegisterCredentialDescription(
@NonNull UnregisterCredentialDescriptionRequest request,
@Nullable CancellationSignal cancellationSignal,
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionException.java b/core/java/android/credentials/RegisterCredentialDescriptionException.java
index 3cf5a75..d19bb8e 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionException.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionException.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
@@ -32,6 +33,7 @@
*
* @hide
*/
+@TestApi
public class RegisterCredentialDescriptionException extends Exception {
@NonNull public final String errorType;
diff --git a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
index de31279..f257ac5 100644
--- a/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/RegisterCredentialDescriptionRequest.java
@@ -19,6 +19,7 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,6 +37,7 @@
*
* @hide
*/
+@TestApi
public final class RegisterCredentialDescriptionRequest implements Parcelable {
public static final String FLATTENED_REQUEST_STRING_KEY = "flattened_request_string";
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionException.java b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
index 0c786bd..a16915a 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionException.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionException.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
@@ -32,6 +33,7 @@
*
* @hide
*/
+@TestApi
public class UnregisterCredentialDescriptionException extends Exception {
@NonNull public final String errorType;
diff --git a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
index f3454c1..6cf40e7 100644
--- a/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
+++ b/core/java/android/credentials/UnregisterCredentialDescriptionRequest.java
@@ -19,6 +19,7 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@
*
* @hide
*/
+@TestApi
public final class UnregisterCredentialDescriptionRequest implements Parcelable {
@NonNull
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ff6e897..c9fc722 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -1246,7 +1246,7 @@
* between frames.</p>
*
* <p>The timestamps match the timestamps of the output surfaces with readout timestamp
- * enabled (via {@link OutputConfiguration#useReadoutTimestamp}) if:</p>
+ * enabled (via {@link OutputConfiguration#setReadoutTimestampEnabled}) if:</p>
* <ul>
* <li> Timestamp base is {@link OutputConfiguration#TIMESTAMP_BASE_DEFAULT} and the
* output
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 11b80cc..f20b25f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -4600,7 +4600,7 @@
* {@link CameraCaptureSession.CaptureCallback#onCaptureStarted }.</p>
* <p>In addition, the application can switch an output surface's timestamp from start of
* exposure to start of readout by calling
- * {@link android.hardware.camera2.params.OutputConfiguration#useReadoutTimestamp }.</p>
+ * {@link android.hardware.camera2.params.OutputConfiguration#setReadoutTimestampEnabled }.</p>
* <p>The readout timestamp is beneficial for video recording, because the encoder favors
* uniform timestamps, and the readout timestamps better reflect the cadence camera sensors
* output data.</p>
@@ -5688,4 +5688,5 @@
+
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3d83009..381c87d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -4198,4 +4198,5 @@
+
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index dad7d3e..635e79c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -5699,4 +5699,5 @@
+
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 8b7c5ec..857f62d 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -257,7 +257,7 @@
/**
* Timestamp is the start of readout in the same time domain as TIMESTAMP_BASE_SENSOR.
*
- * <p>NOTE: do not use! Use useReadoutTimestamp instead.</p>
+ * <p>NOTE: do not use! Use setReadoutTimestampEnabled instead.</p>
*
* @hide
*/
@@ -574,7 +574,7 @@
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mTimestampBase = TIMESTAMP_BASE_DEFAULT;
mMirrorMode = MIRROR_MODE_AUTO;
- mUseReadoutTimestamp = false;
+ mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
}
@@ -676,7 +676,7 @@
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
- mUseReadoutTimestamp = false;
+ mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
}
@@ -1050,7 +1050,7 @@
if (timestampBase == TIMESTAMP_BASE_READOUT_SENSOR) {
mTimestampBase = TIMESTAMP_BASE_SENSOR;
- mUseReadoutTimestamp = true;
+ mReadoutTimestampEnabled = true;
mIsReadoutSensorTimestampBase = true;
} else {
mTimestampBase = timestampBase;
@@ -1131,14 +1131,16 @@
* @param on The output image timestamp is the start of exposure time if false, and
* the start of readout time if true.
*/
- public void useReadoutTimestamp(boolean on) {
- mUseReadoutTimestamp = on;
+ public void setReadoutTimestampEnabled(boolean on) {
+ mReadoutTimestampEnabled = on;
}
/** Whether readout timestamp is used for this OutputConfiguration.
+ *
+ * @see #setReadoutTimestampEnabled
*/
- public boolean isReadoutTimestampUsed() {
- return mUseReadoutTimestamp;
+ public boolean isReadoutTimestampEnabled() {
+ return mReadoutTimestampEnabled;
}
/**
@@ -1172,7 +1174,7 @@
this.mStreamUseCase = other.mStreamUseCase;
this.mTimestampBase = other.mTimestampBase;
this.mMirrorMode = other.mMirrorMode;
- this.mUseReadoutTimestamp = other.mUseReadoutTimestamp;
+ this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
}
/**
@@ -1200,7 +1202,7 @@
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
- boolean useReadoutTimestamp = source.readInt() == 1;
+ boolean readoutTimestampEnabled = source.readInt() == 1;
mSurfaceGroupId = surfaceSetId;
mRotation = rotation;
@@ -1229,7 +1231,7 @@
mStreamUseCase = streamUseCase;
mTimestampBase = timestampBase;
mMirrorMode = mirrorMode;
- mUseReadoutTimestamp = useReadoutTimestamp;
+ mReadoutTimestampEnabled = readoutTimestampEnabled;
}
/**
@@ -1350,7 +1352,7 @@
dest.writeLong(mStreamUseCase);
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
- dest.writeInt(mUseReadoutTimestamp ? 1 : 0);
+ dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
}
/**
@@ -1385,7 +1387,7 @@
mStreamUseCase != other.mStreamUseCase ||
mTimestampBase != other.mTimestampBase ||
mMirrorMode != other.mMirrorMode ||
- mUseReadoutTimestamp != other.mUseReadoutTimestamp)
+ mReadoutTimestampEnabled != other.mReadoutTimestampEnabled)
return false;
if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
return false;
@@ -1428,7 +1430,7 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase,
- mTimestampBase, mMirrorMode, mUseReadoutTimestamp ? 1 : 0);
+ mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
}
return HashCodeHelpers.hashCode(
@@ -1438,7 +1440,7 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
- mMirrorMode, mUseReadoutTimestamp ? 1 : 0);
+ mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
}
private static final String TAG = "OutputConfiguration";
@@ -1480,8 +1482,8 @@
private int mTimestampBase;
// Mirroring mode
private int mMirrorMode;
- // Use readout timestamp
- private boolean mUseReadoutTimestamp;
+ // readout timestamp
+ private boolean mReadoutTimestampEnabled;
// Whether the timestamp base is set to READOUT_SENSOR
private boolean mIsReadoutSensorTimestampBase;
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index dfd9054..f27c34c 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -175,7 +175,7 @@
* The <code>android:label</code> attribute specifies a human-readable descriptive
* label to describe the keyboard layout in the user interface, such as "English (US)".
* The <code>android:keyboardLayout</code> attribute refers to a
- * <a href="http://source.android.com/tech/input/key-character-map-files.html">
+ * <a href="https://source.android.com/docs/core/interaction/input/key-character-map-files">
* key character map</a> resource that defines the keyboard layout.
* The <code>android:keyboardLocale</code> attribute specifies a comma separated list of BCP 47
* language tags depicting the locales supported by the keyboard layout. This attribute is
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index ada5c55..62d9c69 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1006,12 +1006,15 @@
// been replaced with an implementation that will suspendAll and
// send VM_START.
System.out.println("Waiting for debugger first packet");
+
+ mWaiting = true;
while (!isDebuggerConnected()) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
}
}
+ mWaiting = false;
System.out.println("Debug.suspendAllAndSentVmStart");
VMDebug.suspendAllAndSendVmStart();
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index d1d3315..797730b 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -116,8 +116,6 @@
boolean someUserHasSeedAccount(in String accountName, in String accountType);
boolean someUserHasAccount(in String accountName, in String accountType);
String getProfileType(int userId);
- boolean isMediaSharedWithParent(int userId);
- boolean isCredentialSharableWithParent(int userId);
boolean isDemoUser(int userId);
boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index dca722e..4ce9184 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -123,6 +123,9 @@
@TestApi
public static final int MIN_SECONDARY_USER_ID = 10;
+ /** @hide */
+ public static final int MAX_SECONDARY_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+
/**
* (Arbitrary) user handle cache size.
* {@link #CACHED_USER_HANDLES} caches user handles in the range of
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9a25c70..d63d87d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3248,7 +3248,10 @@
* @param userHandle the user handle of the user whose information is being requested.
* @return a UserProperties object for a specific user.
* @throws IllegalArgumentException if {@code userHandle} doesn't correspond to an existing user
+ *
+ * @hide
*/
+ @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.QUERY_USERS,
@@ -5165,19 +5168,25 @@
* Returns false for any other type of user.
*
* @return true if the user shares media with its parent user, false otherwise.
+ *
+ * @deprecated use {@link #getUserProperties(UserHandle)} with
+ * {@link UserProperties#isMediaSharedWithParent()} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@UserHandleAware(
requiresAnyOfPermissionsIfNotCallerProfileGroup = {
Manifest.permission.MANAGE_USERS,
+ Manifest.permission.QUERY_USERS,
Manifest.permission.INTERACT_ACROSS_USERS})
@SuppressAutoDoc
public boolean isMediaSharedWithParent() {
try {
- return mService.isMediaSharedWithParent(mUserId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ return getUserProperties(UserHandle.of(mUserId)).isMediaSharedWithParent();
+ } catch (IllegalArgumentException e) {
+ // If the user doesn't exist, return false (for historical reasons)
+ return false;
}
}
@@ -5187,19 +5196,24 @@
* This API only works for {@link UserManager#isProfile() profiles}
* and will always return false for any other user type.
*
+ * @deprecated use {@link #getUserProperties(UserHandle)} with
+ * {@link UserProperties#isCredentialShareableWithParent()} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@UserHandleAware(
requiresAnyOfPermissionsIfNotCallerProfileGroup = {
Manifest.permission.MANAGE_USERS,
+ Manifest.permission.QUERY_USERS,
Manifest.permission.INTERACT_ACROSS_USERS})
@SuppressAutoDoc
public boolean isCredentialSharableWithParent() {
try {
- return mService.isCredentialSharableWithParent(mUserId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ return getUserProperties(UserHandle.of(mUserId)).isCredentialShareableWithParent();
+ } catch (IllegalArgumentException e) {
+ // If the user doesn't exist, return false (for historical reasons)
+ return false;
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a72ccad..80dd488 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -18,17 +18,10 @@
import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO;
import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
-import static android.app.AppOpsManager.OP_READ_MEDIA_VIDEO;
-import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_AUDIO;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_IMAGES;
-import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.PER_USER_RANGE;
@@ -1869,51 +1862,14 @@
// handle obscure cases like when an app targets Q but was installed on
// a device that was originally running on P before being upgraded to Q.
- /** {@hide} */
- public boolean checkPermissionReadAudio(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_READ_MEDIA_AUDIO);
- }
-
- /** {@hide} */
- public boolean checkPermissionWriteAudio(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_WRITE_MEDIA_AUDIO);
- }
-
- /** {@hide} */
- public boolean checkPermissionReadVideo(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_READ_MEDIA_VIDEO);
- }
-
- /** {@hide} */
- public boolean checkPermissionWriteVideo(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_WRITE_MEDIA_VIDEO);
- }
-
- /** {@hide} */
+ /**
+ * @deprecated This method should not be used since it check slegacy permissions,
+ * no longer valid. Clients should check the appropriate permissions directly
+ * instead (e.g. READ_MEDIA_IMAGES).
+ *
+ * {@hide}
+ */
+ @Deprecated
public boolean checkPermissionReadImages(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
@@ -1924,17 +1880,6 @@
OP_READ_MEDIA_IMAGES);
}
- /** {@hide} */
- public boolean checkPermissionWriteImages(boolean enforce,
- int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
- WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
- return false;
- }
- return noteAppOpAllowingLegacy(enforce, pid, uid, packageName, featureId,
- OP_WRITE_MEDIA_IMAGES);
- }
-
private boolean checkExternalStoragePermissionAndAppOp(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId, String permission,
int op) {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 363d035..bfc5afe 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6422,7 +6422,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if ((type == TYPE_CUSTOM || type == TYPE_ASSISTANT) && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -6634,7 +6634,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -6842,7 +6842,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -7003,7 +7003,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -7210,7 +7210,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -7337,7 +7337,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -7433,7 +7433,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
@@ -7762,7 +7762,7 @@
* for {@link #TYPE_CUSTOM}.
*/
public static final CharSequence getTypeLabel(Resources res, int type,
- CharSequence label) {
+ @Nullable CharSequence label) {
if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
return label;
} else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b05f21..9de424f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9738,7 +9738,7 @@
/**
* Indicates whether "seen" notifications should be suppressed from the lockscreen.
* <p>
- * Type: int (0 for false, 1 for true)
+ * Type: int (0 for unset, 1 for true, 2 for false)
*
* @hide
*/
@@ -14568,15 +14568,6 @@
"app_auto_restriction_enabled";
/**
- * Feature flag to enable or disable the Forced App Standby feature.
- * Type: int (0 for false, 1 for true)
- * Default: 1
- * @hide
- */
- @Readable
- public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
-
- /**
* Whether or not to enable Forced App Standby on small battery devices.
* Type: int (0 for false, 1 for true)
* Default: 0
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index a389223..ff14404 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -70,7 +70,7 @@
@Override
public void onDestroy() {
- if (mCallback != null) {
+ if (mCallback != null && !isFinishing()) {
mCallback.onActivityDestroyed();
}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index df78827..99a7fe5 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -640,6 +640,10 @@
mConstructorArgs[0] = inflaterContext;
View result = root;
+ if (root != null && root.getViewRootImpl() != null) {
+ root.getViewRootImpl().notifyRendererOfExpensiveFrame();
+ }
+
try {
advanceToRootNode(parser);
final String name = parser.getName();
@@ -662,6 +666,10 @@
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
+ if (root == null && temp != null && temp.getViewRootImpl() != null) {
+ temp.getViewRootImpl().notifyRendererOfExpensiveFrame();
+ }
+
ViewGroup.LayoutParams params = null;
if (root != null) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 164a494..9c6e823 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -594,6 +594,13 @@
}
}
+ @Override
+ public void notifyExpensiveFrame() {
+ if (isEnabled()) {
+ super.notifyExpensiveFrame();
+ }
+ }
+
/**
* Updates the light position based on the position of the window.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5dccd06..5165478 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2334,6 +2334,18 @@
}
}
+ /**
+ * Notifies the HardwareRenderer of an expensive upcoming frame, to
+ * allow better handling of power and scheduling requirements.
+ *
+ * @hide
+ */
+ void notifyRendererOfExpensiveFrame() {
+ if (mAttachInfo.mThreadedRenderer != null) {
+ mAttachInfo.mThreadedRenderer.notifyExpensiveFrame();
+ }
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void scheduleTraversals() {
if (!mTraversalScheduled) {
diff --git a/core/java/android/view/WindowAnimationFrameStats.java b/core/java/android/view/WindowAnimationFrameStats.java
index 251ae9b..25f29a7 100644
--- a/core/java/android/view/WindowAnimationFrameStats.java
+++ b/core/java/android/view/WindowAnimationFrameStats.java
@@ -32,7 +32,12 @@
* #getRefreshPeriodNano()}. If the system does not render a frame every refresh
* period the user will see irregular window transitions. The time when the frame was
* actually presented on the display by calling {@link #getFramePresentedTimeNano(int)}.
+ *
+ * @deprecated Use Shared
+ * <a href="https://perfetto.dev/docs/data-sources/frametimeline">FrameTimeline</a>
+ * jank metrics instead.
*/
+@Deprecated
public final class WindowAnimationFrameStats extends FrameStats implements Parcelable {
/**
* @hide
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 141849f..b74b80e 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -20,6 +20,8 @@
import android.graphics.Point;
import android.graphics.Rect;
+import java.util.function.Supplier;
+
/**
* Metrics about a Window, consisting of the bounds and {@link WindowInsets}.
* <p>
@@ -50,8 +52,9 @@
public final class WindowMetrics {
@NonNull
private final Rect mBounds;
- @NonNull
- private final WindowInsets mWindowInsets;
+
+ private WindowInsets mWindowInsets;
+ private Supplier<WindowInsets> mWindowInsetsSupplier;
/** @see android.util.DisplayMetrics#density */
private final float mDensity;
@@ -81,6 +84,21 @@
}
/**
+ * Similar to {@link #WindowMetrics(Rect, WindowInsets, float)} but the window insets are
+ * computed when {@link #getWindowInsets()} is first time called. This reduces unnecessary
+ * calculation and the overhead of obtaining insets state from server side because most
+ * callers are usually only interested in {@link #getBounds()}.
+ *
+ * @hide
+ */
+ public WindowMetrics(@NonNull Rect bounds, @NonNull Supplier<WindowInsets> windowInsetsSupplier,
+ float density) {
+ mBounds = bounds;
+ mWindowInsetsSupplier = windowInsetsSupplier;
+ mDensity = density;
+ }
+
+ /**
* Returns the bounds of the area associated with this window or
* {@link android.annotation.UiContext}.
* <p>
@@ -121,7 +139,10 @@
*/
@NonNull
public WindowInsets getWindowInsets() {
- return mWindowInsets;
+ if (mWindowInsets != null) {
+ return mWindowInsets;
+ }
+ return mWindowInsets = mWindowInsetsSupplier.get();
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 229cc02..ec1badb 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -22,6 +22,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -38,6 +39,7 @@
import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Printer;
import android.util.Slog;
@@ -71,6 +73,16 @@
* @attr ref android.R.styleable#InputMethod_configChanges
*/
public final class InputMethodInfo implements Parcelable {
+
+ /**
+ * {@link Intent#getAction() Intent action} for IME that
+ * {@link #supportsStylusHandwriting() supports stylus handwriting}.
+ *
+ * @see #createStylusHandwritingSettingsActivityIntent().
+ */
+ public static final String ACTION_STYLUS_HANDWRITING_SETTINGS =
+ "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
+
static final String TAG = "InputMethodInfo";
/**
@@ -152,6 +164,11 @@
*/
private final boolean mSupportsStylusHandwriting;
+ /**
+ * The stylus handwriting setting activity's name, used by the system settings to
+ * launch the stylus handwriting specific setting activity of this input method.
+ */
+ private final String mStylusHandwritingSettingsActivityAttr;
/**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
@@ -203,6 +220,7 @@
PackageManager pm = context.getPackageManager();
String settingsActivityComponent = null;
+ String stylusHandwritingSettingsActivity = null;
boolean isVrOnly;
int isDefaultResId = 0;
@@ -253,6 +271,8 @@
com.android.internal.R.styleable.InputMethod_configChanges, 0);
mSupportsStylusHandwriting = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
+ stylusHandwritingSettingsActivity = sa.getString(
+ com.android.internal.R.styleable.InputMethod_stylusHandwritingSettingsActivity);
sa.recycle();
final int depth = parser.getDepth();
@@ -328,6 +348,7 @@
}
mSubtypes = new InputMethodSubtypeArray(subtypes);
mSettingsActivityName = settingsActivityComponent;
+ mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivity;
mIsDefaultResId = isDefaultResId;
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
@@ -359,6 +380,7 @@
mHandledConfigChanges = source.mHandledConfigChanges;
mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
mForceDefault = source.mForceDefault;
+ mStylusHandwritingSettingsActivityAttr = source.mStylusHandwritingSettingsActivityAttr;
}
InputMethodInfo(Parcel source) {
@@ -376,6 +398,7 @@
mSubtypes = new InputMethodSubtypeArray(source);
mHandledConfigChanges = source.readInt();
mSupportsStylusHandwriting = source.readBoolean();
+ mStylusHandwritingSettingsActivityAttr = source.readString8();
mForceDefault = false;
}
@@ -389,10 +412,28 @@
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+ null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
/**
+ * Test API for creating a built-in input method to verify stylus handwriting.
+ * @hide
+ */
+ @TestApi
+ public InputMethodInfo(@NonNull String packageName, @NonNull String className,
+ @NonNull CharSequence label, @NonNull String settingsActivity,
+ boolean supportStylusHandwriting,
+ @NonNull String stylusHandwritingSettingsActivityAttr) {
+ this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
+ settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+ false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
+ 0 /* handledConfigChanges */, supportStylusHandwriting,
+ stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
+ }
+
+ /**
* Temporary API for creating a built-in input method for test.
* @hide
*/
@@ -405,6 +446,7 @@
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
false /* supportsStylusHandwriting */,
+ null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
@@ -419,6 +461,7 @@
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
false /* isVrOnly */, 0 /* handledconfigChanges */,
false /* supportsStylusHandwriting */,
+ null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
@@ -432,6 +475,7 @@
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+ null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
@@ -443,6 +487,7 @@
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting,
+ String stylusHandwritingSettingsActivityAttr,
boolean supportsInlineSuggestionsWithTouchExploration) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
@@ -461,6 +506,7 @@
mIsVrOnly = isVrOnly;
mHandledConfigChanges = handledConfigChanges;
mSupportsStylusHandwriting = supportsStylusHandwriting;
+ mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivityAttr;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -550,6 +596,7 @@
*
* <p>A null will be returned if there is no settings activity associated
* with the input method.</p>
+ * @see #createStylusHandwritingSettingsActivityIntent()
*/
public String getSettingsActivity() {
return mSettingsActivityName;
@@ -622,11 +669,41 @@
/**
* Returns if IME supports handwriting using stylus input.
* @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
+ * @see #createStylusHandwritingSettingsActivityIntent()
*/
public boolean supportsStylusHandwriting() {
return mSupportsStylusHandwriting;
}
+ /**
+ * Returns {@link Intent} for stylus handwriting settings activity with
+ * {@link Intent#getAction() Intent action} {@link #ACTION_STYLUS_HANDWRITING_SETTINGS}
+ * if IME {@link #supportsStylusHandwriting() supports stylus handwriting}, else
+ * <code>null</code> if there are no associated settings for stylus handwriting / handwriting
+ * is not supported or if
+ * {@link android.R.styleable#InputMethod_stylusHandwritingSettingsActivity} is not defined.
+ *
+ * <p>To launch stylus settings, use this method to get the {@link android.content.Intent} to
+ * launch the stylus handwriting settings activity.</p>
+ * <p>e.g.<pre><code>startActivity(createStylusHandwritingSettingsActivityIntent());</code>
+ * </pre></p>
+ *
+ * @attr ref R.styleable#InputMethod_stylusHandwritingSettingsActivity
+ * @see #getSettingsActivity()
+ * @see #supportsStylusHandwriting()
+ */
+ @Nullable
+ public Intent createStylusHandwritingSettingsActivityIntent() {
+ if (TextUtils.isEmpty(mStylusHandwritingSettingsActivityAttr)
+ || !mSupportsStylusHandwriting) {
+ return null;
+ }
+ // TODO(b/210039666): consider returning null if component is not enabled.
+ return new Intent(ACTION_STYLUS_HANDWRITING_SETTINGS).setComponent(
+ new ComponentName(getServiceInfo().packageName,
+ mStylusHandwritingSettingsActivityAttr));
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
@@ -637,7 +714,9 @@
+ mSupportsInlineSuggestionsWithTouchExploration
+ " mSuppressesSpellChecker=" + mSuppressesSpellChecker
+ " mShowInInputMethodPicker=" + mShowInInputMethodPicker
- + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
+ + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting
+ + " mStylusHandwritingSettingsActivityAttr="
+ + mStylusHandwritingSettingsActivityAttr);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -752,6 +831,7 @@
mSubtypes.writeToParcel(dest);
dest.writeInt(mHandledConfigChanges);
dest.writeBoolean(mSupportsStylusHandwriting);
+ dest.writeString8(mStylusHandwritingSettingsActivityAttr);
}
/**
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 06449d5..11bd47d 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -41,6 +41,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
/**
* A controller to handle {@link android.view.WindowMetrics} related APIs, which are
@@ -53,6 +54,9 @@
* @hide
*/
public final class WindowMetricsController {
+ // TODO(b/151908239): Remove and always enable this if it is stable.
+ private static final boolean LAZY_WINDOW_INSETS = android.os.SystemProperties.getBoolean(
+ "persist.wm.debug.win_metrics_lazy_insets", false);
private final Context mContext;
public WindowMetricsController(@NonNull Context context) {
@@ -92,16 +96,11 @@
windowingMode = winConfig.getWindowingMode();
}
final IBinder token = Context.getToken(mContext);
- final WindowInsets windowInsets = getWindowInsetsFromServerForCurrentDisplay(token,
- bounds, isScreenRound, windowingMode);
- return new WindowMetrics(bounds, windowInsets, density);
- }
-
- private WindowInsets getWindowInsetsFromServerForCurrentDisplay(
- IBinder token, Rect bounds, boolean isScreenRound,
- @WindowConfiguration.WindowingMode int windowingMode) {
- return getWindowInsetsFromServerForDisplay(mContext.getDisplayId(), token, bounds,
- isScreenRound, windowingMode);
+ final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay(
+ mContext.getDisplayId(), token, bounds, isScreenRound, windowingMode);
+ return LAZY_WINDOW_INSETS
+ ? new WindowMetrics(new Rect(bounds), insetsSupplier, density)
+ : new WindowMetrics(new Rect(bounds), insetsSupplier.get(), density);
}
/**
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a704eb3..1c85ca2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -53,7 +53,7 @@
boolean showImeSwitcher);
void setWindowState(int display, int window, int state);
- void showRecentApps(boolean triggeredFromAltTab, boolean forward);
+ void showRecentApps(boolean triggeredFromAltTab);
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecentApps();
void toggleSplitScreen();
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 22d741c..826624a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3765,6 +3765,9 @@
{@link android.inputmethodservice.InputMethodService#onFinishInput()}.
-->
<attr name="supportsStylusHandwriting" format="boolean" />
+ <!-- Class name of an activity that allows the user to modify the stylus handwriting
+ settings for this service -->
+ <attr name="stylusHandwritingSettingsActivity" format="string" />
</declare-styleable>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index f4b49e6..58b8601 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -128,6 +128,7 @@
<public name="isCredential"/>
<public name="searchResultHighlightColor" />
<public name="focusedSearchResultHighlightColor" />
+ <public name="stylusHandwritingSettingsActivity" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index e7d7d640..7bef55e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -62,6 +62,8 @@
assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
assertThat(imi.isInlineSuggestionsEnabled(), is(false));
assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
+ assertThat(imi.supportsStylusHandwriting(), is(false));
+ assertThat(imi.createStylusHandwritingSettingsActivityIntent(), equalTo(null));
}
@Test
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 89d6304..f815a5e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -992,6 +992,15 @@
}
/**
+ * Notifies the hardware renderer about upcoming expensive frames.
+ *
+ * @hide
+ */
+ public void notifyExpensiveFrame() {
+ nNotifyExpensiveFrame(mNativeProxy);
+ }
+
+ /**
* b/68769804, b/66945974: For low FPS experiments.
*
* @hide
@@ -1553,4 +1562,6 @@
private static native void nSetRtAnimationsEnabled(boolean rtAnimationsEnabled);
private static native void nNotifyCallbackPending(long nativeProxy);
+
+ private static native void nNotifyExpensiveFrame(long nativeProxy);
}
diff --git a/graphics/java/android/graphics/drawable/LottieDrawable.java b/graphics/java/android/graphics/drawable/LottieDrawable.java
deleted file mode 100644
index c1f1b50..0000000
--- a/graphics/java/android/graphics/drawable/LottieDrawable.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-
-import dalvik.annotation.optimization.FastNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-import java.io.IOException;
-
-/**
- * {@link Drawable} for drawing Lottie files.
- *
- * <p>The framework handles decoding subsequent frames in another thread and
- * updating when necessary. The drawable will only animate while it is being
- * displayed.</p>
- *
- * @hide
- */
-@SuppressLint("NotCloseable")
-public class LottieDrawable extends Drawable implements Animatable {
- private long mNativePtr;
-
- /**
- * Create an animation from the provided JSON string
- * @hide
- */
- private LottieDrawable(@NonNull String animationJson) throws IOException {
- mNativePtr = nCreate(animationJson);
- if (mNativePtr == 0) {
- throw new IOException("could not make LottieDrawable from json");
- }
-
- final long nativeSize = nNativeByteSize(mNativePtr);
- NativeAllocationRegistry registry = new NativeAllocationRegistry(
- LottieDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
- registry.registerNativeAllocation(this, mNativePtr);
- }
-
- /**
- * Factory for LottieDrawable, throws IOException if native Skottie Builder fails to parse
- */
- public static LottieDrawable makeLottieDrawable(@NonNull String animationJson)
- throws IOException {
- return new LottieDrawable(animationJson);
- }
-
-
-
- /**
- * Draw the current frame to the Canvas.
- * @hide
- */
- @Override
- public void draw(@NonNull Canvas canvas) {
- if (mNativePtr == 0) {
- throw new IllegalStateException("called draw on empty LottieDrawable");
- }
-
- nDraw(mNativePtr, canvas.getNativeCanvasWrapper());
- }
-
- /**
- * Start the animation. Needs to be called before draw calls.
- * @hide
- */
- @Override
- public void start() {
- if (mNativePtr == 0) {
- throw new IllegalStateException("called start on empty LottieDrawable");
- }
-
- if (nStart(mNativePtr)) {
- invalidateSelf();
- }
- }
-
- /**
- * Stops the animation playback. Does not release anything.
- * @hide
- */
- @Override
- public void stop() {
- if (mNativePtr == 0) {
- throw new IllegalStateException("called stop on empty LottieDrawable");
- }
- nStop(mNativePtr);
- }
-
- /**
- * Return whether the animation is currently running.
- */
- @Override
- public boolean isRunning() {
- if (mNativePtr == 0) {
- throw new IllegalStateException("called isRunning on empty LottieDrawable");
- }
- return nIsRunning(mNativePtr);
- }
-
- @Override
- public int getOpacity() {
- // We assume translucency to avoid checking each pixel.
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void setAlpha(int alpha) {
- //TODO
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
- //TODO
- }
-
- private static native long nCreate(String json);
- private static native void nDraw(long nativeInstance, long nativeCanvas);
- @FastNative
- private static native long nGetNativeFinalizer();
- @FastNative
- private static native long nNativeByteSize(long nativeInstance);
- @FastNative
- private static native boolean nIsRunning(long nativeInstance);
- @FastNative
- private static native boolean nStart(long nativeInstance);
- @FastNative
- private static native boolean nStop(long nativeInstance);
-
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index b9caf62..26f47fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -114,7 +114,9 @@
t.setPosition(leash, positionInParent.x, positionInParent.y);
t.setAlpha(leash, 1f);
t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
+ if (taskInfo.isVisible) {
+ t.show(leash);
+ }
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 38099fc..21eeaa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -535,17 +536,11 @@
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
- try {
- adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options2);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
+ taskId = INVALID_TASK_ID;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
- return;
}
}
mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
@@ -586,17 +581,11 @@
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
- try {
- adapter.getRunner().onAnimationCancelled(false /* isKeyguardOccluded */);
- pendingIntent1.send();
- } catch (RemoteException | PendingIntent.CanceledException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
+ pendingIntent2 = null;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
- return;
}
}
mStageCoordinator.startIntentsWithLegacyTransition(pendingIntent1, fillInIntent1, options1,
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 a6c4ac2..39cf5f1 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
@@ -629,9 +629,19 @@
RemoteAnimationAdapter adapter, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
+ if (pendingIntent2 == null) {
+ // Launching a solo task.
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+ options1 = activityOptions.toBundle();
+ addActivityOptions(options1, null /* launchTarget */);
+ wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+ mSyncQueue.queue(wct);
+ return;
+ }
+
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
-
startWithLegacyTransition(wct, pendingIntent2, fillInIntent2, options2, splitPosition,
splitRatio, adapter, instanceId);
}
@@ -666,9 +676,19 @@
InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
+ if (taskId == INVALID_TASK_ID) {
+ // Launching a solo task.
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+ options1 = activityOptions.toBundle();
+ addActivityOptions(options1, null /* launchTarget */);
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+ mSyncQueue.queue(wct);
+ return;
+ }
+
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
-
startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
instanceId);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 9112b1b..8d4bda2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -78,7 +78,6 @@
"external/skia/src/utils",
"external/skia/src/gpu",
"external/skia/src/shaders",
- "external/skia/modules/skottie",
],
},
host: {
@@ -388,7 +387,6 @@
"external/skia/src/effects",
"external/skia/src/image",
"external/skia/src/images",
- "external/skia/modules/skottie",
],
shared_libs: [
@@ -420,7 +418,6 @@
"jni/BitmapRegionDecoder.cpp",
"jni/GIFMovie.cpp",
"jni/GraphicsStatsService.cpp",
- "jni/LottieDrawable.cpp",
"jni/Movie.cpp",
"jni/MovieImpl.cpp",
"jni/pdf/PdfDocument.cpp",
@@ -528,7 +525,6 @@
"hwui/BlurDrawLooper.cpp",
"hwui/Canvas.cpp",
"hwui/ImageDecoder.cpp",
- "hwui/LottieDrawable.cpp",
"hwui/MinikinSkia.cpp",
"hwui/MinikinUtils.cpp",
"hwui/PaintImpl.cpp",
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1e0c359..d0124f5 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -740,10 +740,6 @@
return imgDrawable->drawStaging(mCanvas);
}
-void SkiaCanvas::drawLottie(LottieDrawable* lottieDrawable) {
- lottieDrawable->drawStaging(mCanvas);
-}
-
void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
vectorDrawable->drawStaging(this);
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 1524dff..f2c286a 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -144,7 +144,6 @@
float dstTop, float dstRight, float dstBottom,
const Paint* paint) override;
virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override;
- virtual void drawLottie(LottieDrawable* lottieDrawable) override;
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index b1aa1947..f57d80c 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -37,7 +37,6 @@
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_ImageDecoder(JNIEnv*);
extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
-extern int register_android_graphics_drawable_LottieDrawable(JNIEnv*);
extern int register_android_graphics_Interpolator(JNIEnv* env);
extern int register_android_graphics_MaskFilter(JNIEnv* env);
extern int register_android_graphics_Movie(JNIEnv* env);
@@ -118,7 +117,6 @@
REG_JNI(register_android_graphics_HardwareRendererObserver),
REG_JNI(register_android_graphics_ImageDecoder),
REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
- REG_JNI(register_android_graphics_drawable_LottieDrawable),
REG_JNI(register_android_graphics_Interpolator),
REG_JNI(register_android_graphics_MaskFilter),
REG_JNI(register_android_graphics_Matrix),
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 07e2fe2..2a20191 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -60,7 +60,6 @@
typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
class AnimatedImageDrawable;
-class LottieDrawable;
class Bitmap;
class Paint;
struct Typeface;
@@ -243,7 +242,6 @@
const Paint* paint) = 0;
virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0;
- virtual void drawLottie(LottieDrawable* lottieDrawable) = 0;
virtual void drawPicture(const SkPicture& picture) = 0;
/**
diff --git a/libs/hwui/hwui/LottieDrawable.cpp b/libs/hwui/hwui/LottieDrawable.cpp
deleted file mode 100644
index 92dc51e..0000000
--- a/libs/hwui/hwui/LottieDrawable.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LottieDrawable.h"
-
-#include <SkTime.h>
-#include <log/log.h>
-#include <pipeline/skia/SkiaUtils.h>
-
-namespace android {
-
-sk_sp<LottieDrawable> LottieDrawable::Make(sk_sp<skottie::Animation> animation, size_t bytesUsed) {
- if (animation) {
- return sk_sp<LottieDrawable>(new LottieDrawable(std::move(animation), bytesUsed));
- }
- return nullptr;
-}
-LottieDrawable::LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytesUsed)
- : mAnimation(std::move(animation)), mBytesUsed(bytesUsed) {}
-
-bool LottieDrawable::start() {
- if (mRunning) {
- return false;
- }
-
- mRunning = true;
- return true;
-}
-
-bool LottieDrawable::stop() {
- bool wasRunning = mRunning;
- mRunning = false;
- return wasRunning;
-}
-
-bool LottieDrawable::isRunning() {
- return mRunning;
-}
-
-// TODO: Check to see if drawable is actually dirty
-bool LottieDrawable::isDirty() {
- return true;
-}
-
-void LottieDrawable::onDraw(SkCanvas* canvas) {
- if (mRunning) {
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
- nsecs_t t = 0;
- if (mStartTime == 0) {
- mStartTime = currentTime;
- } else {
- t = currentTime - mStartTime;
- }
- double seekTime = std::fmod((double)t * 1e-9, mAnimation->duration());
- mAnimation->seekFrameTime(seekTime);
- mAnimation->render(canvas);
- }
-}
-
-void LottieDrawable::drawStaging(SkCanvas* canvas) {
- onDraw(canvas);
-}
-
-SkRect LottieDrawable::onGetBounds() {
- // We do not actually know the bounds, so give a conservative answer.
- return SkRectMakeLargest();
-}
-
-} // namespace android
diff --git a/libs/hwui/hwui/LottieDrawable.h b/libs/hwui/hwui/LottieDrawable.h
deleted file mode 100644
index 9cc34bf..0000000
--- a/libs/hwui/hwui/LottieDrawable.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <SkDrawable.h>
-#include <Skottie.h>
-#include <utils/Timers.h>
-
-class SkCanvas;
-
-namespace android {
-
-/**
- * Native component of android.graphics.drawable.LottieDrawable.java.
- * This class can be drawn into Canvas.h and maintains the state needed to drive
- * the animation from the RenderThread.
- */
-class LottieDrawable : public SkDrawable {
-public:
- static sk_sp<LottieDrawable> Make(sk_sp<skottie::Animation> animation, size_t bytes);
-
- // Draw to software canvas
- void drawStaging(SkCanvas* canvas);
-
- // Returns true if the animation was started; false otherwise (e.g. it was
- // already running)
- bool start();
- // Returns true if the animation was stopped; false otherwise (e.g. it was
- // already stopped)
- bool stop();
- bool isRunning();
-
- // TODO: Is dirty should take in a time til next frame to determine if it is dirty
- bool isDirty();
-
- SkRect onGetBounds() override;
-
- size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
-
-protected:
- void onDraw(SkCanvas* canvas) override;
-
-private:
- LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytes_used);
-
- sk_sp<skottie::Animation> mAnimation;
- bool mRunning = false;
- // The start time for the drawable itself.
- nsecs_t mStartTime = 0;
- const size_t mBytesUsed = 0;
-};
-
-} // namespace android
diff --git a/libs/hwui/jni/LottieDrawable.cpp b/libs/hwui/jni/LottieDrawable.cpp
deleted file mode 100644
index fb6eede..0000000
--- a/libs/hwui/jni/LottieDrawable.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkRect.h>
-#include <Skottie.h>
-#include <hwui/Canvas.h>
-#include <hwui/LottieDrawable.h>
-
-#include "GraphicsJNI.h"
-#include "Utils.h"
-
-using namespace android;
-
-static jclass gLottieDrawableClass;
-
-static jlong LottieDrawable_nCreate(JNIEnv* env, jobject, jstring jjson) {
- const ScopedUtfChars cstr(env, jjson);
- // TODO(b/259267150) provide more accurate byteSize
- size_t bytes = strlen(cstr.c_str());
- auto animation = skottie::Animation::Builder().make(cstr.c_str(), bytes);
- sk_sp<LottieDrawable> drawable(LottieDrawable::Make(std::move(animation), bytes));
- if (!drawable) {
- return 0;
- }
- return reinterpret_cast<jlong>(drawable.release());
-}
-
-static void LottieDrawable_destruct(LottieDrawable* drawable) {
- SkSafeUnref(drawable);
-}
-
-static jlong LottieDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
- return static_cast<jlong>(reinterpret_cast<uintptr_t>(&LottieDrawable_destruct));
-}
-
-static void LottieDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jlong canvasPtr) {
- auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
- auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
- canvas->drawLottie(drawable);
-}
-
-static jboolean LottieDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
- auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
- return drawable->isRunning();
-}
-
-static jboolean LottieDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
- auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
- return drawable->start();
-}
-
-static jboolean LottieDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
- auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
- return drawable->stop();
-}
-
-static jlong LottieDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
- auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
- return drawable->byteSize();
-}
-
-static const JNINativeMethod gLottieDrawableMethods[] = {
- {"nCreate", "(Ljava/lang/String;)J", (void*)LottieDrawable_nCreate},
- {"nNativeByteSize", "(J)J", (void*)LottieDrawable_nNativeByteSize},
- {"nGetNativeFinalizer", "()J", (void*)LottieDrawable_nGetNativeFinalizer},
- {"nDraw", "(JJ)V", (void*)LottieDrawable_nDraw},
- {"nIsRunning", "(J)Z", (void*)LottieDrawable_nIsRunning},
- {"nStart", "(J)Z", (void*)LottieDrawable_nStart},
- {"nStop", "(J)Z", (void*)LottieDrawable_nStop},
-};
-
-int register_android_graphics_drawable_LottieDrawable(JNIEnv* env) {
- gLottieDrawableClass = reinterpret_cast<jclass>(
- env->NewGlobalRef(FindClassOrDie(env, "android/graphics/drawable/LottieDrawable")));
-
- return android::RegisterMethodsOrDie(env, "android/graphics/drawable/LottieDrawable",
- gLottieDrawableMethods, NELEM(gLottieDrawableMethods));
-}
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 3f4d004..5892308 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -822,6 +822,11 @@
proxy->notifyCallbackPending();
}
+static void android_view_ThreadedRenderer_notifyExpensiveFrame(JNIEnv*, jclass, jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ proxy->notifyExpensiveFrame();
+}
+
// Plumbs the display density down to DeviceInfo.
static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass, jint densityDpi) {
// Convert from dpi to density-independent pixels.
@@ -1000,6 +1005,8 @@
(void*)android_view_ThreadedRenderer_setRtAnimationsEnabled},
{"nNotifyCallbackPending", "(J)V",
(void*)android_view_ThreadedRenderer_notifyCallbackPending},
+ {"nNotifyExpensiveFrame", "(J)V",
+ (void*)android_view_ThreadedRenderer_notifyExpensiveFrame},
};
static JavaVM* mJvm = nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index f0dc5eb..fcfc4f8 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -146,16 +146,6 @@
}
}
- for (auto& lottie : mLotties) {
- // If any animated image in the display list needs updated, then damage the node.
- if (lottie->isDirty()) {
- isDirty = true;
- }
- if (lottie->isRunning()) {
- info.out.hasAnimations = true;
- }
- }
-
for (auto& [vectorDrawable, cachedMatrix] : mVectorDrawables) {
// If any vector drawable in the display list needs update, damage the node.
if (vectorDrawable->isDirty()) {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 39217fc..2a67734 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -22,7 +22,6 @@
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
#include "hwui/AnimatedImageDrawable.h"
-#include "hwui/LottieDrawable.h"
#include "utils/LinearAllocator.h"
#include "utils/Pair.h"
@@ -187,8 +186,6 @@
return mHasHolePunches;
}
- // TODO(b/257304231): create common base class for Lotties and AnimatedImages
- std::vector<LottieDrawable*> mLotties;
std::vector<AnimatedImageDrawable*> mAnimatedImages;
DisplayListData mDisplayList;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 08f0291..c9d79ab 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -189,11 +189,6 @@
#endif
}
-void SkiaRecordingCanvas::drawLottie(LottieDrawable* lottie) {
- drawDrawable(lottie);
- mDisplayList->mLotties.push_back(lottie);
-}
-
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
mRecorder.drawVectorDrawable(tree);
SkMatrix mat;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index c823d8d..7844e2c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -78,7 +78,6 @@
uirenderer::CanvasPropertyPaint* paint) override;
virtual void drawRipple(const RippleDrawableParams& params) override;
- virtual void drawLottie(LottieDrawable* lottieDrawable) override;
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
virtual void enableZ(bool enableZ) override;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 1c76884..23611ef 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -19,7 +19,6 @@
#include <GrContextOptions.h>
#include <SkExecutor.h>
#include <SkGraphics.h>
-#include <SkMathPriv.h>
#include <math.h>
#include <utils/Trace.h>
@@ -47,13 +46,23 @@
setupCacheLimits();
}
+static inline int countLeadingZeros(uint32_t mask) {
+ // __builtin_clz(0) is undefined, so we have to detect that case.
+ return mask ? __builtin_clz(mask) : 32;
+}
+
+// Return the smallest power-of-2 >= n.
+static inline uint32_t nextPowerOfTwo(uint32_t n) {
+ return n ? (1 << (32 - countLeadingZeros(n - 1))) : 1;
+}
+
void CacheManager::setupCacheLimits() {
mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
// This sets the maximum size for a single texture atlas in the GPU font cache. If
// necessary, the cache can allocate additional textures that are counted against the
// total cache limits provided to Skia.
- mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+ mMaxGpuFontAtlasBytes = nextPowerOfTwo(mMaxSurfaceArea);
// This sets the maximum size of the CPU font cache to be at least the same size as the
// total number of GPU font caches (i.e. 4 separate GPU atlases).
mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b769f8d..f223137 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -1021,6 +1021,10 @@
mHintSessionWrapper.sendLoadResetHint();
}
+void CanvasContext::sendLoadIncreaseHint() {
+ mHintSessionWrapper.sendLoadIncreaseHint();
+}
+
void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
mSyncDelayDuration = duration;
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 3f796d9..a274d2f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -223,6 +223,8 @@
void sendLoadResetHint();
+ void sendLoadIncreaseHint();
+
void setSyncDelayDuration(nsecs_t duration);
private:
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 94c9d94..dece548 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -158,6 +158,11 @@
mLastFrameNotification = now;
}
+void HintSessionWrapper::sendLoadIncreaseHint() {
+ if (!useHintSession()) return;
+ gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP));
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index fcbc101..c0f7a57 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -33,6 +33,7 @@
void updateTargetWorkDuration(long targetDurationNanos);
void reportActualWorkDuration(long actualDurationNanos);
void sendLoadResetHint();
+ void sendLoadIncreaseHint();
private:
bool useHintSession();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ed01e32..5edb0b1 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -254,6 +254,10 @@
mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
}
+void RenderProxy::notifyExpensiveFrame() {
+ mRenderThread.queue().post([this]() { mContext->sendLoadIncreaseHint(); });
+}
+
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
mRenderThread.queue().runSync([&]() {
std::lock_guard lock(mRenderThread.getJankDataMutex());
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 17cf665..2aafe76 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -112,6 +112,7 @@
void stopDrawing();
void notifyFramePending();
void notifyCallbackPending();
+ void notifyExpensiveFrame();
void dumpProfileInfo(int fd, int dumpFlags);
// Not exported, only used for testing
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 27c2a98..ac920d2 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1170,6 +1170,11 @@
* in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
* TunerVersionChecker#getTunerVersion()} to get the version information.
*
+ * <p>Tuning with {@link
+ * android.media.tv.tuner.frontend.IptvFrontendSettings} is only supported
+ * in Tuner 3.0 or higher version. Unsupported version will cause no-op. Use {@link
+ * TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
* @param settings Signal delivery information the frontend uses to
* search and lock the signal.
* @return result status of tune operation.
@@ -1198,6 +1203,12 @@
return RESULT_UNAVAILABLE;
}
}
+ if (mFrontendType == FrontendSettings.TYPE_IPTV) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "Tuner with IPTV Frontend")) {
+ return RESULT_UNAVAILABLE;
+ }
+ }
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
mFrontendInfo = null;
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 2f45a70..0a1ecee 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -38,7 +38,7 @@
/** @hide */
@IntDef(prefix = "TYPE_",
value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS,
- TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB})
+ TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB, TYPE_IPTV})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -86,7 +86,10 @@
* Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type.
*/
public static final int TYPE_DTMB = FrontendType.DTMB;
-
+ /**
+ * Internet Protocol (IPTV) frontend type.
+ */
+ public static final int TYPE_IPTV = FrontendType.IPTV;
/** @hide */
@LongDef(prefix = "FEC_",
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 9fbea72..fd677ac 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -23,6 +23,7 @@
import android.annotation.SystemApi;
import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerVersionChecker;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -57,7 +58,10 @@
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_IDS,
- FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO})
+ FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO,
+ FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST,
+ FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS,
+ FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -271,6 +275,36 @@
android.hardware.tv.tuner.FrontendStatusType.DVBT_CELL_IDS;
/**
+ * IPTV content URL.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_CONTENT_URL;
+
+ /**
+ * IPTV packets lost.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_PACKETS_LOST;
+
+ /**
+ * IPTV packets received.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_PACKETS_RECEIVED;
+
+ /**
+ * IPTV worst jitter.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_WORST_JITTER_MS;
+
+ /**
+ * IPTV average jitter.
+ */
+ public static final int FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS =
+ android.hardware.tv.tuner.FrontendStatusType.IPTV_AVERAGE_JITTER_MS;
+
+ /**
* All PLP information in a frequency band for ATSC-3.0 frontend, which includes both tuned and
* not tuned PLPs for currently watching service.
*/
@@ -519,6 +553,11 @@
private int[] mStreamIds;
private int[] mDvbtCellIds;
private Atsc3PlpInfo[] mAllPlpInfo;
+ private String mIptvContentUrl;
+ private Long mIptvPacketsLost;
+ private Long mIptvPacketsReceived;
+ private Integer mIptvWorstJitterMs;
+ private Integer mIptvAverageJitterMs;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1144,4 +1183,94 @@
return mUec;
}
}
+
+ /**
+ * Gets the IPTV content URL.
+ *
+ * @return A String URL in the format protocol://ip:port (udp://127.0.0.1:3000).
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @NonNull
+ public String getIptvContentUrl() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvContentUrl status");
+ if (mIptvContentUrl == null) {
+ throw new IllegalStateException("IptvContentUrl status is empty");
+ }
+ return mIptvContentUrl;
+ }
+
+ /**
+ * Gets the number of packets lost.
+ *
+ * @return A long value representing the number of packets lost in transmission.
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public long getIptvPacketsLost() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvPacketsLost status");
+ if (mIptvPacketsLost == null) {
+ throw new IllegalStateException("IptvPacketsLost status is empty");
+ }
+ return mIptvPacketsLost;
+ }
+
+ /**
+ * Gets the number of packets received.
+ *
+ * @return A long value representing the number of packets received.
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public long getIptvPacketsReceived() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvPacketsReceived status");
+ if (mIptvPacketsReceived == null) {
+ throw new IllegalStateException("IptvPacketsReceived status is empty");
+ }
+ return mIptvPacketsReceived;
+ }
+
+ /**
+ * Gets the worst jitter.
+ *
+ * @return An integer representing worst jitter recorded (in milliseconds).
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public int getIptvWorstJitterMillis() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvWorstJitterMs status");
+ if (mIptvWorstJitterMs == null) {
+ throw new IllegalStateException("IptvWorstJitterMs status is empty");
+ }
+ return mIptvWorstJitterMs;
+ }
+
+ /**
+ * Gets the average jitter.
+ *
+ * @return An integer representing average jitter recorded (in milliseconds).
+ *
+ * <p>This query is only supported by Tuner HAL 3.0 or higher. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IntRange(from = 0)
+ public int getIptvAverageJitterMillis() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0, "IptvAverageJitterMs status");
+ if (mIptvAverageJitterMs == null) {
+ throw new IllegalStateException("IptvAverageJitterMs status is empty");
+ }
+ return mIptvAverageJitterMs;
+ }
}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
new file mode 100644
index 0000000..fb11ea9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 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.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendIptvSettingsIgmp;
+import android.hardware.tv.tuner.FrontendIptvSettingsProtocol;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Frontend settings for IPTV.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendSettings extends FrontendSettings {
+ /** @hide */
+ @IntDef(prefix = "PROTOCOL_",
+ value = {PROTOCOL_UNDEFINED, PROTOCOL_UDP, PROTOCOL_RTP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protocol {}
+
+ /**
+ * IP protocol type UNDEFINED.
+ */
+ public static final int PROTOCOL_UNDEFINED = FrontendIptvSettingsProtocol.UNDEFINED;
+
+ /**
+ * IP protocol type UDP (User Datagram Protocol).
+ */
+ public static final int PROTOCOL_UDP = FrontendIptvSettingsProtocol.UDP;
+
+ /**
+ * IP protocol type RTP (Real-time Transport Protocol).
+ */
+ public static final int PROTOCOL_RTP = FrontendIptvSettingsProtocol.RTP;
+
+ /** @hide */
+ @IntDef(prefix = "IGMP_",
+ value = {IGMP_UNDEFINED, IGMP_V1, IGMP_V2, IGMP_V3})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Igmp {}
+
+ /**
+ * IGMP (Internet Group Management Protocol) UNDEFINED.
+ */
+ public static final int IGMP_UNDEFINED = FrontendIptvSettingsIgmp.UNDEFINED;
+
+ /**
+ * IGMP (Internet Group Management Protocol) V1.
+ */
+ public static final int IGMP_V1 = FrontendIptvSettingsIgmp.V1;
+
+ /**
+ * IGMP (Internet Group Management Protocol) V2.
+ */
+ public static final int IGMP_V2 = FrontendIptvSettingsIgmp.V2;
+
+ /**
+ * IGMP (Internet Group Management Protocol) V3.
+ */
+ public static final int IGMP_V3 = FrontendIptvSettingsIgmp.V3;
+
+ private final byte[] mSrcIpAddress;
+ private final byte[] mDstIpAddress;
+ private final int mSrcPort;
+ private final int mDstPort;
+ private final IptvFrontendSettingsFec mFec;
+ private final int mProtocol;
+ private final int mIgmp;
+ private final long mBitrate;
+ private final String mContentUrl;
+
+ public IptvFrontendSettings(@NonNull byte[] srcIpAddress, @NonNull byte[] dstIpAddress,
+ int srcPort, int dstPort, @NonNull IptvFrontendSettingsFec fec, int protocol, int igmp,
+ long bitrate, @NonNull String contentUrl) {
+ super(0);
+ mSrcIpAddress = srcIpAddress;
+ mDstIpAddress = dstIpAddress;
+ mSrcPort = srcPort;
+ mDstPort = dstPort;
+ mFec = fec;
+ mProtocol = protocol;
+ mIgmp = igmp;
+ mBitrate = bitrate;
+ mContentUrl = contentUrl;
+ }
+
+ /**
+ * Gets the source IP address.
+ */
+ @Size(min = 4, max = 16)
+ @NonNull
+ public byte[] getSrcIpAddress() {
+ return mSrcIpAddress;
+ }
+
+ /**
+ * Gets the destination IP address.
+ */
+ @Size(min = 4, max = 16)
+ @NonNull
+ public byte[] getDstIpAddress() {
+ return mDstIpAddress;
+ }
+
+ /**
+ * Gets the source port.
+ */
+ public int getSrcPort() {
+ return mSrcPort;
+ }
+
+ /**
+ * Gets the destination port.
+ */
+ public int getDstPort() {
+ return mDstPort;
+ }
+
+ /**
+ * Gets FEC (Forward Error Correction).
+ */
+ @Nullable
+ public IptvFrontendSettingsFec getFec() {
+ return mFec;
+ }
+
+ /**
+ * Gets the protocol.
+ */
+ @Protocol
+ public int getProtocol() {
+ return mProtocol;
+ }
+
+ /**
+ * Gets the IGMP (Internet Group Management Protocol).
+ */
+ @Igmp
+ public int getIgmp() {
+ return mIgmp;
+ }
+
+ /**
+ * Gets the bitrate.
+ */
+ @IntRange(from = 0)
+ public long getBitrate() {
+ return mBitrate;
+ }
+
+ /**
+ * Gets the contentUrl
+ * contentUrl is a source URL in the format protocol://ip:port containing data
+ */
+ @NonNull
+ public String getContentUrl() {
+ return mContentUrl;
+ }
+
+ /**
+ * Creates a builder for {@link IptvFrontendSettings}.
+ */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IptvFrontendSettings}.
+ */
+ public static final class Builder {
+ private byte[] mSrcIpAddress = {0, 0, 0, 0};
+ private byte[] mDstIpAddress = {0, 0, 0, 0};
+ private int mSrcPort = 0;
+ private int mDstPort = 0;
+ private IptvFrontendSettingsFec mFec = null;
+ private int mProtocol = FrontendIptvSettingsProtocol.UNDEFINED;
+ private int mIgmp = FrontendIptvSettingsIgmp.UNDEFINED;
+ private long mBitrate = 0;
+ private String mContentUrl = "";
+
+ private Builder() {
+ }
+
+ /**
+ * Sets the source IP address.
+ *
+ * <p>Default value is 0.0.0.0, an invalid IP address.
+ */
+ @NonNull
+ public Builder setSrcIpAddress(@NonNull byte[] srcIpAddress) {
+ mSrcIpAddress = srcIpAddress;
+ return this;
+ }
+
+ /**
+ * Sets the destination IP address.
+ *
+ * <p>Default value is 0.0.0.0, an invalid IP address.
+ */
+ @NonNull
+ public Builder setDstIpAddress(@NonNull byte[] dstIpAddress) {
+ mDstIpAddress = dstIpAddress;
+ return this;
+ }
+
+ /**
+ * Sets the source IP port.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ public Builder setSrcPort(int srcPort) {
+ mSrcPort = srcPort;
+ return this;
+ }
+
+ /**
+ * Sets the destination IP port.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ public Builder setDstPort(int dstPort) {
+ mDstPort = dstPort;
+ return this;
+ }
+
+ /**
+ * Sets the FEC (Forward Error Correction).
+ *
+ * <p>Default value is {@code null}.
+ */
+ @NonNull
+ public Builder setFec(@Nullable IptvFrontendSettingsFec fec) {
+ mFec = fec;
+ return this;
+ }
+
+ /**
+ * Sets the protocol.
+ *
+ * <p>Default value is {@link #PROTOCOL_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setProtocol(@Protocol int protocol) {
+ mProtocol = protocol;
+ return this;
+ }
+
+ /**
+ * Sets the IGMP (Internet Group Management Protocol).
+ *
+ * <p>Default value is {@link #IGMP_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setIgmp(@Igmp int igmp) {
+ mIgmp = igmp;
+ return this;
+ }
+
+ /**
+ * Sets the bitrate.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ public Builder setBitrate(@IntRange(from = 0) long bitrate) {
+ mBitrate = bitrate;
+ return this;
+ }
+
+ /**
+ * Sets the contentUrl.
+ *
+ * <p>Default value is "".
+ */
+ @NonNull
+ public Builder setContentUrl(@NonNull String contentUrl) {
+ mContentUrl = contentUrl;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IptvFrontendSettings} object.
+ */
+ @NonNull
+ public IptvFrontendSettings build() {
+ return new IptvFrontendSettings(mSrcIpAddress, mDstIpAddress, mSrcPort,
+ mDstPort, mFec, mProtocol, mIgmp, mBitrate, mContentUrl);
+ }
+ }
+
+ @Override
+ public int getType() {
+ return FrontendSettings.TYPE_IPTV;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
new file mode 100644
index 0000000..699d615
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 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.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendIptvSettingsFecType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * FEC (Forward Error Correction) for IPTV.
+ *
+ * @hide
+ */
+@SystemApi
+public class IptvFrontendSettingsFec {
+ /** @hide */
+ @IntDef(prefix = "FEC_TYPE_",
+ value = {FEC_TYPE_UNDEFINED, FEC_TYPE_COLUMN, FEC_TYPE_ROW, FEC_TYPE_COLUMN_ROW})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FecType {}
+
+ /**
+ * FEC (Forward Error Correction) type UNDEFINED.
+ */
+ public static final int FEC_TYPE_UNDEFINED = FrontendIptvSettingsFecType.UNDEFINED;
+
+ /**
+ * FEC (Forward Error Correction) type Column.
+ */
+ public static final int FEC_TYPE_COLUMN = FrontendIptvSettingsFecType.COLUMN;
+
+ /**
+ * FEC (Forward Error Correction) type ROW.
+ */
+ public static final int FEC_TYPE_ROW = FrontendIptvSettingsFecType.ROW;
+
+ /**
+ * FEC (Forward Error Correction) type Column Row.
+ */
+ public static final int FEC_TYPE_COLUMN_ROW = FrontendIptvSettingsFecType.COLUMN_ROW;
+
+ private final int mFecType;
+ private final int mFecRowNum;
+ private final int mFecColNum;
+
+ public IptvFrontendSettingsFec(@FecType int fecType, int fecRowNum, int fecColNum) {
+ mFecType = fecType;
+ mFecRowNum = fecRowNum;
+ mFecColNum = fecColNum;
+ }
+
+ /**
+ * Gets the FEC (Forward Error Correction) type.
+ */
+ @FecType
+ public int getFecType() {
+ return mFecType;
+ }
+
+ /**
+ * Get the FEC (Forward Error Correction) row number.
+ */
+ @IntRange(from = 0)
+ public int getFecRowNum() {
+ return mFecRowNum;
+ }
+
+ /**
+ * Gets the FEC (Forward Error Correction) column number.
+ */
+ @IntRange(from = 0)
+ public int getFecColNum() {
+ return mFecColNum;
+ }
+
+ /**
+ * Creates a builder for {@link IptvFrontendSettingsFec}.
+ */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IptvFrontendSettingsFec}.
+ */
+ public static final class Builder {
+ private int mFecType;
+ private int mFecRowNum;
+ private int mFecColNum;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets the FEC (Forward Error Correction) type
+ */
+ @NonNull
+ public Builder setFecType(@FecType int fecType) {
+ mFecType = fecType;
+ return this;
+ }
+ /**
+ * Sets the FEC (Forward Error Correction) row number.
+ */
+ @NonNull
+ public Builder setFecRowNum(@IntRange(from = 0) int fecRowNum) {
+ mFecRowNum = fecRowNum;
+ return this;
+ }
+ /**
+ * Sets the FEC (Forward Error Correction) column number.
+ */
+ @NonNull
+ public Builder setFecColNum(@IntRange(from = 0) int fecColNum) {
+ mFecColNum = fecColNum;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IptvFrontendSettingsFec} object.
+ */
+ @NonNull
+ public IptvFrontendSettingsFec build() {
+ return new IptvFrontendSettingsFec(mFecType, mFecRowNum, mFecColNum);
+ }
+ }
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a73725b..35ee3ee9 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -490,9 +490,11 @@
}
void MediaEvent::finalize() {
- if (mAvHandleRefCnt == 0 && mFilterClient != nullptr) {
- mFilterClient->releaseAvHandle(
- mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0);
+ if (mAvHandleRefCnt == 0) {
+ if (mFilterClient != nullptr) {
+ mFilterClient->releaseAvHandle(
+ mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0);
+ }
native_handle_close(mAvHandle);
}
}
@@ -2825,6 +2827,52 @@
env->DeleteLocalRef(plpClazz);
break;
}
+ case FrontendStatus::Tag::iptvContentUrl: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvContentUrl", "Ljava/lang/String;");
+ std::string iptvContentUrl = s.get<FrontendStatus::Tag::iptvContentUrl>();
+ jstring iptvContentUrlUtf8 = env->NewStringUTF(iptvContentUrl.c_str());
+ env->SetObjectField(statusObj, field, iptvContentUrlUtf8);
+ env->DeleteLocalRef(iptvContentUrlUtf8);
+ break;
+ }
+ case FrontendStatus::Tag::iptvPacketsLost: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvPacketsLost", "Ljava/lang/Long;");
+ jobject newLongObj =
+ env->NewObject(longClazz, initLong,
+ s.get<FrontendStatus::Tag::iptvPacketsLost>());
+ env->SetObjectField(statusObj, field, newLongObj);
+ env->DeleteLocalRef(newLongObj);
+ break;
+ }
+ case FrontendStatus::Tag::iptvPacketsReceived: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvPacketsReceived", "Ljava/lang/Long;");
+ jobject newLongObj =
+ env->NewObject(longClazz, initLong,
+ s.get<FrontendStatus::Tag::iptvPacketsReceived>());
+ env->SetObjectField(statusObj, field, newLongObj);
+ env->DeleteLocalRef(newLongObj);
+ break;
+ }
+ case FrontendStatus::Tag::iptvWorstJitterMs: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvWorstJitterMs",
+ "Ljava/lang/Integer;");
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::iptvWorstJitterMs>());
+ env->SetObjectField(statusObj, field, newIntegerObj);
+ env->DeleteLocalRef(newIntegerObj);
+ break;
+ }
+ case FrontendStatus::Tag::iptvAverageJitterMs: {
+ jfieldID field = env->GetFieldID(clazz, "mIptvAverageJitterMs",
+ "Ljava/lang/Integer;");
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::iptvAverageJitterMs>());
+ env->SetObjectField(statusObj, field, newIntegerObj);
+ env->DeleteLocalRef(newIntegerObj);
+ break;
+ }
}
}
return statusObj;
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index e4b9b5d..987b23f 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -1,9 +1,9 @@
LIBANDROID {
global:
- AActivityManager_addUidImportanceListener; # apex # introduced=31
- AActivityManager_removeUidImportanceListener; # apex # introduced=31
- AActivityManager_isUidActive; # apex # introduced=31
- AActivityManager_getUidImportance; # apex # introduced=31
+ AActivityManager_addUidImportanceListener; # systemapi # introduced=31
+ AActivityManager_removeUidImportanceListener; # systemapi # introduced=31
+ AActivityManager_isUidActive; # systemapi # introduced=31
+ AActivityManager_getUidImportance; # systemapi # introduced=31
AAssetDir_close;
AAssetDir_getNextFileName;
AAssetDir_rewind;
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
index a2fb101..3b9bf47 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
@@ -33,7 +33,6 @@
fun DisposableBroadcastReceiverAsUser(
intentFilter: IntentFilter,
userHandle: UserHandle,
- onStart: () -> Unit = {},
onReceive: (Intent) -> Unit,
) {
val context = LocalContext.current
@@ -49,7 +48,6 @@
context.registerReceiverAsUser(
broadcastReceiver, userHandle, intentFilter, null, null
)
- onStart()
},
onStop = {
context.unregisterReceiver(broadcastReceiver)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index ce0c551..5342def 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -29,19 +29,11 @@
import kotlinx.coroutines.runBlocking
/**
- * The config used to load the App List.
- */
-data class AppListConfig(
- val userId: Int,
- val showInstantApps: Boolean,
-)
-
-/**
* The repository to load the App List data.
*/
internal interface AppListRepository {
/** Loads the list of [ApplicationInfo]. */
- suspend fun loadApps(config: AppListConfig): List<ApplicationInfo>
+ suspend fun loadApps(userId: Int, showInstantApps: Boolean): List<ApplicationInfo>
/** Gets the flow of predicate that could used to filter system app. */
fun showSystemPredicate(
@@ -50,7 +42,7 @@
): Flow<(app: ApplicationInfo) -> Boolean>
/** Gets the system app package names. */
- fun getSystemPackageNamesBlocking(config: AppListConfig): Set<String>
+ fun getSystemPackageNamesBlocking(userId: Int, showInstantApps: Boolean): Set<String>
}
/**
@@ -59,15 +51,21 @@
object AppListRepositoryUtil {
/** Gets the system app package names. */
@JvmStatic
- fun getSystemPackageNames(context: Context, config: AppListConfig): Set<String> {
- return AppListRepositoryImpl(context).getSystemPackageNamesBlocking(config)
- }
+ fun getSystemPackageNames(
+ context: Context,
+ userId: Int,
+ showInstantApps: Boolean,
+ ): Set<String> =
+ AppListRepositoryImpl(context).getSystemPackageNamesBlocking(userId, showInstantApps)
}
internal class AppListRepositoryImpl(private val context: Context) : AppListRepository {
private val packageManager = context.packageManager
- override suspend fun loadApps(config: AppListConfig): List<ApplicationInfo> = coroutineScope {
+ override suspend fun loadApps(
+ userId: Int,
+ showInstantApps: Boolean,
+ ): List<ApplicationInfo> = coroutineScope {
val hiddenSystemModulesDeferred = async {
packageManager.getInstalledModules(0)
.filter { it.isHidden }
@@ -82,12 +80,12 @@
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
)
val installedApplicationsAsUser =
- packageManager.getInstalledApplicationsAsUser(flags, config.userId)
+ packageManager.getInstalledApplicationsAsUser(flags, userId)
val hiddenSystemModules = hiddenSystemModulesDeferred.await()
val hideWhenDisabledPackages = hideWhenDisabledPackagesDeferred.await()
installedApplicationsAsUser.filter { app ->
- app.isInAppList(config.showInstantApps, hiddenSystemModules, hideWhenDisabledPackages)
+ app.isInAppList(showInstantApps, hiddenSystemModules, hideWhenDisabledPackages)
}
}
@@ -97,18 +95,17 @@
): Flow<(app: ApplicationInfo) -> Boolean> =
userIdFlow.combine(showSystemFlow, ::showSystemPredicate)
- override fun getSystemPackageNamesBlocking(config: AppListConfig) = runBlocking {
- getSystemPackageNames(config)
- }
+ override fun getSystemPackageNamesBlocking(userId: Int, showInstantApps: Boolean) =
+ runBlocking { getSystemPackageNames(userId, showInstantApps) }
- private suspend fun getSystemPackageNames(config: AppListConfig): Set<String> =
- coroutineScope {
- val loadAppsDeferred = async { loadApps(config) }
- val homeOrLauncherPackages = loadHomeOrLauncherPackages(config.userId)
- val showSystemPredicate =
- { app: ApplicationInfo -> isSystemApp(app, homeOrLauncherPackages) }
- loadAppsDeferred.await().filter(showSystemPredicate).map { it.packageName }.toSet()
- }
+ private suspend fun getSystemPackageNames(userId: Int, showInstantApps: Boolean): Set<String> =
+ coroutineScope {
+ val loadAppsDeferred = async { loadApps(userId, showInstantApps) }
+ val homeOrLauncherPackages = loadHomeOrLauncherPackages(userId)
+ val showSystemPredicate =
+ { app: ApplicationInfo -> isSystemApp(app, homeOrLauncherPackages) }
+ loadAppsDeferred.await().filter(showSystemPredicate).map { it.packageName }.toSet()
+ }
private suspend fun showSystemPredicate(
userId: Int,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
index 6cd1c0d..8896042 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
@@ -26,6 +26,7 @@
import com.android.settingslib.spa.framework.util.asyncMapItem
import com.android.settingslib.spa.framework.util.waitFirst
import com.android.settingslib.spa.widget.ui.SpinnerOption
+import com.android.settingslib.spaprivileged.template.app.AppListConfig
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,8 +35,8 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
@@ -78,32 +79,77 @@
private val labelMap = ConcurrentHashMap<String, String>()
private val scope = viewModelScope + Dispatchers.IO
- private val userIdFlow = appListConfig.flow.map { it.userId }
+ private val userSubGraphsFlow = appListConfig.flow.map { config ->
+ config.userIds.map { userId -> UserSubGraph(userId, config.showInstantApps) }
+ }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
- private val appsStateFlow = MutableStateFlow<List<ApplicationInfo>?>(null)
+ private inner class UserSubGraph(
+ private val userId: Int,
+ private val showInstantApps: Boolean,
+ ) {
+ private val userIdFlow = flowOf(userId)
- private val recordListFlow = listModel.flow
- .flatMapLatest { it.transform(userIdFlow, appsStateFlow.filterNotNull()) }
- .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+ private val appsStateFlow = MutableStateFlow<List<ApplicationInfo>?>(null)
- private val systemFilteredFlow =
- appListRepository.showSystemPredicate(userIdFlow, showSystem.flow)
- .combine(recordListFlow) { showAppPredicate, recordList ->
- recordList.filter { showAppPredicate(it.app) }
+ val recordListFlow = listModel.flow
+ .flatMapLatest { it.transform(userIdFlow, appsStateFlow.filterNotNull()) }
+ .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+ private val systemFilteredFlow =
+ appListRepository.showSystemPredicate(userIdFlow, showSystem.flow)
+ .combine(recordListFlow) { showAppPredicate, recordList ->
+ recordList.filter { showAppPredicate(it.app) }
+ }
+ .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+ val listModelFilteredFlow = optionFlow.filterNotNull().flatMapLatest { option ->
+ listModel.flow.flatMapLatest { listModel ->
+ listModel.filter(this.userIdFlow, option, this.systemFilteredFlow)
}
+ }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+ fun reloadApps() {
+ scope.launch {
+ appsStateFlow.value = appListRepository.loadApps(userId, showInstantApps)
+ }
+ }
+ }
+
+ private val combinedRecordListFlow = userSubGraphsFlow.flatMapLatest { userSubGraphList ->
+ combine(userSubGraphList.map { it.recordListFlow }) { it.toList().flatten() }
+ }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
override val spinnerOptionsFlow =
- recordListFlow.combine(listModel.flow) { recordList, listModel ->
+ combinedRecordListFlow.combine(listModel.flow) { recordList, listModel ->
listModel.getSpinnerOptions(recordList)
- }
+ }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
- override val appListDataFlow = optionFlow.filterNotNull().flatMapLatest(::filterAndSort)
- .combine(searchQuery.flow) { appListData, searchQuery ->
+ private val appEntryListFlow = userSubGraphsFlow.flatMapLatest { userSubGraphList ->
+ combine(userSubGraphList.map { it.listModelFilteredFlow }) { it.toList().flatten() }
+ }.asyncMapItem { record ->
+ val label = getLabel(record.app)
+ AppEntry(
+ record = record,
+ label = label,
+ labelCollationKey = collator.getCollationKey(label),
+ )
+ }
+
+ override val appListDataFlow =
+ combine(
+ appEntryListFlow,
+ listModel.flow,
+ optionFlow.filterNotNull(),
+ ) { appEntries, listModel, option ->
+ AppListData(
+ appEntries = appEntries.sortedWith(listModel.getComparator(option)),
+ option = option,
+ )
+ }.combine(searchQuery.flow) { appListData, searchQuery ->
appListData.filter {
it.label.contains(other = searchQuery, ignoreCase = true)
}
- }
- .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+ }.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
init {
scheduleOnFirstLoaded()
@@ -111,30 +157,16 @@
fun reloadApps() {
scope.launch {
- appsStateFlow.value = appListRepository.loadApps(appListConfig.flow.first())
+ userSubGraphsFlow.collect { userSubGraphList ->
+ for (userSubGraph in userSubGraphList) {
+ userSubGraph.reloadApps()
+ }
+ }
}
}
- private fun filterAndSort(option: Int) = listModel.flow.flatMapLatest { listModel ->
- listModel.filter(userIdFlow, option, systemFilteredFlow)
- .asyncMapItem { record ->
- val label = getLabel(record.app)
- AppEntry(
- record = record,
- label = label,
- labelCollationKey = collator.getCollationKey(label),
- )
- }
- .map { appEntries ->
- AppListData(
- appEntries = appEntries.sortedWith(listModel.getComparator(option)),
- option = option,
- )
- }
- }
-
private fun scheduleOnFirstLoaded() {
- recordListFlow
+ combinedRecordListFlow
.waitFirst(appListDataFlow)
.combine(listModel.flow) { recordList, listModel ->
if (listModel.onFirstLoaded(recordList)) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 57a60e5..4a8c00e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -32,6 +32,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.settingslib.spa.framework.compose.LifecycleEffect
import com.android.settingslib.spa.framework.compose.LogCompositions
import com.android.settingslib.spa.framework.compose.TimeMeasurer.Companion.rememberTimeMeasurer
import com.android.settingslib.spa.framework.compose.rememberLazyListStateAndHideKeyboardWhenStartScroll
@@ -43,7 +44,6 @@
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser
import com.android.settingslib.spaprivileged.model.app.AppEntry
-import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppListViewModel
@@ -56,6 +56,14 @@
private const val TAG = "AppList"
private const val CONTENT_TYPE_HEADER = "header"
+/**
+ * The config used to load the App List.
+ */
+data class AppListConfig(
+ val userIds: List<Int>,
+ val showInstantApps: Boolean,
+)
+
data class AppListState(
val showSystem: State<Boolean>,
val searchQuery: State<String>,
@@ -84,7 +92,7 @@
internal fun <T : AppRecord> AppListInput<T>.AppListImpl(
viewModelSupplier: @Composable () -> IAppListViewModel<T>,
) {
- LogCompositions(TAG, config.userId.toString())
+ LogCompositions(TAG, config.userIds.toString())
val viewModel = viewModelSupplier()
Column(Modifier.fillMaxSize()) {
val optionsState = viewModel.spinnerOptionsFlow.collectAsState(null, Dispatchers.IO)
@@ -168,21 +176,23 @@
listModel: AppListModel<T>,
state: AppListState,
): AppListViewModel<T> {
- val viewModel: AppListViewModel<T> = viewModel(key = config.userId.toString())
+ val viewModel: AppListViewModel<T> = viewModel(key = config.userIds.toString())
viewModel.appListConfig.setIfAbsent(config)
viewModel.listModel.setIfAbsent(listModel)
viewModel.showSystem.Sync(state.showSystem)
viewModel.searchQuery.Sync(state.searchQuery)
- DisposableBroadcastReceiverAsUser(
- intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
- addAction(Intent.ACTION_PACKAGE_REMOVED)
- addAction(Intent.ACTION_PACKAGE_CHANGED)
- addDataScheme("package")
- },
- userHandle = UserHandle.of(config.userId),
- onStart = { viewModel.reloadApps() },
- ) { viewModel.reloadApps() }
-
+ LifecycleEffect(onStart = { viewModel.reloadApps() })
+ val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
+ addAction(Intent.ACTION_PACKAGE_REMOVED)
+ addAction(Intent.ACTION_PACKAGE_CHANGED)
+ addDataScheme("package")
+ }
+ for (userId in config.userIds) {
+ DisposableBroadcastReceiverAsUser(
+ intentFilter = intentFilter,
+ userHandle = UserHandle.of(userId),
+ ) { viewModel.reloadApps() }
+ }
return viewModel
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index 404e27c..2ebbe8a 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -24,10 +24,9 @@
import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope
import com.android.settingslib.spa.widget.scaffold.SearchScaffold
import com.android.settingslib.spaprivileged.R
-import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
-import com.android.settingslib.spaprivileged.template.common.WorkProfilePager
+import com.android.settingslib.spaprivileged.template.common.UserProfilePager
/**
* The full screen template for an App List page.
@@ -55,10 +54,10 @@
}
},
) { bottomPadding, searchQuery ->
- WorkProfilePager(primaryUserOnly) { userInfo ->
+ UserProfilePager(primaryUserOnly) { userGroup ->
val appListInput = AppListInput(
config = AppListConfig(
- userId = userInfo.id,
+ userIds = userGroup.userInfos.map { it.id },
showInstantApps = showInstantApps,
),
listModel = listModel,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
new file mode 100644
index 0000000..b5a4929
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.common
+
+import android.content.pm.UserInfo
+import android.content.pm.UserProperties
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import com.android.settingslib.spa.widget.scaffold.SettingsPager
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
+
+/**
+ * Info about how to group multiple profiles for Settings.
+ *
+ * @see [UserProperties.ShowInSettings]
+ */
+data class UserGroup(
+ /** The users in this user group, if multiple users, the first one is parent user. */
+ val userInfos: List<UserInfo>,
+)
+
+@Composable
+fun UserProfilePager(
+ primaryUserOnly: Boolean = false,
+ content: @Composable (userGroup: UserGroup) -> Unit,
+) {
+ val context = LocalContext.current
+ val userGroups = remember {
+ context.userManager.getUserGroups(primaryUserOnly)
+ }
+ val titles = remember {
+ val enterpriseRepository = EnterpriseRepository(context)
+ userGroups.map { userGroup ->
+ enterpriseRepository.getProfileTitle(
+ isManagedProfile = userGroup.userInfos.first().isManagedProfile,
+ )
+ }
+ }
+
+ SettingsPager(titles) { page ->
+ content(userGroups[page])
+ }
+}
+
+private fun UserManager.getUserGroups(primaryUserOnly: Boolean): List<UserGroup> {
+ val userGroupList = mutableListOf<UserGroup>()
+ val profileToShowInSettingsList = getProfiles(UserHandle.myUserId())
+ .filter { userInfo -> !primaryUserOnly || userInfo.isPrimary }
+ .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle).showInSettings }
+
+ profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT }
+ .takeIf { it.isNotEmpty() }
+ ?.map { it.first }
+ ?.let { userInfos -> userGroupList += UserGroup(userInfos) }
+
+ profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_LAUNCHER_SEPARATE }
+ .forEach { userGroupList += UserGroup(userInfos = listOf(it.first)) }
+
+ return userGroupList
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
deleted file mode 100644
index a76c438..0000000
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spaprivileged.template.common
-
-import android.content.pm.UserInfo
-import android.os.UserHandle
-import android.os.UserManager
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
-import com.android.settingslib.spa.widget.scaffold.SettingsPager
-import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
-
-@Composable
-fun WorkProfilePager(
- primaryUserOnly: Boolean = false,
- content: @Composable (userInfo: UserInfo) -> Unit,
-) {
- val context = LocalContext.current
- val profiles = remember {
- val userManager = checkNotNull(context.getSystemService(UserManager::class.java))
- userManager.getProfiles(UserHandle.myUserId()).filter { userInfo ->
- !primaryUserOnly || userInfo.isPrimary
- }
- }
- val titles = remember {
- val enterpriseRepository = EnterpriseRepository(context)
- profiles.map {
- enterpriseRepository.getProfileTitle(isManagedProfile = it.isManagedProfile)
- }
- }
-
- SettingsPager(titles) { page ->
- content(profiles[page])
- }
-}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
index 01f4cc6..9bd9242 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
@@ -85,23 +85,6 @@
assertThat(onReceiveIsCalled).isTrue()
}
- @Test
- fun broadcastReceiver_onStartIsCalled() {
- var onStartIsCalled = false
- composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
- DisposableBroadcastReceiverAsUser(
- intentFilter = IntentFilter(),
- userHandle = USER_HANDLE,
- onStart = { onStartIsCalled = true },
- onReceive = {},
- )
- }
- }
-
- assertThat(onStartIsCalled).isTrue()
- }
-
private companion object {
val USER_HANDLE: UserHandle = UserHandle.of(0)
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index b0ea40a..57972ed 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -83,9 +83,8 @@
@Test
fun loadApps_notShowInstantApps() = runTest {
mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
assertThat(appListFlow).containsExactly(NORMAL_APP)
}
@@ -93,9 +92,8 @@
@Test
fun loadApps_showInstantApps() = runTest {
mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = true)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = true)
assertThat(appListFlow).containsExactly(NORMAL_APP, INSTANT_APP)
}
@@ -109,9 +107,8 @@
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(arrayOf(app.packageName))
mockInstalledApplications(listOf(app))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
assertThat(appListFlow).isEmpty()
}
@@ -126,9 +123,8 @@
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(arrayOf(app.packageName))
mockInstalledApplications(listOf(app))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
assertThat(appListFlow).isEmpty()
}
@@ -142,9 +138,8 @@
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(arrayOf(app.packageName))
mockInstalledApplications(listOf(app))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
assertThat(appListFlow).containsExactly(app)
}
@@ -157,9 +152,8 @@
enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
}
mockInstalledApplications(listOf(app))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
assertThat(appListFlow).containsExactly(app)
}
@@ -171,9 +165,8 @@
enabled = false
}
mockInstalledApplications(listOf(app))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
- val appListFlow = repository.loadApps(appListConfig)
+ val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
assertThat(appListFlow).isEmpty()
}
@@ -223,7 +216,7 @@
@Test
fun showSystemPredicate_appInLauncher() = runTest {
- val app = IN_LAUMCHER_APP
+ val app = IN_LAUNCHER_APP
whenever(
packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
@@ -237,10 +230,13 @@
@Test
fun getSystemPackageNames_returnExpectedValues() = runTest {
mockInstalledApplications(listOf(
- NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUMCHER_APP))
- val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
+ NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP))
- val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames(context, appListConfig)
+ val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames(
+ context = context,
+ userId = USER_ID,
+ showInstantApps = false,
+ )
assertThat(systemPackageNames).containsExactly("system.app", "home.app", "app.in.launcher")
}
@@ -280,7 +276,7 @@
flags = ApplicationInfo.FLAG_SYSTEM
}
- val IN_LAUMCHER_APP = ApplicationInfo().apply {
+ val IN_LAUNCHER_APP = ApplicationInfo().apply {
packageName = "app.in.launcher"
flags = ApplicationInfo.FLAG_SYSTEM
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
index b1d02f5..fc40aed 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.mapItem
import com.android.settingslib.spa.testutils.waitUntil
+import com.android.settingslib.spaprivileged.template.app.AppListConfig
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -84,14 +85,17 @@
}
private object FakeAppListRepository : AppListRepository {
- override suspend fun loadApps(config: AppListConfig) = listOf(APP)
+ override suspend fun loadApps(userId: Int, showInstantApps: Boolean) = listOf(APP)
override fun showSystemPredicate(
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { true }
- override fun getSystemPackageNamesBlocking(config: AppListConfig): Set<String> = setOf()
+ override fun getSystemPackageNamesBlocking(
+ userId: Int,
+ showInstantApps: Boolean,
+ ): Set<String> = emptySet()
}
private object FakeAppRepository : AppRepository {
@@ -105,7 +109,7 @@
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
- val CONFIG = AppListConfig(userId = USER_ID, showInstantApps = false)
+ val CONFIG = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false)
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
index d5c7c19..a99d02d 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
@@ -32,7 +32,6 @@
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.AppEntry
-import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.IAppListViewModel
import com.android.settingslib.spaprivileged.tests.testutils.TestAppListModel
@@ -115,7 +114,7 @@
) {
composeTestRule.setContent {
AppListInput(
- config = AppListConfig(userId = USER_ID, showInstantApps = false),
+ config = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false),
listModel = TestAppListModel(enableGrouping = enableGrouping),
state = AppListState(
showSystem = false.toState(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index db224be..a82f070 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -30,6 +30,8 @@
public static final String CATEGORY_APPS = "com.android.settings.category.ia.apps";
public static final String CATEGORY_APPS_DEFAULT =
"com.android.settings.category.ia.apps.default";
+ public static final String CATEGORY_SPECIAL_APP_ACCESS =
+ "com.android.settings.category.ia.special_app_access";
public static final String CATEGORY_BATTERY = "com.android.settings.category.ia.battery";
public static final String CATEGORY_DISPLAY = "com.android.settings.category.ia.display";
public static final String CATEGORY_SOUND = "com.android.settings.category.ia.sound";
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ede69be..75d28d5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -228,9 +228,6 @@
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
GlobalSettingsProto.App.AUTO_RESTRICTION_ENABLED);
dumpSetting(s, p,
- Settings.Global.FORCED_APP_STANDBY_ENABLED,
- GlobalSettingsProto.App.FORCED_APP_STANDBY_ENABLED);
- dumpSetting(s, p,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
GlobalSettingsProto.App.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED);
p.end(appToken);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index db7032e..01740319 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -285,7 +285,6 @@
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
- Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.WIFI_ON_WHEN_PROXY_DISCONNECTED,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 697e181..f305981 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -112,6 +112,24 @@
],
}
+//Create a library to expose SystemUI's resources to other modules.
+android_library {
+ name: "SystemUI-res",
+ resource_dirs: [
+ "res-product",
+ "res-keyguard",
+ "res",
+ ],
+ static_libs: [
+ "SystemUISharedLib",
+ "SettingsLib",
+ "androidx.leanback_leanback",
+ "androidx.slice_slice-core",
+ "androidx.slice_slice-view",
+ ],
+ manifest: "AndroidManifest-res.xml",
+}
+
android_library {
name: "SystemUI-core",
defaults: [
diff --git a/packages/SystemUI/AndroidManifest-res.xml b/packages/SystemUI/AndroidManifest-res.xml
new file mode 100644
index 0000000..58e9dc6
--- /dev/null
+++ b/packages/SystemUI/AndroidManifest-res.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 Google Inc.
+ *
+ * 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.
+ -->
+<manifest package= "com.android.systemui.res">
+ <application/>
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8f70dcc..c729b09 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -301,10 +301,13 @@
interface Callback {
/** Whether we are currently on the keyguard or not. */
- fun isOnKeyguard(): Boolean
+ @JvmDefault fun isOnKeyguard(): Boolean = false
/** Hide the keyguard and animate using [runner]. */
- fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
+ @JvmDefault
+ fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
+ throw UnsupportedOperationException()
+ }
/* Get the background color of [task]. */
fun getBackgroundColor(task: TaskInfo): Int
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 3d341af..2903288 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -33,9 +33,7 @@
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_DEFAULT_VALUE = 0f
-/**
- * Provide interpolation of two fonts by adjusting font variation settings.
- */
+/** Provide interpolation of two fonts by adjusting font variation settings. */
class FontInterpolator {
/**
@@ -61,11 +59,14 @@
var index: Int,
val sortedAxes: MutableList<FontVariationAxis>
) {
- constructor(font: Font, axes: List<FontVariationAxis>) :
- this(font.sourceIdentifier,
- font.ttcIndex,
- axes.toMutableList().apply { sortBy { it.tag } }
- )
+ constructor(
+ font: Font,
+ axes: List<FontVariationAxis>
+ ) : this(
+ font.sourceIdentifier,
+ font.ttcIndex,
+ axes.toMutableList().apply { sortBy { it.tag } }
+ )
fun set(font: Font, axes: List<FontVariationAxis>) {
sourceId = font.sourceIdentifier
@@ -86,9 +87,7 @@
private val tmpInterpKey = InterpKey(null, null, 0f)
private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf())
- /**
- * Linear interpolate the font variation settings.
- */
+ /** Linear interpolate the font variation settings. */
fun lerp(start: Font, end: Font, progress: Float): Font {
if (progress == 0f) {
return start
@@ -115,27 +114,34 @@
// this doesn't take much time since the variation axes is usually up to 5. If we need to
// support more number of axes, we may want to preprocess the font and store the sorted axes
// and also pre-fill the missing axes value with default value from 'fvar' table.
- val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue ->
- when (tag) {
- // TODO: Good to parse 'fvar' table for retrieving default value.
- TAG_WGHT -> adjustWeight(
- MathUtils.lerp(
+ val newAxes =
+ lerp(startAxes, endAxes) { tag, startValue, endValue ->
+ when (tag) {
+ // TODO: Good to parse 'fvar' table for retrieving default value.
+ TAG_WGHT ->
+ adjustWeight(
+ MathUtils.lerp(
startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- progress))
- TAG_ITAL -> adjustItalic(
- MathUtils.lerp(
+ progress
+ )
+ )
+ TAG_ITAL ->
+ adjustItalic(
+ MathUtils.lerp(
startValue ?: FONT_ITALIC_DEFAULT_VALUE,
endValue ?: FONT_ITALIC_DEFAULT_VALUE,
- progress))
- else -> {
- require(startValue != null && endValue != null) {
- "Unable to interpolate due to unknown default axes value : $tag"
+ progress
+ )
+ )
+ else -> {
+ require(startValue != null && endValue != null) {
+ "Unable to interpolate due to unknown default axes value : $tag"
+ }
+ MathUtils.lerp(startValue, endValue, progress)
}
- MathUtils.lerp(startValue, endValue, progress)
}
}
- }
// Check if we already make font for this axes. This is typically happens if the animation
// happens backward.
@@ -149,9 +155,7 @@
// This is the first time to make the font for the axes. Build and store it to the cache.
// Font.Builder#build won't throw IOException since creating fonts from existing fonts will
// not do any IO work.
- val newFont = Font.Builder(start)
- .setFontVariationSettings(newAxes.toTypedArray())
- .build()
+ val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
interpCache[InterpKey(start, end, progress)] = newFont
verFontCache[VarFontKey(start, newAxes)] = newFont
return newFont
@@ -173,26 +177,28 @@
val tagA = if (i < start.size) start[i].tag else null
val tagB = if (j < end.size) end[j].tag else null
- val comp = when {
- tagA == null -> 1
- tagB == null -> -1
- else -> tagA.compareTo(tagB)
- }
+ val comp =
+ when {
+ tagA == null -> 1
+ tagB == null -> -1
+ else -> tagA.compareTo(tagB)
+ }
- val axis = when {
- comp == 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
- FontVariationAxis(tagA, v)
+ val axis =
+ when {
+ comp == 0 -> {
+ val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
+ FontVariationAxis(tagA, v)
+ }
+ comp < 0 -> {
+ val v = filter(tagA!!, start[i++].styleValue, null)
+ FontVariationAxis(tagA, v)
+ }
+ else -> { // comp > 0
+ val v = filter(tagB!!, null, end[j++].styleValue)
+ FontVariationAxis(tagB, v)
+ }
}
- comp < 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, null)
- FontVariationAxis(tagA, v)
- }
- else -> { // comp > 0
- val v = filter(tagB!!, null, end[j++].styleValue)
- FontVariationAxis(tagB, v)
- }
- }
result.add(axis)
}
@@ -202,21 +208,21 @@
// For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
// Cache hit ratio in the Skia glyph cache.
private fun adjustWeight(value: Float) =
- coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
+ coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
// For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
// Cache hit ratio in the Skia glyph cache.
private fun adjustItalic(value: Float) =
- coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
+ coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
- (v.coerceIn(min, max) / step).toInt() * step
+ (v.coerceIn(min, max) / step).toInt() * step
companion object {
private val EMPTY_AXES = arrayOf<FontVariationAxis>()
// Returns true if given two font instance can be interpolated.
fun canInterpolate(start: Font, end: Font) =
- start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
+ start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 5f1bb83..fdab749 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -36,8 +36,8 @@
* Currently this class can provide text style animation for text weight and text size. For example
* the simple view that draws text with animating text size is like as follows:
*
- * <pre>
- * <code>
+ * <pre> <code>
+ * ```
* class SimpleTextAnimation : View {
* @JvmOverloads constructor(...)
*
@@ -53,83 +53,63 @@
* animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate)
* }
* }
- * </code>
- * </pre>
+ * ```
+ * </code> </pre>
*/
-class TextAnimator(
- layout: Layout,
- private val invalidateCallback: () -> Unit
-) {
+class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
// Following two members are for mutable for testing purposes.
public var textInterpolator: TextInterpolator = TextInterpolator(layout)
- public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
- duration = DEFAULT_ANIMATION_DURATION
- addUpdateListener {
- textInterpolator.progress = it.animatedValue as Float
- invalidateCallback()
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- textInterpolator.rebase()
+ public var animator: ValueAnimator =
+ ValueAnimator.ofFloat(1f).apply {
+ duration = DEFAULT_ANIMATION_DURATION
+ addUpdateListener {
+ textInterpolator.progress = it.animatedValue as Float
+ invalidateCallback()
}
- override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
- })
- }
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ textInterpolator.rebase()
+ }
+ override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
+ }
+ )
+ }
sealed class PositionedGlyph {
- /**
- * Mutable X coordinate of the glyph position relative from drawing offset.
- */
+ /** Mutable X coordinate of the glyph position relative from drawing offset. */
var x: Float = 0f
- /**
- * Mutable Y coordinate of the glyph position relative from the baseline.
- */
+ /** Mutable Y coordinate of the glyph position relative from the baseline. */
var y: Float = 0f
- /**
- * The current line of text being drawn, in a multi-line TextView.
- */
+ /** The current line of text being drawn, in a multi-line TextView. */
var lineNo: Int = 0
- /**
- * Mutable text size of the glyph in pixels.
- */
+ /** Mutable text size of the glyph in pixels. */
var textSize: Float = 0f
- /**
- * Mutable color of the glyph.
- */
+ /** Mutable color of the glyph. */
var color: Int = 0
- /**
- * Immutable character offset in the text that the current font run start.
- */
+ /** Immutable character offset in the text that the current font run start. */
abstract var runStart: Int
protected set
- /**
- * Immutable run length of the font run.
- */
+ /** Immutable run length of the font run. */
abstract var runLength: Int
protected set
- /**
- * Immutable glyph index of the font run.
- */
+ /** Immutable glyph index of the font run. */
abstract var glyphIndex: Int
protected set
- /**
- * Immutable font instance for this font run.
- */
+ /** Immutable font instance for this font run. */
abstract var font: Font
protected set
- /**
- * Immutable glyph ID for this glyph.
- */
+ /** Immutable glyph ID for this glyph. */
abstract var glyphId: Int
protected set
}
@@ -147,20 +127,18 @@
/**
* GlyphFilter applied just before drawing to canvas for tweaking positions and text size.
*
- * This callback is called for each glyphs just before drawing the glyphs. This function will
- * be called with the intrinsic position, size, color, glyph ID and font instance. You can
- * mutate the position, size and color for tweaking animations.
- * Do not keep the reference of passed glyph object. The interpolator reuses that object for
- * avoiding object allocations.
+ * This callback is called for each glyphs just before drawing the glyphs. This function will be
+ * called with the intrinsic position, size, color, glyph ID and font instance. You can mutate
+ * the position, size and color for tweaking animations. Do not keep the reference of passed
+ * glyph object. The interpolator reuses that object for avoiding object allocations.
*
- * Details:
- * The text is drawn with font run units. The font run is a text segment that draws with the
- * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text
- * that current glyph is in. Once the font run is determined, the system will convert characters
- * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and
- * {@code glyphIndex} is the offset of the converted glyph array. Please note that the
- * {@code glyphIndex} is not a character index, because the character will not be converted to
- * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
+ * Details: The text is drawn with font run units. The font run is a text segment that draws
+ * with the same font. The {@code runStart} and {@code runLimit} is a range of the font run in
+ * the text that current glyph is in. Once the font run is determined, the system will convert
+ * characters into glyph IDs. The {@code glyphId} is the glyph identifier in the font and {@code
+ * glyphIndex} is the offset of the converted glyph array. Please note that the {@code
+ * glyphIndex} is not a character index, because the character will not be converted to glyph
+ * one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
* composed from multiple characters.
*
* Here is an example of font runs: "fin. 終わり"
@@ -193,7 +171,9 @@
*/
var glyphFilter: GlyphCallback?
get() = textInterpolator.glyphFilter
- set(value) { textInterpolator.glyphFilter = value }
+ set(value) {
+ textInterpolator.glyphFilter = value
+ }
fun draw(c: Canvas) = textInterpolator.draw(c)
@@ -208,7 +188,7 @@
* @param weight an optional text weight.
* @param textSize an optional font size.
* @param colors an optional colors array that must be the same size as numLines passed to
- * the TextInterpolator
+ * the TextInterpolator
* @param animate an optional boolean indicating true for showing style transition as animation,
* false for immediate style transition. True by default.
* @param duration an optional animation duration in milliseconds. This is ignored if animate is
@@ -237,10 +217,11 @@
if (weight >= 0) {
// Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
// memory impact, cache the typeface result.
- textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) {
- textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
- textInterpolator.targetPaint.typeface
- }
+ textInterpolator.targetPaint.typeface =
+ typefaceCache.getOrElse(weight) {
+ textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+ textInterpolator.targetPaint.typeface
+ }
}
if (color != null) {
textInterpolator.targetPaint.color = color
@@ -249,22 +230,24 @@
if (animate) {
animator.startDelay = delay
- animator.duration = if (duration == -1L) {
- DEFAULT_ANIMATION_DURATION
- } else {
- duration
- }
+ animator.duration =
+ if (duration == -1L) {
+ DEFAULT_ANIMATION_DURATION
+ } else {
+ duration
+ }
interpolator?.let { animator.interpolator = it }
if (onAnimationEnd != null) {
- val listener = object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd.run()
- animator.removeListener(this)
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd.run()
+ animator.removeListener(this)
+ }
+ override fun onAnimationCancel(animation: Animator?) {
+ animator.removeListener(this)
+ }
}
- override fun onAnimationCancel(animation: Animator?) {
- animator.removeListener(this)
- }
- }
animator.addListener(listener)
}
animator.start()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 0448c81..341784e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -26,12 +26,8 @@
import com.android.internal.graphics.ColorUtils
import java.lang.Math.max
-/**
- * Provide text style linear interpolation for plain text.
- */
-class TextInterpolator(
- layout: Layout
-) {
+/** Provide text style linear interpolation for plain text. */
+class TextInterpolator(layout: Layout) {
/**
* Returns base paint used for interpolation.
@@ -64,12 +60,11 @@
var baseFont: Font,
var targetFont: Font
) {
- val length: Int get() = end - start
+ val length: Int
+ get() = end - start
}
- /**
- * A class represents text layout of a single run.
- */
+ /** A class represents text layout of a single run. */
private class Run(
val glyphIds: IntArray,
val baseX: FloatArray, // same length as glyphIds
@@ -79,12 +74,8 @@
val fontRuns: List<FontRun>
)
- /**
- * A class represents text layout of a single line.
- */
- private class Line(
- val runs: List<Run>
- )
+ /** A class represents text layout of a single line. */
+ private class Line(val runs: List<Run>)
private var lines = listOf<Line>()
private val fontInterpolator = FontInterpolator()
@@ -106,8 +97,8 @@
/**
* The layout used for drawing text.
*
- * Only non-styled text is supported. Even if the given layout is created from Spanned, the
- * span information is not used.
+ * Only non-styled text is supported. Even if the given layout is created from Spanned, the span
+ * information is not used.
*
* The paint objects used for interpolation are not changed by this method call.
*
@@ -122,6 +113,9 @@
shapeText(value)
}
+ var shapedText: String = ""
+ private set
+
init {
// shapeText needs to be called after all members are initialized.
shapeText(layout)
@@ -130,8 +124,8 @@
/**
* Recalculate internal text layout for interpolation.
*
- * Whenever the target paint is modified, call this method to recalculate internal
- * text layout used for interpolation.
+ * Whenever the target paint is modified, call this method to recalculate internal text layout
+ * used for interpolation.
*/
fun onTargetPaintModified() {
updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
@@ -140,8 +134,8 @@
/**
* Recalculate internal text layout for interpolation.
*
- * Whenever the base paint is modified, call this method to recalculate internal
- * text layout used for interpolation.
+ * Whenever the base paint is modified, call this method to recalculate internal text layout
+ * used for interpolation.
*/
fun onBasePaintModified() {
updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
@@ -152,11 +146,11 @@
*
* The text interpolator does not calculate all the text position by text shaper due to
* performance reasons. Instead, the text interpolator shape the start and end state and
- * calculate text position of the middle state by linear interpolation. Due to this trick,
- * the text positions of the middle state is likely different from the text shaper result.
- * So, if you want to start animation from the middle state, you will see the glyph jumps due to
- * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different
- * from text shape result of weight 550.
+ * calculate text position of the middle state by linear interpolation. Due to this trick, the
+ * text positions of the middle state is likely different from the text shaper result. So, if
+ * you want to start animation from the middle state, you will see the glyph jumps due to this
+ * trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different from
+ * text shape result of weight 550.
*
* After calling this method, do not call onBasePaintModified() since it reshape the text and
* update the base state. As in above notice, the text shaping result at current progress is
@@ -168,8 +162,8 @@
* animate weight from 200 to 400, then if you want to move back to 200 at the half of the
* animation, it will look like
*
- * <pre>
- * <code>
+ * <pre> <code>
+ * ```
* val interp = TextInterpolator(layout)
*
* // Interpolate between weight 200 to 400.
@@ -199,9 +193,8 @@
* // progress is 0.5
* animator.start()
* }
- * </code>
- * </pre>
- *
+ * ```
+ * </code> </pre>
*/
fun rebase() {
if (progress == 0f) {
@@ -263,69 +256,73 @@
}
var maxRunLength = 0
- lines = baseLayout.zip(targetLayout) { baseLine, targetLine ->
- val runs = baseLine.zip(targetLine) { base, target ->
-
- require(base.glyphCount() == target.glyphCount()) {
- "Inconsistent glyph count at line ${lines.size}"
- }
-
- val glyphCount = base.glyphCount()
-
- // Good to recycle the array if the existing array can hold the new layout result.
- val glyphIds = IntArray(glyphCount) {
- base.getGlyphId(it).also { baseGlyphId ->
- require(baseGlyphId == target.getGlyphId(it)) {
- "Inconsistent glyph ID at $it in line ${lines.size}"
+ lines =
+ baseLayout.zip(targetLayout) { baseLine, targetLine ->
+ val runs =
+ baseLine.zip(targetLine) { base, target ->
+ require(base.glyphCount() == target.glyphCount()) {
+ "Inconsistent glyph count at line ${lines.size}"
}
- }
- }
- val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
- val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
- val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
- val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+ val glyphCount = base.glyphCount()
- // Calculate font runs
- val fontRun = mutableListOf<FontRun>()
- if (glyphCount != 0) {
- var start = 0
- var baseFont = base.getFont(start)
- var targetFont = target.getFont(start)
- require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
- "Cannot interpolate font at $start ($baseFont vs $targetFont)"
- }
-
- for (i in 1 until glyphCount) {
- val nextBaseFont = base.getFont(i)
- val nextTargetFont = target.getFont(i)
-
- if (baseFont !== nextBaseFont) {
- require(targetFont !== nextTargetFont) {
- "Base font has changed at $i but target font has not changed."
+ // Good to recycle the array if the existing array can hold the new layout
+ // result.
+ val glyphIds =
+ IntArray(glyphCount) {
+ base.getGlyphId(it).also { baseGlyphId ->
+ require(baseGlyphId == target.getGlyphId(it)) {
+ "Inconsistent glyph ID at $it in line ${lines.size}"
+ }
+ }
}
- // Font transition point. push run and reset context.
- fontRun.add(FontRun(start, i, baseFont, targetFont))
- maxRunLength = max(maxRunLength, i - start)
- baseFont = nextBaseFont
- targetFont = nextTargetFont
- start = i
+
+ val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
+ val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
+ val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
+ val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+
+ // Calculate font runs
+ val fontRun = mutableListOf<FontRun>()
+ if (glyphCount != 0) {
+ var start = 0
+ var baseFont = base.getFont(start)
+ var targetFont = target.getFont(start)
require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
"Cannot interpolate font at $start ($baseFont vs $targetFont)"
}
- } else { // baseFont === nextBaseFont
- require(targetFont === nextTargetFont) {
- "Base font has not changed at $i but target font has changed."
+
+ for (i in 1 until glyphCount) {
+ val nextBaseFont = base.getFont(i)
+ val nextTargetFont = target.getFont(i)
+
+ if (baseFont !== nextBaseFont) {
+ require(targetFont !== nextTargetFont) {
+ "Base font has changed at $i but target font is unchanged."
+ }
+ // Font transition point. push run and reset context.
+ fontRun.add(FontRun(start, i, baseFont, targetFont))
+ maxRunLength = max(maxRunLength, i - start)
+ baseFont = nextBaseFont
+ targetFont = nextTargetFont
+ start = i
+ require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+ "Cannot interpolate font at $start" +
+ " ($baseFont vs $targetFont)"
+ }
+ } else { // baseFont === nextBaseFont
+ require(targetFont === nextTargetFont) {
+ "Base font is unchanged at $i but target font has changed."
+ }
+ }
}
+ fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
+ maxRunLength = max(maxRunLength, glyphCount - start)
}
+ Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
}
- fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
- maxRunLength = max(maxRunLength, glyphCount - start)
- }
- Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
+ Line(runs)
}
- Line(runs)
- }
// Update float array used for drawing.
if (tmpPositionArray.size < maxRunLength * 2) {
@@ -357,9 +354,9 @@
if (glyphFilter == null) {
for (i in run.start until run.end) {
tmpPositionArray[arrayIndex++] =
- MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+ MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
tmpPositionArray[arrayIndex++] =
- MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+ MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
}
c.drawGlyphs(line.glyphIds, run.start, tmpPositionArray, 0, run.length, font, paint)
return
@@ -388,13 +385,14 @@
tmpPaintForGlyph.color = tmpGlyph.color
c.drawGlyphs(
- line.glyphIds,
- prevStart,
- tmpPositionArray,
- 0,
- i - prevStart,
- font,
- tmpPaintForGlyph)
+ line.glyphIds,
+ prevStart,
+ tmpPositionArray,
+ 0,
+ i - prevStart,
+ font,
+ tmpPaintForGlyph
+ )
prevStart = i
arrayIndex = 0
}
@@ -404,13 +402,14 @@
}
c.drawGlyphs(
- line.glyphIds,
- prevStart,
- tmpPositionArray,
- 0,
- run.end - prevStart,
- font,
- tmpPaintForGlyph)
+ line.glyphIds,
+ prevStart,
+ tmpPositionArray,
+ 0,
+ run.end - prevStart,
+ font,
+ tmpPaintForGlyph
+ )
}
private fun updatePositionsAndFonts(
@@ -418,9 +417,7 @@
updateBase: Boolean
) {
// Update target positions with newly calculated text layout.
- check(layoutResult.size == lines.size) {
- "The new layout result has different line count."
- }
+ check(layoutResult.size == lines.size) { "The new layout result has different line count." }
lines.zip(layoutResult) { line, runs ->
line.runs.zip(runs) { lineRun, newGlyphs ->
@@ -436,7 +433,7 @@
}
require(newFont === newGlyphs.getFont(i)) {
"The new layout has different font run." +
- " $newFont vs ${newGlyphs.getFont(i)} at $i"
+ " $newFont vs ${newGlyphs.getFont(i)} at $i"
}
}
@@ -444,7 +441,7 @@
// check new font can be interpolatable with base font.
require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
"New font cannot be interpolated with existing font. $newFont," +
- " ${run.baseFont}"
+ " ${run.baseFont}"
}
if (updateBase) {
@@ -480,14 +477,13 @@
}
// Shape the text and stores the result to out argument.
- private fun shapeText(
- layout: Layout,
- paint: TextPaint
- ): List<List<PositionedGlyphs>> {
+ private fun shapeText(layout: Layout, paint: TextPaint): List<List<PositionedGlyphs>> {
+ var text = StringBuilder()
val out = mutableListOf<List<PositionedGlyphs>>()
for (lineNo in 0 until layout.lineCount) { // Shape all lines.
val lineStart = layout.getLineStart(lineNo)
- var count = layout.getLineEnd(lineNo) - lineStart
+ val lineEnd = layout.getLineEnd(lineNo)
+ var count = lineEnd - lineStart
// Do not render the last character in the line if it's a newline and unprintable
val last = lineStart + count - 1
if (last > lineStart && last < layout.text.length && layout.text[last] == '\n') {
@@ -495,19 +491,28 @@
}
val runs = mutableListOf<PositionedGlyphs>()
- TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
- paint) { _, _, glyphs, _ ->
- runs.add(glyphs)
- }
+ TextShaper.shapeText(
+ layout.text,
+ lineStart,
+ count,
+ layout.textDirectionHeuristic,
+ paint
+ ) { _, _, glyphs, _ -> runs.add(glyphs) }
out.add(runs)
+
+ if (lineNo > 0) {
+ text.append("\n")
+ }
+ text.append(layout.text.substring(lineStart, lineEnd))
}
+ shapedText = text.toString()
return out
}
}
private fun Layout.getDrawOrigin(lineNo: Int) =
- if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
- getLineLeft(lineNo)
- } else {
- getLineRight(lineNo)
- }
+ if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
+ getLineLeft(lineNo)
+ } else {
+ getLineRight(lineNo)
+ }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 7f1c78f..e4e9c46 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -178,6 +178,9 @@
/** Flag denoting whether the customizable clocks feature is enabled. */
const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
+ /** Flag denoting whether the Wallpaper preview should use the full screen UI. */
+ const val FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW = "wallpaper_fullscreen_preview"
+
object Columns {
/** String. Unique ID for the flag. */
const val NAME = "name"
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 6d970b3..3d3ccf4 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -1,7 +1,5 @@
+packages/SystemUI
--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
-packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt
-packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index dfb73a9..87b5a4c 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -16,58 +16,13 @@
* limitations under the License.
*/
-->
-<selector
+<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-
- <item android:state_selected="true">
- <layer-list>
- <item
- android:left="3dp"
- android:top="3dp"
- android:right="3dp"
- android:bottom="3dp">
- <!-- We make the shapes a rounded rectangle instead of an oval so that it can animate -->
- <!-- properly into an app/dialog. -->
- <shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface"/>
- <size
- android:width="@dimen/keyguard_affordance_fixed_width"
- android:height="@dimen/keyguard_affordance_fixed_height"/>
- <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
- </shape>
- </item>
-
- <item>
- <shape android:shape="rectangle">
- <stroke
- android:color="@color/control_primary_text"
- android:width="2dp"/>
- <size
- android:width="@dimen/keyguard_affordance_fixed_width"
- android:height="@dimen/keyguard_affordance_fixed_height"/>
- <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
- </shape>
- </item>
- </layer-list>
- </item>
-
- <item>
- <layer-list>
- <item
- android:left="3dp"
- android:top="3dp"
- android:right="3dp"
- android:bottom="3dp">
- <shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface"/>
- <size
- android:width="@dimen/keyguard_affordance_fixed_width"
- android:height="@dimen/keyguard_affordance_fixed_height"/>
- <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
- </shape>
- </item>
- </layer-list>
- </item>
-
-</selector>
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ <size
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
new file mode 100644
index 0000000..acd2462
--- /dev/null
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_selected_border.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 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.
+*/
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_selected="true">
+ <shape android:shape="oval">
+ <stroke
+ android:color="@color/control_primary_text"
+ android:width="2dp"/>
+ <size
+ android:width="@dimen/keyguard_affordance_fixed_width"
+ android:height="@dimen/keyguard_affordance_fixed_height"/>
+ </shape>
+ </item>
+</selector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 6120863..3f95515 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -67,6 +67,7 @@
android:scaleType="center"
android:tint="?android:attr/textColorPrimary"
android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
@@ -79,6 +80,7 @@
android:scaleType="center"
android:tint="?android:attr/textColorPrimary"
android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:foreground="@drawable/keyguard_bottom_affordance_selected_border"
android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index b76de5a..e182a6a 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,6 +24,7 @@
android:orientation="vertical">
<LinearLayout
+ android:id="@+id/media_metadata_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
diff --git a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
index 21d12c2..4483db8 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
@@ -27,6 +27,14 @@
android:layout_height="wrap_content"
/>
+ <com.android.systemui.media.taptotransfer.receiver.ReceiverChipRippleView
+ android:id="@+id/icon_glow_ripple"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <!-- Add a bottom margin to avoid the glow of the icon ripple from being cropped by screen
+ bounds while animating with the icon -->
<com.android.internal.widget.CachingIconView
android:id="@+id/app_icon"
android:background="@drawable/media_ttt_chip_background_receiver"
@@ -34,6 +42,7 @@
android:layout_height="@dimen/media_ttt_icon_size_receiver"
android:layout_gravity="center|bottom"
android:alpha="0.0"
+ android:layout_marginBottom="@dimen/media_ttt_receiver_icon_bottom_margin"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6202939..f122805 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1087,6 +1087,7 @@
(112 - 40) / 2 = 36dp -->
<dimen name="media_ttt_generic_icon_padding">36dp</dimen>
<dimen name="media_ttt_receiver_vert_translation">40dp</dimen>
+ <dimen name="media_ttt_receiver_icon_bottom_margin">10dp</dimen>
<!-- Window magnification -->
<dimen name="magnification_border_drag_size">35dp</dimen>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 8a0fca0..28e786b 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -91,6 +91,9 @@
static_libs: [
"SystemUI-flag-types",
],
+ optimize: {
+ proguard_flags_files: ["proguard_flags.flags"],
+ },
java_version: "1.8",
min_sdk_version: "current",
}
diff --git a/packages/SystemUI/shared/proguard_flags.flags b/packages/SystemUI/shared/proguard_flags.flags
new file mode 100644
index 0000000..08859cd
--- /dev/null
+++ b/packages/SystemUI/shared/proguard_flags.flags
@@ -0,0 +1 @@
+-keep class * implements com.android.systemui.flags.ParcelableFlag
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index fa484c7..a71fb56 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -37,7 +37,7 @@
/**
* Sent when overview is to be shown.
*/
- void onOverviewShown(boolean triggeredFromAltTab, boolean forward) = 7;
+ void onOverviewShown(boolean triggeredFromAltTab) = 7;
/**
* Sent when overview is to be hidden.
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 1454210..fb0c0a6 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -20,7 +20,7 @@
import android.content.res.Configuration
import android.graphics.PixelFormat
import android.os.SystemProperties
-import android.util.DisplayMetrics
+import android.view.Surface
import android.view.View
import android.view.WindowManager
import com.android.internal.annotations.VisibleForTesting
@@ -36,7 +36,6 @@
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
@@ -172,30 +171,28 @@
}
private fun layoutRipple() {
- val displayMetrics = DisplayMetrics()
- context.display.getRealMetrics(displayMetrics)
- val width = displayMetrics.widthPixels
- val height = displayMetrics.heightPixels
+ val bounds = windowManager.currentWindowMetrics.bounds
+ val width = bounds.width()
+ val height = bounds.height()
val maxDiameter = Integer.max(width, height) * 2f
rippleView.setMaxSize(maxDiameter, maxDiameter)
- when (RotationUtils.getExactRotation(context)) {
- RotationUtils.ROTATION_LANDSCAPE -> {
+ when (context.display.rotation) {
+ Surface.ROTATION_0 -> {
+ rippleView.setCenter(
+ width * normalizedPortPosX, height * normalizedPortPosY)
+ }
+ Surface.ROTATION_90 -> {
rippleView.setCenter(
width * normalizedPortPosY, height * (1 - normalizedPortPosX))
}
- RotationUtils.ROTATION_UPSIDE_DOWN -> {
+ Surface.ROTATION_180 -> {
rippleView.setCenter(
width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
}
- RotationUtils.ROTATION_SEASCAPE -> {
+ Surface.ROTATION_270 -> {
rippleView.setCenter(
width * (1 - normalizedPortPosY), height * normalizedPortPosX)
}
- else -> {
- // ROTATION_NONE
- rippleView.setCenter(
- width * normalizedPortPosX, height * normalizedPortPosY)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5d85fc96..848f391 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -104,7 +104,7 @@
unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
- unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+ releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
// TODO(b/263414400): Tracking Bug
@JvmField
@@ -217,6 +217,11 @@
val ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS =
unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = false)
+ // TODO(b/242908637): Tracking Bug
+ @JvmField
+ val WALLPAPER_FULLSCREEN_PREVIEW =
+ unreleasedFlag(227, "wallpaper_fullscreen_preview", teamfood = true)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -230,7 +235,8 @@
// TODO(b/258517050): Clean up after the feature is launched.
@JvmField
- val SMARTSPACE_DATE_WEATHER_DECOUPLED = unreleasedFlag(403, "smartspace_date_weather_decoupled")
+ val SMARTSPACE_DATE_WEATHER_DECOUPLED =
+ sysPropBooleanFlag(403, "persist.sysui.ss.dw_decoupled", default = false)
// 500 - quick settings
@@ -463,6 +469,16 @@
val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
unreleasedFlag(1206, "persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
+ // TODO(b/238475428): Tracking Bug
+ @JvmField
+ val WM_SHADE_ALLOW_BACK_GESTURE =
+ unreleasedFlag(1207, "persist.wm.debug.shade_allow_back_gesture", teamfood = false)
+
+ // TODO(b/238475428): Tracking Bug
+ @JvmField
+ val WM_SHADE_ANIMATE_BACK_GESTURE =
+ unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = true)
+
// 1300 - screenshots
// TODO(b/254513155): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index c219380..9ddc575 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -363,6 +363,10 @@
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
value = featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
),
+ KeyguardPickerFlag(
+ name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW,
+ value = featureFlags.isEnabled(Flags.WALLPAPER_FULLSCREEN_PREVIEW),
+ ),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 5c65c8b..4827a16 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -230,7 +230,14 @@
fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
var anyChanged = false
- colorTransitions.forEach { anyChanged = it.updateColorScheme(colorScheme) || anyChanged }
+ colorTransitions.forEach {
+ val isChanged = it.updateColorScheme(colorScheme)
+
+ // Ignore changes to colorSeamless, since that is expected when toggling dark mode
+ if (it == colorSeamless) return@forEach
+
+ anyChanged = isChanged || anyChanged
+ }
colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
return anyChanged
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a9e1a4d..4803371 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -95,6 +95,7 @@
private RecyclerView mDevicesRecyclerView;
private LinearLayout mDeviceListLayout;
private LinearLayout mCastAppLayout;
+ private LinearLayout mMediaMetadataSectionLayout;
private Button mDoneButton;
private Button mStopButton;
private Button mAppButton;
@@ -240,6 +241,7 @@
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
+ mMediaMetadataSectionLayout = mDialogView.requireViewById(R.id.media_metadata_section);
mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
mDoneButton = mDialogView.requireViewById(R.id.done);
mStopButton = mDialogView.requireViewById(R.id.stop);
@@ -255,21 +257,17 @@
mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter);
mDevicesRecyclerView.setHasFixedSize(false);
- // Init header icon
- mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> {
mMediaOutputController.releaseSession();
dismiss();
});
- mAppButton.setOnClickListener(v -> {
- mBroadcastSender.closeSystemDialogs();
- if (mMediaOutputController.getAppLaunchIntent() != null) {
- mContext.startActivity(mMediaOutputController.getAppLaunchIntent());
- }
- dismiss();
- });
+ mAppButton.setOnClickListener(v -> mMediaOutputController.tryToLaunchMediaApplication());
+ if (mMediaOutputController.isAdvancedLayoutSupported()) {
+ mMediaMetadataSectionLayout.setOnClickListener(
+ v -> mMediaOutputController.tryToLaunchMediaApplication());
+ }
}
@Override
@@ -560,7 +558,7 @@
@Override
public void dismissDialog() {
- dismiss();
+ mBroadcastSender.closeSystemDialogs();
}
void onHeaderIconClick() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 9cf672b..1587e62 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -382,6 +382,15 @@
return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
}
+ void tryToLaunchMediaApplication() {
+ Intent launchIntent = getAppLaunchIntent();
+ if (launchIntent != null) {
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mCallback.dismissDialog();
+ mContext.startActivity(launchIntent);
+ }
+ }
+
CharSequence getHeaderTitle() {
if (mMediaController != null) {
final MediaMetadata metadata = mMediaController.getMetadata();
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 889147b..60dd5da 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
@@ -30,8 +30,8 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
+import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import com.android.internal.widget.CachingIconView
-import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.ui.binder.TintedIconViewBinder
@@ -78,6 +78,7 @@
private val viewUtil: ViewUtil,
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
+ private val rippleController: MediaTttReceiverRippleController,
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger<ChipReceiverInfo>>(
context,
logger,
@@ -114,9 +115,6 @@
}
}
- private var maxRippleWidth: Float = 0f
- private var maxRippleHeight: Float = 0f
-
private fun updateMediaTapToTransferReceiverDisplay(
@StatusBarManager.MediaTransferReceiverState displayState: Int,
routeInfo: MediaRoute2Info,
@@ -201,41 +199,44 @@
val iconView = currentView.getAppIconView()
iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
+ iconView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
TintedIconViewBinder.bind(iconInfo.toTintedIcon(), iconView)
}
override fun animateViewIn(view: ViewGroup) {
val appIconView = view.getAppIconView()
- appIconView.animate()
- .translationYBy(-1 * getTranslationAmount().toFloat())
- .setDuration(ICON_TRANSLATION_ANIM_DURATION)
- .start()
- appIconView.animate()
- .alpha(1f)
- .setDuration(ICON_ALPHA_ANIM_DURATION)
- .start()
- // Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
- appIconView.postOnAnimation { view.requestAccessibilityFocus() }
- expandRipple(view.requireViewById(R.id.ripple))
+ val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
+ val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
+ animateViewTranslationAndFade(appIconView, -1 * getTranslationAmount(), 1f)
+ animateViewTranslationAndFade(iconRippleView, -1 * getTranslationAmount(), 1f)
+ rippleController.expandToInProgressState(rippleView, iconRippleView)
}
override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
val appIconView = view.getAppIconView()
- appIconView.animate()
- .translationYBy(getTranslationAmount().toFloat())
- .setDuration(ICON_TRANSLATION_ANIM_DURATION)
- .start()
- appIconView.animate()
- .alpha(0f)
- .setDuration(ICON_ALPHA_ANIM_DURATION)
- .start()
-
+ val iconRippleView: ReceiverChipRippleView = view.requireViewById(R.id.icon_glow_ripple)
val rippleView: ReceiverChipRippleView = view.requireViewById(R.id.ripple)
if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name &&
mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) {
- expandRippleToFull(rippleView, onAnimationEnd)
+ rippleController.expandToSuccessState(rippleView, onAnimationEnd)
+ animateViewTranslationAndFade(
+ iconRippleView,
+ -1 * getTranslationAmount(),
+ 0f,
+ translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+ alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+ )
+ animateViewTranslationAndFade(
+ appIconView,
+ -1 * getTranslationAmount(),
+ 0f,
+ translationDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+ alphaDuration = ICON_TRANSLATION_SUCCEEDED_DURATION,
+ )
} else {
- rippleView.collapseRipple(onAnimationEnd)
+ rippleController.collapseRipple(rippleView, onAnimationEnd)
+ animateViewTranslationAndFade(iconRippleView, getTranslationAmount(), 0f)
+ animateViewTranslationAndFade(appIconView, getTranslationAmount(), 0f)
}
}
@@ -245,74 +246,41 @@
viewUtil.setRectToViewWindowLocation(view.getAppIconView(), outRect)
}
+ /** Animation of view translation and fading. */
+ private fun animateViewTranslationAndFade(
+ view: View,
+ translationYBy: Float,
+ alphaEndValue: Float,
+ translationDuration: Long = ICON_TRANSLATION_ANIM_DURATION,
+ alphaDuration: Long = ICON_ALPHA_ANIM_DURATION,
+ ) {
+ view.animate()
+ .translationYBy(translationYBy)
+ .setDuration(translationDuration)
+ .start()
+ view.animate()
+ .alpha(alphaEndValue)
+ .setDuration(alphaDuration)
+ .start()
+ }
+
/** Returns the amount that the chip will be translated by in its intro animation. */
- private fun getTranslationAmount(): Int {
- return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
- }
-
- private fun expandRipple(rippleView: ReceiverChipRippleView) {
- if (rippleView.rippleInProgress()) {
- // Skip if ripple is still playing
- return
- }
-
- // In case the device orientation changes, we need to reset the layout.
- rippleView.addOnLayoutChangeListener (
- View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
- if (v == null) return@OnLayoutChangeListener
-
- val layoutChangedRippleView = v as ReceiverChipRippleView
- layoutRipple(layoutChangedRippleView)
- layoutChangedRippleView.invalidate()
- }
- )
- rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
- override fun onViewDetachedFromWindow(view: View?) {}
-
- override fun onViewAttachedToWindow(view: View?) {
- if (view == null) {
- return
- }
- val attachedRippleView = view as ReceiverChipRippleView
- layoutRipple(attachedRippleView)
- attachedRippleView.expandRipple()
- attachedRippleView.removeOnAttachStateChangeListener(this)
- }
- })
- }
-
- private fun layoutRipple(rippleView: ReceiverChipRippleView, isFullScreen: Boolean = false) {
- val windowBounds = windowManager.currentWindowMetrics.bounds
- val height = windowBounds.height().toFloat()
- val width = windowBounds.width().toFloat()
-
- if (isFullScreen) {
- maxRippleHeight = height * 2f
- maxRippleWidth = width * 2f
- } else {
- maxRippleHeight = height / 2f
- maxRippleWidth = width / 2f
- }
- rippleView.setMaxSize(maxRippleWidth, maxRippleHeight)
- // Center the ripple on the bottom of the screen in the middle.
- rippleView.setCenter(width * 0.5f, height)
- val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
- rippleView.setColor(color, 70)
+ private fun getTranslationAmount(): Float {
+ return rippleController.getRippleSize() * 0.5f -
+ rippleController.getReceiverIconSize()
}
private fun View.getAppIconView(): CachingIconView {
return this.requireViewById(R.id.app_icon)
}
- private fun expandRippleToFull(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable?) {
- layoutRipple(rippleView, true)
- rippleView.expandToFull(maxRippleHeight, onAnimationEnd)
+ companion object {
+ private const val ICON_TRANSLATION_ANIM_DURATION = 500L
+ private const val ICON_TRANSLATION_SUCCEEDED_DURATION = 167L
+ private val ICON_ALPHA_ANIM_DURATION = 5.frames
}
}
-val ICON_TRANSLATION_ANIM_DURATION = 30.frames
-val ICON_ALPHA_ANIM_DURATION = 5.frames
-
data class ChipReceiverInfo(
val routeInfo: MediaRoute2Info,
val appIconDrawableOverride: Drawable?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
new file mode 100644
index 0000000..5013802
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverRippleController.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.media.taptotransfer.receiver
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.view.View
+import android.view.WindowManager
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import javax.inject.Inject
+
+/**
+ * A controller responsible for the animation of the ripples shown in media tap-to-transfer on the
+ * receiving device.
+ */
+class MediaTttReceiverRippleController
+@Inject
+constructor(
+ private val context: Context,
+ private val windowManager: WindowManager,
+) {
+
+ private var maxRippleWidth: Float = 0f
+ private var maxRippleHeight: Float = 0f
+
+ /** Expands the icon and main ripple to in-progress state */
+ fun expandToInProgressState(
+ mainRippleView: ReceiverChipRippleView,
+ iconRippleView: ReceiverChipRippleView,
+ ) {
+ expandRipple(mainRippleView, isIconRipple = false)
+ expandRipple(iconRippleView, isIconRipple = true)
+ }
+
+ private fun expandRipple(rippleView: ReceiverChipRippleView, isIconRipple: Boolean) {
+ if (rippleView.rippleInProgress()) {
+ // Skip if ripple is still playing
+ return
+ }
+
+ // In case the device orientation changes, we need to reset the layout.
+ rippleView.addOnLayoutChangeListener(
+ View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
+ if (v == null) return@OnLayoutChangeListener
+
+ val layoutChangedRippleView = v as ReceiverChipRippleView
+ if (isIconRipple) {
+ layoutIconRipple(layoutChangedRippleView)
+ } else {
+ layoutRipple(layoutChangedRippleView)
+ }
+ layoutChangedRippleView.invalidate()
+ }
+ )
+ rippleView.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewDetachedFromWindow(view: View?) {}
+
+ override fun onViewAttachedToWindow(view: View?) {
+ if (view == null) {
+ return
+ }
+ val attachedRippleView = view as ReceiverChipRippleView
+ if (isIconRipple) {
+ layoutIconRipple(attachedRippleView)
+ } else {
+ layoutRipple(attachedRippleView)
+ }
+ attachedRippleView.expandRipple()
+ attachedRippleView.removeOnAttachStateChangeListener(this)
+ }
+ }
+ )
+ }
+
+ /** Expands the ripple to cover the screen. */
+ fun expandToSuccessState(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable?) {
+ layoutRipple(rippleView, isFullScreen = true)
+ rippleView.expandToFull(maxRippleHeight, onAnimationEnd)
+ }
+
+ /** Collapses the ripple. */
+ fun collapseRipple(rippleView: ReceiverChipRippleView, onAnimationEnd: Runnable? = null) {
+ rippleView.collapseRipple(onAnimationEnd)
+ }
+
+ private fun layoutRipple(rippleView: ReceiverChipRippleView, isFullScreen: Boolean = false) {
+ val windowBounds = windowManager.currentWindowMetrics.bounds
+ val height = windowBounds.height().toFloat()
+ val width = windowBounds.width().toFloat()
+
+ if (isFullScreen) {
+ maxRippleHeight = height * 2f
+ maxRippleWidth = width * 2f
+ } else {
+ maxRippleHeight = getRippleSize()
+ maxRippleWidth = getRippleSize()
+ }
+ rippleView.setMaxSize(maxRippleWidth, maxRippleHeight)
+ // Center the ripple on the bottom of the screen in the middle.
+ rippleView.setCenter(width * 0.5f, height)
+ rippleView.setColor(getRippleColor(), RIPPLE_OPACITY)
+ }
+
+ private fun layoutIconRipple(iconRippleView: ReceiverChipRippleView) {
+ val windowBounds = windowManager.currentWindowMetrics.bounds
+ val height = windowBounds.height().toFloat()
+ val width = windowBounds.width().toFloat()
+ val radius = getReceiverIconSize().toFloat()
+
+ iconRippleView.setMaxSize(radius * 0.8f, radius * 0.8f)
+ iconRippleView.setCenter(
+ width * 0.5f,
+ height - getReceiverIconSize() * 0.5f - getReceiverIconBottomMargin()
+ )
+ iconRippleView.setColor(getRippleColor(), RIPPLE_OPACITY)
+ }
+
+ private fun getRippleColor(): Int {
+ var colorStateList =
+ ColorStateList.valueOf(
+ Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
+ )
+ return colorStateList.withLStar(TONE_PERCENT).defaultColor
+ }
+
+ /** Returns the size of the ripple. */
+ internal fun getRippleSize(): Float {
+ return getReceiverIconSize() * 4f
+ }
+
+ /** Returns the size of the icon of the receiver. */
+ internal fun getReceiverIconSize(): Int {
+ return context.resources.getDimensionPixelSize(R.dimen.media_ttt_icon_size_receiver)
+ }
+
+ /** Return the bottom margin of the icon of the receiver. */
+ internal fun getReceiverIconBottomMargin(): Int {
+ // Adding a margin to make sure ripple behind the icon is not cut by the screen bounds.
+ return context.resources.getDimensionPixelSize(
+ R.dimen.media_ttt_receiver_icon_bottom_margin
+ )
+ }
+
+ companion object {
+ const val RIPPLE_OPACITY = 70
+ const val TONE_PERCENT = 95f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 87b2528..f8785fc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -33,14 +33,14 @@
private var isStarted: Boolean
init {
- setupShader(RippleShader.RippleShape.ELLIPSE)
+ setupShader(RippleShader.RippleShape.CIRCLE)
setRippleFill(true)
setSparkleStrength(0f)
- duration = 3000L
isStarted = false
}
fun expandRipple(onAnimationEnd: Runnable? = null) {
+ duration = DEFAULT_DURATION
isStarted = true
super.startRipple(onAnimationEnd)
}
@@ -50,6 +50,7 @@
if (!isStarted) {
return // Ignore if ripple is not started yet.
}
+ duration = DEFAULT_DURATION
// Reset all listeners to animator.
animator.removeAllListeners()
animator.addListener(object : AnimatorListenerAdapter() {
@@ -74,6 +75,7 @@
setRippleFill(false)
val startingPercentage = calculateStartingPercentage(newHeight)
+ animator.duration = EXPAND_TO_FULL_DURATION
animator.addUpdateListener { updateListener ->
val now = updateListener.currentPlayTime
val progress = updateListener.animatedValue as Float
@@ -100,4 +102,9 @@
val remainingPercentage = (1 - ratio).toDouble().pow(1 / 3.toDouble()).toFloat()
return 1 - remainingPercentage
}
+
+ companion object {
+ const val DEFAULT_DURATION = 333L
+ const val EXPAND_TO_FULL_DURATION = 1000L
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 06db5ed..32dee92 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -744,6 +744,7 @@
setWindowVisible(isNavBarWindowVisible());
mView.setBehavior(mBehavior);
setNavBarMode(mNavBarMode);
+ repositionNavigationBar(mCurrentRotation);
mView.setUpdateActiveTouchRegionsCallback(
() -> mOverviewProxyService.onActiveNavBarRegionChanges(
getButtonLocations(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index c335a6d..5ea1c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -59,11 +59,11 @@
}
@Override
- public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+ public void showRecentApps(boolean triggeredFromAltTab) {
IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
if (overviewProxy != null) {
try {
- overviewProxy.onOverviewShown(triggeredFromAltTab, forward);
+ overviewProxy.onOverviewShown(triggeredFromAltTab);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send overview show event to launcher.", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 95d6c18..b041f95 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -65,14 +65,14 @@
}
@Override
- public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+ public void showRecentApps(boolean triggeredFromAltTab) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
return;
}
- mImpl.showRecentApps(triggeredFromAltTab, forward);
+ mImpl.showRecentApps(triggeredFromAltTab);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index 010ceda..8848dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -31,7 +31,7 @@
default void preloadRecentApps() {}
default void cancelPreloadRecentApps() {}
- default void showRecentApps(boolean triggeredFromAltTab, boolean forward) {}
+ default void showRecentApps(boolean triggeredFromAltTab) {}
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
default void toggleRecentApps() {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b532c13..d11f9da 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2097,7 +2097,17 @@
}
}
- public void expandWithoutQs() {
+ /**
+ * Expand shade so that notifications are visible.
+ * Non-split shade: just expanding shade or collapsing QS when they're expanded.
+ * Split shade: only expanding shade, notifications are always visible
+ *
+ * Called when `adb shell cmd statusbar expand-notifications` is executed.
+ */
+ public void expandShadeToNotifications() {
+ if (mSplitShadeEnabled && (isShadeFullyOpen() || isExpanding())) {
+ return;
+ }
if (isQsExpanded()) {
flingSettings(0 /* velocity */, FLING_COLLAPSE);
} else {
@@ -5536,7 +5546,7 @@
@Override
public void flingTopOverscroll(float velocity, boolean open) {
- // in split shade mode we want to expand/collapse QS only when touch happens within QS
+ // in split shade touches affect QS only when touch happens within QS
if (isSplitShadeAndTouchXOutsideQs(mInitialTouchX)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7556750..a0a7586 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,7 +53,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
@@ -228,7 +227,7 @@
*/
default void setImeWindowStatus(int displayId, IBinder token, int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
- default void showRecentApps(boolean triggeredFromAltTab, boolean forward) { }
+ default void showRecentApps(boolean triggeredFromAltTab) { }
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
default void toggleRecentApps() { }
default void toggleSplitScreen() { }
@@ -695,11 +694,11 @@
}
}
- public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+ public void showRecentApps(boolean triggeredFromAltTab) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
- mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, triggeredFromAltTab ? 1 : 0,
- forward ? 1 : 0, null).sendToTarget();
+ mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, triggeredFromAltTab ? 1 : 0, 0,
+ null).sendToTarget();
}
}
@@ -1271,8 +1270,7 @@
public void showMediaOutputSwitcher(String packageName) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- Slog.e(TAG, "Call only allowed from system server.");
- return;
+ throw new SecurityException("Call only allowed from system server.");
}
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
@@ -1407,7 +1405,7 @@
break;
case MSG_SHOW_RECENT_APPS:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showRecentApps(msg.arg1 != 0, msg.arg2 != 0);
+ mCallbacks.get(i).showRecentApps(msg.arg1 != 0);
}
break;
case MSG_HIDE_RECENT_APPS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 76252d0..e996b78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -114,10 +114,10 @@
.onStart { emit(Unit) }
// for each change, lookup the new value
.map {
- secureSettings.getBoolForUser(
+ secureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_CURRENT,
- )
+ ) == 1
}
// perform lookups on the bg thread pool
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 66632e4..856d7de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -218,7 +218,7 @@
return;
}
- mNotificationPanelViewController.expandWithoutQs();
+ mNotificationPanelViewController.expandShadeToNotifications();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 001da6f..f7b8745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1146,7 +1146,7 @@
if (hideImmediately) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
} else {
- mNotificationPanelViewController.expandWithoutQs();
+ mNotificationPanelViewController.expandShadeToNotifications();
}
}
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index f8c17e8..4866f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -303,7 +303,8 @@
mEntry.mRemoteEditImeVisible = editTextRootWindowInsets != null
&& editTextRootWindowInsets.isVisible(WindowInsets.Type.ime());
if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
- mController.removeRemoteInput(mEntry, mToken);
+ // Pass null to ensure all inputs are cleared for this entry b/227115380
+ mController.removeRemoteInput(mEntry, null);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 8d5e01c..9050dad 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -29,13 +29,14 @@
import android.os.Handler
import android.os.UserHandle
import android.util.Log
-import android.view.InputDevice
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.hardware.hasInputDevice
+import com.android.systemui.shared.hardware.isAnyStylusSource
import com.android.systemui.util.NotificationChannels
import java.text.NumberFormat
import javax.inject.Inject
@@ -150,10 +151,7 @@
}
private fun hasConnectedBluetoothStylus(): Boolean {
- // TODO(b/257936830): get bt address once input api available
- return inputManager.inputDeviceIds.any { deviceId ->
- inputManager.getInputDevice(deviceId).supportsSource(InputDevice.SOURCE_STYLUS)
- }
+ return inputManager.hasInputDevice { it.isAnyStylusSource && it.bluetoothAddress != null }
}
private fun getPendingBroadcast(action: String): PendingIntent? {
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
index 0b2f004..31f35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -1,15 +1,17 @@
/*
* 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
+ * 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.
+ * 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.util.leak;
@@ -26,7 +28,27 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-public class RotationUtils {
+/**
+ * Utility class that provides device orientation.
+ *
+ * <p>Consider using {@link Surface.Rotation} or add a function that respects device aspect ratio
+ * and {@code android.internal.R.bool.config_reverseDefaultRotation}.
+ *
+ * <p>If you only care about the rotation, use {@link Surface.Rotation}, as it always gives the
+ * counter clock-wise rotation. (e.g. If you have a device that has a charging port at the bottom,
+ * rotating three times in counter clock direction will give you {@link Surface#ROTATION_270} while
+ * having the charging port on the left side of the device.)
+ *
+ * <p>If you need whether the device is in portrait or landscape (or their opposites), please add a
+ * function here that respects the device aspect ratio and
+ * {@code android.internal.R.bool.config_reverseDefaultRotation} together.
+ *
+ * <p>Note that {@code android.internal.R.bool.config_reverseDefaultRotation} does not change the
+ * winding order. In other words, the rotation order (counter clock-wise) will remain the same. It
+ * only flips the device orientation, such that portrait becomes upside down, landscape becomes
+ * seascape.
+ */
+public final class RotationUtils {
public static final int ROTATION_NONE = 0;
public static final int ROTATION_LANDSCAPE = 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index d159714..d6cafcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -16,18 +16,23 @@
package com.android.systemui.charging
+import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.view.Surface
import android.view.View
import android.view.WindowManager
+import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.ripple.RippleView
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
@@ -35,12 +40,12 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -54,6 +59,7 @@
@Mock private lateinit var rippleView: RippleView
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var windowMetrics: WindowMetrics
private val systemClock = FakeSystemClock()
@Before
@@ -66,6 +72,9 @@
rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
+
+ `when`(windowMetrics.bounds).thenReturn(Rect(0, 0, 100, 100))
+ `when`(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
}
@Test
@@ -164,4 +173,63 @@
verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture())
verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>())
}
+
+ @Test
+ fun testRipple_layoutsCorrectly() {
+ // Sets the correct ripple size.
+ val width = 100
+ val height = 200
+ whenever(windowMetrics.bounds).thenReturn(Rect(0, 0, width, height))
+
+ // Trigger ripple.
+ val captor = ArgumentCaptor
+ .forClass(BatteryController.BatteryStateChangeCallback::class.java)
+ verify(batteryController).addCallback(captor.capture())
+
+ captor.value.onBatteryLevelChanged(
+ /* unusedBatteryLevel= */ 0,
+ /* plugged in= */ true,
+ /* charging= */ false)
+
+ val attachListenerCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+ verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
+ verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>())
+
+ val runnableCaptor =
+ ArgumentCaptor.forClass(Runnable::class.java)
+ attachListenerCaptor.value.onViewAttachedToWindow(rippleView)
+ verify(rippleView).startRipple(runnableCaptor.capture())
+
+ // Verify size and center position.
+ val maxSize = 400f // Double the max value between width and height.
+ verify(rippleView).setMaxSize(maxWidth = maxSize, maxHeight = maxSize)
+
+ val normalizedPortPosX =
+ context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_x)
+ val normalizedPortPosY =
+ context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_y)
+ val expectedCenterX: Float
+ val expectedCenterY: Float
+ when (context.display.rotation) {
+ Surface.ROTATION_90 -> {
+ expectedCenterX = width * normalizedPortPosY
+ expectedCenterY = height * (1 - normalizedPortPosX)
+ }
+ Surface.ROTATION_180 -> {
+ expectedCenterX = width * (1 - normalizedPortPosX)
+ expectedCenterY = height * (1 - normalizedPortPosY)
+ }
+ Surface.ROTATION_270 -> {
+ expectedCenterX = width * (1 - normalizedPortPosY)
+ expectedCenterY = height * normalizedPortPosX
+ }
+ else -> { // Surface.ROTATION_0
+ expectedCenterX = width * normalizedPortPosX
+ expectedCenterY = height * normalizedPortPosY
+ }
+ }
+
+ verify(rippleView).setCenter(expectedCenterX, expectedCenterY)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 0a03b2c..c0af0cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -173,6 +173,7 @@
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
set(Flags.REVAMPED_WALLPAPER_UI, true)
+ set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
},
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 094d69a..9a0bd9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -243,6 +243,13 @@
}
@Test
+ public void dismissDialog_closesDialogByBroadcastSender() {
+ mMediaOutputBaseDialogImpl.dismissDialog();
+
+ verify(mBroadcastSender).closeSystemDialogs();
+ }
+
+ @Test
public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index f5432e2..117751c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -252,6 +252,13 @@
}
@Test
+ public void tryToLaunchMediaApplication_nullIntent_skip() {
+ mMediaOutputController.tryToLaunchMediaApplication();
+
+ verify(mCb, never()).dismissDialog();
+ }
+
+ @Test
public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException {
mMediaOutputController.start(mCb);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index 9c4e849..b3e621e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -48,6 +48,7 @@
viewUtil: ViewUtil,
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
+ rippleController: MediaTttReceiverRippleController,
) :
MediaTttChipControllerReceiver(
commandQueue,
@@ -65,6 +66,7 @@
viewUtil,
wakeLockBuilder,
systemClock,
+ rippleController,
) {
override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
// Just bypass the animation in tests
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index cefc742..5e40898 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -85,6 +85,8 @@
private lateinit var windowManager: WindowManager
@Mock
private lateinit var commandQueue: CommandQueue
+ @Mock
+ private lateinit var rippleController: MediaTttReceiverRippleController
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -134,6 +136,7 @@
viewUtil,
fakeWakeLockBuilder,
fakeClock,
+ rippleController,
)
controllerReceiver.start()
@@ -163,6 +166,7 @@
viewUtil,
fakeWakeLockBuilder,
fakeClock,
+ rippleController,
)
controllerReceiver.start()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index fc7cd89..0000c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -209,9 +209,9 @@
@Test
public void testShowRecentApps() {
- mCommandQueue.showRecentApps(true, false);
+ mCommandQueue.showRecentApps(true);
waitForIdleSync();
- verify(mCallbacks).showRecentApps(eq(true), eq(false));
+ verify(mCallbacks).showRecentApps(eq(true));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index be6b1dc..2686238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -321,7 +321,7 @@
val testDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
val fakeSettings = FakeSettings().apply {
- putBool(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, true)
+ putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
}
val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator =
@@ -372,14 +372,14 @@
var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
get() =
- fakeSettings.getBoolForUser(
+ fakeSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_CURRENT,
- )
+ ) == 1
set(value) {
- fakeSettings.putBoolForUser(
+ fakeSettings.putIntForUser(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- value,
+ if (value) 1 else 2,
UserHandle.USER_CURRENT,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 6fc60f1..52bd424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -147,7 +147,7 @@
// Trying to open it does nothing.
mSbcqCallbacks.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController, never()).expandWithoutQs();
+ verify(mNotificationPanelViewController, never()).expandShadeToNotifications();
mSbcqCallbacks.animateExpandSettingsPanel(null);
verify(mNotificationPanelViewController, never()).expand(anyBoolean());
}
@@ -165,7 +165,7 @@
// Can now be opened.
mSbcqCallbacks.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController).expandWithoutQs();
+ verify(mNotificationPanelViewController).expandShadeToNotifications();
mSbcqCallbacks.animateExpandSettingsPanel(null);
verify(mNotificationPanelViewController).expandWithQs();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index 1e81dc7..e1668e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -36,7 +36,6 @@
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -79,7 +78,7 @@
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf())
whenever(inputManager.getInputDevice(0)).thenReturn(btStylusDevice)
whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
- // whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
+ whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
stylusUsiPowerUi = StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler)
broadcastReceiver = stylusUsiPowerUi.receiver
@@ -179,7 +178,6 @@
}
@Test
- @Ignore("TODO(b/257936830): get bt address once input api available")
fun refresh_hasConnectedBluetoothStylus_cancelsNotification() {
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
@@ -189,7 +187,6 @@
}
@Test
- @Ignore("TODO(b/257936830): get bt address once input api available")
fun refresh_hasConnectedBluetoothStylus_existingNotification_cancelsNotification() {
stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(0))
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
new file mode 100644
index 0000000..56e777f
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
@@ -0,0 +1,25 @@
+/*
+ * 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.companion.datatransfer.contextsync;
+
+/** Callback for call metadata syncing. */
+public abstract class CallMetadataSyncCallback {
+
+ abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
+
+ abstract void requestCrossDeviceSync(int userId);
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
new file mode 100644
index 0000000..077fd2a
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
@@ -0,0 +1,252 @@
+/*
+ * 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.companion.datatransfer.contextsync;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.telecom.Call;
+import android.telecom.CallAudioState;
+import android.telecom.VideoProfile;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/** Data holder for a telecom call and additional metadata. */
+public class CrossDeviceCall {
+
+ private static final String TAG = "CrossDeviceCall";
+
+ private static final int APP_ICON_BITMAP_DIMENSION = 256;
+
+ private static final AtomicLong sNextId = new AtomicLong(1);
+
+ private final long mId;
+ private final Call mCall;
+ @VisibleForTesting boolean mIsEnterprise;
+ @VisibleForTesting boolean mIsOtt;
+ private String mCallingAppName;
+ private byte[] mCallingAppIcon;
+ private String mCallerDisplayName;
+ private int mStatus = android.companion.Telecom.Call.UNKNOWN_STATUS;
+ private String mContactDisplayName;
+ private boolean mIsMuted;
+ private final Set<Integer> mControls = new HashSet<>();
+
+ public CrossDeviceCall(PackageManager packageManager, Call call,
+ CallAudioState callAudioState) {
+ mId = sNextId.getAndIncrement();
+ mCall = call;
+ final String callingAppPackageName = call != null
+ ? call.getDetails().getAccountHandle().getComponentName().getPackageName() : null;
+ mIsOtt = call != null
+ && (call.getDetails().getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
+ == Call.Details.PROPERTY_SELF_MANAGED;
+ mIsEnterprise = call != null
+ && (call.getDetails().getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL)
+ == Call.Details.PROPERTY_ENTERPRISE_CALL;
+ try {
+ final ApplicationInfo applicationInfo = packageManager
+ .getApplicationInfo(callingAppPackageName,
+ PackageManager.ApplicationInfoFlags.of(0));
+ mCallingAppName = packageManager.getApplicationLabel(applicationInfo).toString();
+ mCallingAppIcon = renderDrawableToByteArray(
+ packageManager.getApplicationIcon(applicationInfo));
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Could not get application info for package " + callingAppPackageName, e);
+ }
+ mIsMuted = callAudioState != null && callAudioState.isMuted();
+ if (call != null) {
+ updateCallDetails(call.getDetails());
+ }
+ }
+
+ private byte[] renderDrawableToByteArray(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ // Can't recycle the drawable's bitmap, so handle separately
+ final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ if (bitmap.getWidth() > APP_ICON_BITMAP_DIMENSION
+ || bitmap.getHeight() > APP_ICON_BITMAP_DIMENSION) {
+ // Downscale, as the original drawable bitmap is too large.
+ final Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
+ APP_ICON_BITMAP_DIMENSION, APP_ICON_BITMAP_DIMENSION, /* filter= */ true);
+ final byte[] renderedBitmap = renderBitmapToByteArray(scaledBitmap);
+ scaledBitmap.recycle();
+ return renderedBitmap;
+ }
+ return renderBitmapToByteArray(bitmap);
+ }
+ final Bitmap bitmap = Bitmap.createBitmap(APP_ICON_BITMAP_DIMENSION,
+ APP_ICON_BITMAP_DIMENSION,
+ Bitmap.Config.ARGB_8888);
+ try {
+ final Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ drawable.draw(canvas);
+ } finally {
+ bitmap.recycle();
+ }
+ return renderBitmapToByteArray(bitmap);
+ }
+
+ private byte[] renderBitmapToByteArray(Bitmap bitmap) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream(bitmap.getByteCount());
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+ return baos.toByteArray();
+ }
+
+ /**
+ * Update the mute state of this call. No-op if the call is not capable of being muted.
+ *
+ * @param isMuted true if the call should be muted, and false if the call should be unmuted.
+ */
+ public void updateMuted(boolean isMuted) {
+ mIsMuted = isMuted;
+ updateCallDetails(mCall.getDetails());
+ }
+
+ /**
+ * Update the state of the call to be ringing silently if it is currently ringing. No-op if the
+ * call is not
+ * currently ringing.
+ */
+ public void updateSilencedIfRinging() {
+ if (mStatus == android.companion.Telecom.Call.RINGING) {
+ mStatus = android.companion.Telecom.Call.RINGING_SILENCED;
+ }
+ mControls.remove(android.companion.Telecom.Call.SILENCE);
+ }
+
+ @VisibleForTesting
+ void updateCallDetails(Call.Details callDetails) {
+ mCallerDisplayName = callDetails.getCallerDisplayName();
+ mContactDisplayName = callDetails.getContactDisplayName();
+ mStatus = convertStateToStatus(callDetails.getState());
+ mControls.clear();
+ if (mStatus == android.companion.Telecom.Call.RINGING
+ || mStatus == android.companion.Telecom.Call.RINGING_SILENCED) {
+ mControls.add(android.companion.Telecom.Call.ACCEPT);
+ mControls.add(android.companion.Telecom.Call.REJECT);
+ if (mStatus == android.companion.Telecom.Call.RINGING) {
+ mControls.add(android.companion.Telecom.Call.SILENCE);
+ }
+ }
+ if (mStatus == android.companion.Telecom.Call.ONGOING
+ || mStatus == android.companion.Telecom.Call.ON_HOLD) {
+ mControls.add(android.companion.Telecom.Call.END);
+ if (callDetails.can(Call.Details.CAPABILITY_HOLD)) {
+ mControls.add(
+ mStatus == android.companion.Telecom.Call.ON_HOLD
+ ? android.companion.Telecom.Call.TAKE_OFF_HOLD
+ : android.companion.Telecom.Call.PUT_ON_HOLD);
+ }
+ }
+ if (mStatus == android.companion.Telecom.Call.ONGOING && callDetails.can(
+ Call.Details.CAPABILITY_MUTE)) {
+ mControls.add(mIsMuted ? android.companion.Telecom.Call.UNMUTE
+ : android.companion.Telecom.Call.MUTE);
+ }
+ }
+
+ private int convertStateToStatus(int callState) {
+ switch (callState) {
+ case Call.STATE_HOLDING:
+ return android.companion.Telecom.Call.ON_HOLD;
+ case Call.STATE_ACTIVE:
+ return android.companion.Telecom.Call.ONGOING;
+ case Call.STATE_RINGING:
+ return android.companion.Telecom.Call.RINGING;
+ case Call.STATE_NEW:
+ case Call.STATE_DIALING:
+ case Call.STATE_DISCONNECTED:
+ case Call.STATE_SELECT_PHONE_ACCOUNT:
+ case Call.STATE_CONNECTING:
+ case Call.STATE_DISCONNECTING:
+ case Call.STATE_PULLING_CALL:
+ case Call.STATE_AUDIO_PROCESSING:
+ case Call.STATE_SIMULATED_RINGING:
+ default:
+ return android.companion.Telecom.Call.UNKNOWN_STATUS;
+ }
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public Call getCall() {
+ return mCall;
+ }
+
+ public String getCallingAppName() {
+ return mCallingAppName;
+ }
+
+ public byte[] getCallingAppIcon() {
+ return mCallingAppIcon;
+ }
+
+ /**
+ * Get a human-readable "caller id" to display as the origin of the call.
+ *
+ * @param isAdminBlocked whether there is an admin that has blocked contacts over Bluetooth
+ */
+ public String getReadableCallerId(boolean isAdminBlocked) {
+ if (mIsOtt) {
+ return mCallerDisplayName;
+ }
+ return mIsEnterprise && isAdminBlocked ? mCallerDisplayName : mContactDisplayName;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public Set<Integer> getControls() {
+ return mControls;
+ }
+
+ void doAccept() {
+ mCall.answer(VideoProfile.STATE_AUDIO_ONLY);
+ }
+
+ void doReject() {
+ if (mStatus == android.companion.Telecom.Call.RINGING) {
+ mCall.reject(Call.REJECT_REASON_DECLINED);
+ }
+ }
+
+ void doEnd() {
+ mCall.disconnect();
+ }
+
+ void doPutOnHold() {
+ mCall.hold();
+ }
+
+ void doTakeOffHold() {
+ mCall.unhold();
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 47ec80e..2395814c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -176,6 +176,11 @@
@VisibleForTesting
void notifyRunningAppsChanged(int deviceId, ArraySet<Integer> uids) {
synchronized (mVirtualDeviceManagerLock) {
+ if (!mVirtualDevices.contains(deviceId)) {
+ Slog.e(TAG, "notifyRunningAppsChanged called for unknown deviceId:" + deviceId
+ + " (maybe it was recently closed?)");
+ return;
+ }
mAppsOnVirtualDevices.put(deviceId, uids);
}
mLocalService.onAppsOnVirtualDeviceChanged();
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index a35aa7c..70eeb7f 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -329,11 +329,11 @@
* when the user is first unlocked to update the usage stats package mappings data that might
* be stale or have existed from a restore and belongs to packages that are not installed for
* this user anymore.
- * Note: this is only executed for the system user.
*
+ * @param userId The user to update
* @return {@code true} if the updating was successful, {@code false} otherwise
*/
- public abstract boolean updatePackageMappingsData();
+ public abstract boolean updatePackageMappingsData(@UserIdInt int userId);
/**
* Listener interface for usage events.
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index ff75796..4854c37 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -321,6 +321,7 @@
private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
+ private final ArraySet<String> mAutomaticRollbackDenylistedPackages = new ArraySet<>();
private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
// A map from package name of vendor APEXes that can be updated to an installer package name
// allowed to install updates for it.
@@ -461,6 +462,10 @@
return mRollbackWhitelistedPackages;
}
+ public Set<String> getAutomaticRollbackDenylistedPackages() {
+ return mAutomaticRollbackDenylistedPackages;
+ }
+
public Set<String> getWhitelistedStagedInstallers() {
return mWhitelistedStagedInstallers;
}
@@ -1356,6 +1361,16 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "automatic-rollback-denylisted-app": {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mAutomaticRollbackDenylistedPackages.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
case "whitelisted-staged-installer": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7b8ca91..4504f91 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2250,14 +2250,19 @@
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
+ boolean preciseCallStateChanged = false;
mRingingCallState[phoneId] = ringingCallState;
mForegroundCallState[phoneId] = foregroundCallState;
mBackgroundCallState[phoneId] = backgroundCallState;
- mPreciseCallState[phoneId] = new PreciseCallState(
+ PreciseCallState preciseCallState = new PreciseCallState(
ringingCallState, foregroundCallState,
backgroundCallState,
DisconnectCause.NOT_VALID,
PreciseDisconnectCause.NOT_VALID);
+ if (!preciseCallState.equals(mPreciseCallState[phoneId])) {
+ preciseCallStateChanged = true;
+ mPreciseCallState[phoneId] = preciseCallState;
+ }
boolean notifyCallState = true;
if (mCallQuality == null) {
log("notifyPreciseCallState: mCallQuality is null, "
@@ -2271,6 +2276,8 @@
mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
mCallQuality[phoneId] = createCallQuality();
}
+ List<CallState> prevCallStateList = new ArrayList<>();
+ prevCallStateList.addAll(mCallStateLists.get(phoneId));
mCallStateLists.get(phoneId).clear();
if (foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_NOT_VALID
&& foregroundCallState != PreciseCallState.PRECISE_CALL_STATE_IDLE) {
@@ -2330,6 +2337,9 @@
}
mCallStateLists.get(phoneId).add(builder.build());
}
+ if (prevCallStateList.equals(mCallStateLists.get(phoneId))) {
+ notifyCallState = false;
+ }
boolean hasOngoingCall = false;
for (CallState cs : mCallStateLists.get(phoneId)) {
if (cs.getCallState() != PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED) {
@@ -2343,23 +2353,30 @@
}
}
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
+ if (preciseCallStateChanged) {
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
- if (notifyCallState && r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
+ }
+
+ if (notifyCallState) {
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCallStatesChanged(mCallStateLists.get(phoneId));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
}
@@ -3976,66 +3993,6 @@
}
}
- /**
- * Returns a string representation of the radio technology (network type)
- * currently in use on the device.
- * @param type for which network type is returned
- * @return the name of the radio technology
- *
- */
- private String getNetworkTypeName(@Annotation.NetworkType int type) {
- switch (type) {
- case TelephonyManager.NETWORK_TYPE_GPRS:
- return "GPRS";
- case TelephonyManager.NETWORK_TYPE_EDGE:
- return "EDGE";
- case TelephonyManager.NETWORK_TYPE_UMTS:
- return "UMTS";
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- return "HSDPA";
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- return "HSUPA";
- case TelephonyManager.NETWORK_TYPE_HSPA:
- return "HSPA";
- case TelephonyManager.NETWORK_TYPE_CDMA:
- return "CDMA";
- case TelephonyManager.NETWORK_TYPE_EVDO_0:
- return "CDMA - EvDo rev. 0";
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- return "CDMA - EvDo rev. A";
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- return "CDMA - EvDo rev. B";
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- return "CDMA - 1xRTT";
- case TelephonyManager.NETWORK_TYPE_LTE:
- return "LTE";
- case TelephonyManager.NETWORK_TYPE_EHRPD:
- return "CDMA - eHRPD";
- case TelephonyManager.NETWORK_TYPE_IDEN:
- return "iDEN";
- case TelephonyManager.NETWORK_TYPE_HSPAP:
- return "HSPA+";
- case TelephonyManager.NETWORK_TYPE_GSM:
- return "GSM";
- case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
- return "TD_SCDMA";
- case TelephonyManager.NETWORK_TYPE_IWLAN:
- return "IWLAN";
-
- //TODO: This network type is marked as hidden because it is not a
- // true network type and we are looking to remove it completely from the available list
- // of network types. Since this method is only used for logging, in the event that this
- // network type is selected, the log will read as "Unknown."
- //case TelephonyManager.NETWORK_TYPE_LTE_CA:
- // return "LTE_CA";
-
- case TelephonyManager.NETWORK_TYPE_NR:
- return "NR";
- default:
- return "UNKNOWN";
- }
- }
-
/** Returns a new PreciseCallState object with default values. */
private static PreciseCallState createPreciseCallState() {
return new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_NOT_VALID,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 41437d1..73bb8d7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -206,6 +206,7 @@
import com.android.server.SystemService;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.LowMemDetector.MemFactor;
+import com.android.server.am.ServiceRecord.ShortFgsInfo;
import com.android.server.pm.KnownPackages;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -1985,7 +1986,7 @@
foregroundServiceType == FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
final boolean isOldTypeShortFgsAndTimedOut = r.shouldTriggerShortFgsTimeout();
- if (isOldTypeShortFgs || isNewTypeShortFgs) {
+ if (r.isForeground && (isOldTypeShortFgs || isNewTypeShortFgs)) {
if (DEBUG_SHORT_SERVICE) {
Slog.i(TAG_SERVICE, String.format(
"FGS type changing from %x%s to %x: %s",
@@ -2021,10 +2022,8 @@
} else {
// FGS type is changing from SHORT_SERVICE to another type when
// an app is allowed to start FGS, so this will succeed.
- // The timeout will stop -- we actually don't cancel the handler
- // events, but they'll be ignored if the service type is not
- // SHORT_SERVICE.
- // TODO(short-service) Let's actaully cancel the handler events.
+ // The timeout will stop later, in
+ // maybeUpdateShortFgsTrackingLocked().
}
} else {
// We catch this case later, in the
@@ -2226,7 +2225,7 @@
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
- maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(r,
+ maybeUpdateShortFgsTrackingLocked(r,
extendShortServiceTimeout);
} else {
if (DEBUG_FOREGROUND_SERVICE) {
@@ -3022,11 +3021,17 @@
}
/**
- * If {@code sr} is of a short-fgs, start a short-FGS timeout.
+ * Update a {@link ServiceRecord}'s {@link ShortFgsInfo} as needed, and also start
+ * a timeout as needed.
+ *
+ * If the {@link ServiceRecord} is not a short-FGS, then we'll stop the timeout and clear
+ * the {@link ShortFgsInfo}.
*/
- private void maybeStartShortFgsTimeoutAndUpdateShortFgsInfoLocked(ServiceRecord sr,
+ private void maybeUpdateShortFgsTrackingLocked(ServiceRecord sr,
boolean extendTimeout) {
if (!sr.isShortFgs()) {
+ sr.clearShortFgsInfo(); // Just in case we have it.
+ unscheduleShortFgsTimeoutLocked(sr);
return;
}
if (DEBUG_SHORT_SERVICE) {
@@ -3035,29 +3040,32 @@
if (extendTimeout || !sr.hasShortFgsInfo()) {
sr.setShortFgsInfo(SystemClock.uptimeMillis());
+
+ // We'll restart the timeout.
+ unscheduleShortFgsTimeoutLocked(sr);
+
+ final Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr);
+ mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getTimeoutTime());
} else {
// We only (potentially) update the start command, start count, but not the timeout
// time.
+ // In this case, we keep the existing timeout running.
sr.getShortFgsInfo().update();
}
- unscheduleShortFgsTimeoutLocked(sr); // Do it just in case
-
- final Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr);
- mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getTimeoutTime());
}
/**
* Stop the timeout for a ServiceRecord, if it's of a short-FGS.
*/
private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) {
+ sr.clearShortFgsInfo(); // Always clear, just in case.
if (!sr.isShortFgs()) {
return;
}
if (DEBUG_SHORT_SERVICE) {
Slog.i(TAG_SERVICE, "Stop short FGS timeout: " + sr);
}
- sr.clearShortFgsInfo();
unscheduleShortFgsTimeoutLocked(sr);
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f54e2b0..410bc41 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -118,7 +118,7 @@
private static final int FREEZE_BINDER_TIMEOUT_MS = 100;
// Defaults for phenotype flags.
- @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
+ @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = true;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
@VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_ALL;
@VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 85de637..8d3c21e 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -57,6 +57,7 @@
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -87,7 +88,6 @@
import com.android.server.LocalServices;
import com.android.server.RescueParty;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerService;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.sdksandbox.SdkSandboxManagerLocal;
@@ -186,7 +186,7 @@
checkTime(startTime, "getContentProviderImpl: getProviderByName");
- UserManagerService userManagerService = UserManagerService.getInstance();
+ UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
/*
For clone user profile and allowed authority, skipping finding provider and redirecting
@@ -196,8 +196,10 @@
used and redirect to owner user's MediaProvider.
*/
//todo(b/236121588) MediaProvider should not be installed in clone profile.
- if (!isAuthorityRedirectedForCloneProfile(name)
- || !userManagerService.isMediaSharedWithParent(userId)) {
+ final UserProperties userProps = umInternal.getUserProperties(userId);
+ final boolean isMediaSharedWithParent =
+ userProps != null && userProps.isMediaSharedWithParent();
+ if (!isAuthorityRedirectedForCloneProfile(name) || !isMediaSharedWithParent) {
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
}
@@ -215,9 +217,7 @@
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else if (isAuthorityRedirectedForCloneProfile(name)) {
- if (userManagerService.isMediaSharedWithParent(userId)) {
- UserManagerInternal umInternal = LocalServices.getService(
- UserManagerInternal.class);
+ if (isMediaSharedWithParent) {
userId = umInternal.getProfileParentId(userId);
checkCrossUser = false;
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 43075bc..14ecf9f 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -32,6 +32,7 @@
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@@ -65,6 +66,7 @@
/** If enabled BAL are prevented by default in applications targeting U and later. */
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @Overridable
private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER = 244637991;
private static final String ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER =
"enable_default_rescind_bal_privileges_from_pending_intent_sender";
@@ -450,16 +452,11 @@
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
- // to ensure that we can launch the pending intent with a consistent launch mode even
- // if the provided PendingIntent is immutable (ie. to force an activity to launch into
- // a new task, or to launch multiple instances if supported by the app)
+ // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
+ // can specify a consistent launch mode even if the PendingIntent is immutable
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- // TODO(b/254490217): Move this check into SafeActivityOptions
- if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
- }
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 9bb63d3..58a47d7 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -49,6 +49,7 @@
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.expresslog.Counter;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.anr.AnrLatencyTracker;
@@ -260,6 +261,28 @@
mDialogController = new ErrorDialogController(app);
}
+ @GuardedBy("mService")
+ boolean skipAnrLocked(String annotation) {
+ // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+ if (mService.mAtmInternal.isShuttingDown()) {
+ Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+ return true;
+ } else if (isNotResponding()) {
+ Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+ return true;
+ } else if (isCrashing()) {
+ Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+ return true;
+ } else if (mApp.isKilledByAm()) {
+ Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+ return true;
+ } else if (mApp.isKilled()) {
+ Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+ return true;
+ }
+ return false;
+ }
+
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, TimeoutRecord timeoutRecord,
@@ -303,26 +326,10 @@
// Store annotation here as instance above will not be hit on all paths.
setAnrAnnotation(annotation);
- // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
- if (mService.mAtmInternal.isShuttingDown()) {
- Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+ Counter.logIncrement("stability_anr.value_total_anrs");
+ if (skipAnrLocked(annotation)) {
latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
- return;
- } else if (isNotResponding()) {
- Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
- latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
- return;
- } else if (isCrashing()) {
- Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
- latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
- return;
- } else if (mApp.isKilledByAm()) {
- Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
- latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
- return;
- } else if (mApp.isKilled()) {
- Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
- latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
+ Counter.logIncrement("stability_anr.value_skipped_anrs");
return;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 67166b8..8ce9889 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1457,8 +1457,7 @@
}
private boolean shouldStartWithParent(UserInfo user) {
- final UserProperties properties = mInjector.getUserManagerInternal()
- .getUserProperties(user.id);
+ final UserProperties properties = getUserProperties(user.id);
return (properties != null && properties.getStartWithParent())
&& !user.isQuietModeEnabled();
}
@@ -2773,6 +2772,10 @@
return mInjector.getUserManager().getUserInfo(userId);
}
+ private @Nullable UserProperties getUserProperties(@UserIdInt int userId) {
+ return mInjector.getUserManagerInternal().getUserProperties(userId);
+ }
+
int[] getUserIds() {
return mInjector.getUserManager().getUserIds();
}
@@ -2903,7 +2906,8 @@
if (getStartedUserState(userId) == null) {
return false;
}
- if (!mInjector.getUserManager().isCredentialSharableWithParent(userId)) {
+ final UserProperties properties = getUserProperties(userId);
+ if (properties == null || !properties.isCredentialShareableWithParent()) {
return false;
}
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index b18be3c..08dcccf 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -86,7 +86,7 @@
private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER,
- ACCOUNT_MANAGER_HELPER);
+ ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER);
private int mUserId = UserHandle.USER_SYSTEM;
@@ -100,7 +100,7 @@
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(mUserId));
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId));
addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId));
- addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+ addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(mUserId));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper(mUserId));
addHelper(SLICES_HELPER, new SliceBackupHelper(this));
diff --git a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
index d6a70d3..4098c1a 100644
--- a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
+++ b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
@@ -1,9 +1,9 @@
package com.android.server.backup;
+import android.annotation.UserIdInt;
import android.app.backup.BlobBackupHelper;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.Context;
import android.os.UserHandle;
import android.util.Log;
@@ -26,8 +26,16 @@
// same as UsageStatsDatabase.KEY_USAGE_STATS
static final String KEY_USAGE_STATS = "usage_stats";
- public UsageStatsBackupHelper(Context context) {
+ private final @UserIdInt int mUserId;
+
+ /**
+ * Marshall/unmarshall the usagestats data for the given user
+ *
+ * @param userId The userId to backup/restore
+ */
+ public UsageStatsBackupHelper(@UserIdInt int userId) {
super(BLOB_VERSION, KEY_USAGE_STATS);
+ mUserId = userId;
}
@Override
@@ -38,8 +46,11 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
try {
+ // Note: Write 0 here deliberately so that a backup from a secondary user
+ // can still be restored to an older OS where the restore was always to user 0
+ // Writing the actual userId here would result in restores not working on pre-U.
out.writeInt(UserHandle.USER_SYSTEM);
- out.write(localUsageStatsManager.getBackupPayload(UserHandle.USER_SYSTEM, key));
+ out.write(localUsageStatsManager.getBackupPayload(mUserId, key));
} catch (IOException ioe) {
if (DEBUG) Log.e(TAG, "Failed to backup Usage Stats", ioe);
baos.reset();
@@ -49,7 +60,6 @@
return null;
}
-
@Override
protected void applyRestoredPayload(String key, byte[] payload) {
if (KEY_USAGE_STATS.equals(key)) {
@@ -57,10 +67,10 @@
LocalServices.getService(UsageStatsManagerInternal.class);
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
try {
- int user = in.readInt();
+ in.readInt(); // Legacy userId parameter, read and ignore
byte[] restoreData = new byte[payload.length - 4];
in.read(restoreData, 0, restoreData.length);
- localUsageStatsManager.applyRestoredPayload(user, key, restoreData);
+ localUsageStatsManager.applyRestoredPayload(mUserId, key, restoreData);
} catch (IOException ioe) {
if (DEBUG) Log.e(TAG, "Failed to restore Usage Stats", ioe);
}
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 7448611..4a9b562 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -96,7 +96,11 @@
mListeners.remove(l);
}
- void setBrightness(float brightness) {
+ /**
+ * Sets the brigtness and broadcasts the change to the listeners.
+ * @param brightness The value to which the brightness is to be set.
+ */
+ public void setBrightness(float brightness) {
if (Float.isNaN(brightness)) {
Slog.w(TAG, "Attempting to set invalid brightness");
return;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d558e69..466070f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -109,6 +109,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
@@ -260,6 +261,13 @@
final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
mDisplayWindowPolicyControllers = new SparseArray<>();
+ /**
+ * Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
+ * {@link DisplayDevice#mUniqueId}.
+ */
+ public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
+ new ArrayMap<>();
+
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1640,7 +1648,16 @@
DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChanged();
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
+ + display.getDisplayIdLocked());
+ return;
+ }
+
+ final String uniqueId = device.getUniqueId();
+ HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
+ dpc.onDisplayChanged(hbmMetadata);
}
}
@@ -1698,7 +1715,15 @@
final int displayId = display.getDisplayIdLocked();
final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChanged();
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
+ + display.getDisplayIdLocked());
+ return;
+ }
+ final String uniqueId = device.getUniqueId();
+ HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
+ dpc.onDisplayChanged(hbmMetadata);
}
}
@@ -2651,6 +2676,26 @@
mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
}
+ private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+ + display.getDisplayIdLocked());
+ return null;
+ }
+
+ final String uniqueId = device.getUniqueId();
+
+ if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
+ return mHighBrightnessModeMetadataMap.get(uniqueId);
+ }
+
+ // HBM Time info not present. Create a new one for this physical display.
+ HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
+ mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
+ return hbmInfo;
+ }
+
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
private void addDisplayPowerControllerLocked(LogicalDisplay display) {
if (mPowerHandler == null) {
@@ -2666,17 +2711,23 @@
display, mSyncRoot);
final DisplayPowerControllerInterface displayPowerController;
+ // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
+ // Or create a new one and use that.
+ // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
+ // displayPowerController, so the hbm info can be correctly associated
+ // with the corresponding displaydevice.
+ HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
if (DeviceConfig.getBoolean("display_manager",
"use_newly_structured_display_power_controller", true)) {
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display));
+ () -> handleBrightnessChange(display), hbmMetadata);
} else {
displayPowerController = new DisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display));
+ () -> handleBrightnessChange(display), hbmMetadata);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index c960416..f8d6c5f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -3081,10 +3081,10 @@
@Override
public boolean supportsFrameRateOverride() {
- return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true)
+ return SurfaceFlingerProperties.enable_frame_rate_override().orElse(false)
&& !SurfaceFlingerProperties.frame_rate_override_for_native_rates()
- .orElse(false)
- && SurfaceFlingerProperties.frame_rate_override_global().orElse(true);
+ .orElse(true)
+ && SurfaceFlingerProperties.frame_rate_override_global().orElse(false);
}
private DisplayManager getDisplayManager() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index cdaa3d0..142ec68 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -388,6 +388,7 @@
private float[] mNitsRange;
private final HighBrightnessModeController mHbmController;
+ private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
private final BrightnessThrottler mBrightnessThrottler;
@@ -505,13 +506,14 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable) {
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mTag = "DisplayPowerController[" + mDisplayId + "]";
+ mHighBrightnessModeMetadata = hbmMetadata;
mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
@@ -790,7 +792,7 @@
* Make sure DisplayManagerService.mSyncRoot is held when this is called
*/
@Override
- public void onDisplayChanged() {
+ public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
@@ -812,7 +814,7 @@
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info);
+ loadFromDisplayDeviceConfig(token, info, hbmMetadata);
/// Since the underlying display-device changed, we really don't know the
// last command that was sent to change it's state. Lets assume it is unknown so
@@ -864,7 +866,8 @@
}
}
- private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
+ HighBrightnessModeMetadata hbmMetadata) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
@@ -877,6 +880,7 @@
mBrightnessRampIncreaseMaxTimeMillis,
mBrightnessRampDecreaseMaxTimeMillis);
}
+ mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1961,7 +1965,7 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
}
- }, mContext);
+ }, mHighBrightnessModeMetadata, mContext);
}
private BrightnessThrottler createBrightnessThrottlerLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index da59cca..ba9fe38 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -68,6 +68,7 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.brightness.DisplayBrightnessController;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
@@ -197,8 +198,6 @@
// mScreenBrightnessDimConfig.
private final float mScreenBrightnessMinimumDimAmount;
- private final float mScreenBrightnessDefault;
-
// True if auto-brightness should be used.
private boolean mUseSoftwareAutoBrightnessConfig;
@@ -328,11 +327,10 @@
private float[] mNitsRange;
private final HighBrightnessModeController mHbmController;
+ private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
private final BrightnessThrottler mBrightnessThrottler;
- private final BrightnessSetting mBrightnessSetting;
-
private final Runnable mOnBrightnessChangeRunnable;
private final BrightnessEvent mLastBrightnessEvent;
@@ -383,19 +381,6 @@
@Nullable
private BrightnessConfiguration mBrightnessConfiguration;
- // The last brightness that was set by the user and not temporary. Set to
- // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
- private float mLastUserSetScreenBrightness = Float.NaN;
-
- // The screen brightness setting has changed but not taken effect yet. If this is different
- // from the current screen brightness setting then this is coming from something other than us
- // and should be considered a user interaction.
- private float mPendingScreenBrightnessSetting;
-
- // The last observed screen brightness setting, either set by us or by the settings app on
- // behalf of the user.
- private float mCurrentScreenBrightnessSetting;
-
// The last auto brightness adjustment that was set by the user and not temporary. Set to
// Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
private float mAutoBrightnessAdjustment;
@@ -416,7 +401,6 @@
private ObjectAnimator mColorFadeOnAnimator;
private ObjectAnimator mColorFadeOffAnimator;
private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
- private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
// True if this DisplayPowerController2 has been stopped and should no longer be running.
private boolean mStopped;
@@ -432,7 +416,7 @@
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
- Runnable onBrightnessChangeRunnable) {
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
mInjector = injector != null ? injector : new Injector();
mClock = mInjector.getClock();
@@ -448,6 +432,7 @@
mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
() -> updatePowerState(), mDisplayId, mSensorManager);
+ mHighBrightnessModeMetadata = hbmMetadata;
mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
mTag = "DisplayPowerController2[" + mDisplayId + "]";
@@ -469,8 +454,6 @@
mBlanker = blanker;
mContext = context;
mBrightnessTracker = brightnessTracker;
- // TODO: b/186428377 update brightness setting when display changes
- mBrightnessSetting = brightnessSetting;
mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -478,18 +461,13 @@
final Resources resources = context.getResources();
// DOZE AND DIM SETTINGS
- mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
+ mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
- mScreenBrightnessDimConfig = clampAbsoluteBrightness(
+ mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
mScreenBrightnessMinimumDimAmount = resources.getFloat(
R.dimen.config_screenBrightnessMinimumDimAmountFloat);
-
- // NORMAL SCREEN SETTINGS
- mScreenBrightnessDefault = clampAbsoluteBrightness(
- mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
-
loadBrightnessRampRates();
mSkipScreenOnBrightnessRamp = resources.getBoolean(
R.bool.config_skipScreenOnBrightnessRamp);
@@ -497,7 +475,10 @@
mHbmController = createHbmControllerLocked();
mBrightnessThrottler = createBrightnessThrottlerLocked();
-
+ mDisplayBrightnessController =
+ new DisplayBrightnessController(context, null,
+ mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
+ brightnessSetting, () -> postBrightnessChangeRunnable());
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
@@ -552,12 +533,7 @@
mBrightnessBucketsInDozeConfig = resources.getBoolean(
R.bool.config_displayBrightnessBucketsInDoze);
-
- mDisplayBrightnessController =
- new DisplayBrightnessController(context, null, mDisplayId);
- mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
- mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
@@ -707,7 +683,7 @@
* Make sure DisplayManagerService.mSyncRoot lock is held when this is called
*/
@Override
- public void onDisplayChanged() {
+ public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
@@ -721,6 +697,7 @@
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+
mHandler.post(() -> {
boolean changed = false;
if (mDisplayDevice != device) {
@@ -729,7 +706,7 @@
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
- loadFromDisplayDeviceConfig(token, info);
+ loadFromDisplayDeviceConfig(token, info, hbmMetadata);
mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
// Since the underlying display-device changed, we really don't know the
@@ -770,15 +747,14 @@
mAutomaticBrightnessController.stop();
}
- if (mBrightnessSetting != null) {
- mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
- }
+ mDisplayBrightnessController.stop();
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
}
}
- private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
+ HighBrightnessModeMetadata hbmMetadata) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
loadBrightnessRampRates();
@@ -790,6 +766,7 @@
mBrightnessRampIncreaseMaxTimeMillis,
mBrightnessRampDecreaseMaxTimeMillis);
}
+ mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
mDisplayDeviceConfig.getHighBrightnessModeData(),
new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -849,12 +826,14 @@
if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
}
- mBrightnessSettingListener = brightnessValue -> {
+
+ BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> {
Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
mHandler.sendMessage(msg);
};
+ mDisplayBrightnessController
+ .registerBrightnessSettingChangeListener(brightnessSettingListener);
- mBrightnessSetting.registerListener(mBrightnessSettingListener);
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
@@ -1204,7 +1183,8 @@
? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
- final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
+ final boolean userSetBrightnessChanged = mDisplayBrightnessController
+ .updateUserSetScreenBrightness();
final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
@@ -1230,7 +1210,7 @@
hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
mAutomaticBrightnessController.configure(autoBrightnessState,
mBrightnessConfiguration,
- mLastUserSetScreenBrightness,
+ mDisplayBrightnessController.getLastUserSetScreenBrightness(),
userSetBrightnessChanged, autoBrightnessAdjustment,
autoBrightnessAdjustmentChanged, mPowerRequest.policy,
mShouldResetShortTermModel);
@@ -1242,7 +1222,7 @@
}
boolean updateScreenBrightnessSetting = false;
-
+ float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness();
// Apply auto-brightness.
boolean slowChange = false;
if (Float.isNaN(brightnessState)) {
@@ -1253,14 +1233,14 @@
newAutoBrightnessAdjustment =
mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
}
- if (isValidBrightnessValue(brightnessState)
+ if (BrightnessUtils.isValidBrightnessValue(brightnessState)
|| brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
// Use current auto-brightness value and slowly adjust to changes.
brightnessState = clampScreenBrightness(brightnessState);
if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
slowChange = true; // slowly adapt to auto-brightness
}
- updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+ updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
mAppliedAutoBrightness = true;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
if (mScreenOffBrightnessSensorController != null) {
@@ -1298,9 +1278,10 @@
if (Float.isNaN(brightnessState) && autoBrightnessEnabled
&& mScreenOffBrightnessSensorController != null) {
brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness();
- if (isValidBrightnessValue(brightnessState)) {
+ if (BrightnessUtils.isValidBrightnessValue(brightnessState)) {
brightnessState = clampScreenBrightness(brightnessState);
- updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+ updateScreenBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness()
+ != brightnessState;
mBrightnessReasonTemp.setReason(
BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR);
}
@@ -1308,8 +1289,8 @@
// Apply manual brightness.
if (Float.isNaN(brightnessState)) {
- brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
- if (brightnessState != mCurrentScreenBrightnessSetting) {
+ brightnessState = clampScreenBrightness(currentBrightnessSetting);
+ if (brightnessState != currentBrightnessSetting) {
// The manually chosen screen brightness is outside of the currently allowed
// range (i.e., high-brightness-mode), make sure we tell the rest of the system
// by updating the setting.
@@ -1346,7 +1327,7 @@
// before applying the low power or dim transformations so that the slider
// accurately represents the full possible range, even if they range changes what
// it means in absolute terms.
- updateScreenBrightnessSetting(brightnessState);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessState);
}
// Apply dimming by at least some minimum amount when user activity
@@ -1457,7 +1438,7 @@
final float currentBrightness = mPowerState.getScreenBrightness();
final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
- if (isValidBrightnessValue(animateValue)
+ if (BrightnessUtils.isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
if (initialRampSkip || hasBrightnessBuckets
@@ -1742,7 +1723,7 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
}
- }, mContext);
+ }, mHighBrightnessModeMetadata, mContext);
}
private BrightnessThrottler createBrightnessThrottlerLocked() {
@@ -1892,12 +1873,6 @@
mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
}
- // Checks whether the brightness is within the valid brightness range, not including off.
- private boolean isValidBrightnessValue(float brightness) {
- return brightness >= PowerManager.BRIGHTNESS_MIN
- && brightness <= PowerManager.BRIGHTNESS_MAX;
- }
-
private void animateScreenBrightness(float target, float sdrTarget, float rate) {
if (DEBUG) {
Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
@@ -2078,11 +2053,14 @@
}
private void handleSettingsChange(boolean userSwitch) {
- mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+ mDisplayBrightnessController
+ .setPendingScreenBrightness(mDisplayBrightnessController
+ .getScreenBrightnessSetting());
mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
if (userSwitch) {
// Don't treat user switches as user initiated change.
- setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ mDisplayBrightnessController.setCurrentScreenBrightness(mDisplayBrightnessController
+ .getPendingScreenBrightness());
updateAutoBrightnessAdjustment();
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.resetShortTermModel();
@@ -2111,34 +2089,12 @@
@Override
public float getScreenBrightnessSetting() {
- float brightness = mBrightnessSetting.getBrightness();
- if (Float.isNaN(brightness)) {
- brightness = mScreenBrightnessDefault;
- }
- return clampAbsoluteBrightness(brightness);
+ return mDisplayBrightnessController.getScreenBrightnessSetting();
}
@Override
public void setBrightness(float brightnessValue) {
- // Update the setting, which will eventually call back into DPC to have us actually update
- // the display with the new value.
- mBrightnessSetting.setBrightness(brightnessValue);
- }
-
- private void updateScreenBrightnessSetting(float brightnessValue) {
- if (!isValidBrightnessValue(brightnessValue)
- || brightnessValue == mCurrentScreenBrightnessSetting) {
- return;
- }
- setCurrentScreenBrightness(brightnessValue);
- mBrightnessSetting.setBrightness(brightnessValue);
- }
-
- private void setCurrentScreenBrightness(float brightnessValue) {
- if (brightnessValue != mCurrentScreenBrightnessSetting) {
- mCurrentScreenBrightnessSetting = brightnessValue;
- postBrightnessChangeRunnable();
- }
+ mDisplayBrightnessController.setBrightness(brightnessValue);
}
private void putAutoBrightnessAdjustmentSetting(float adjustment) {
@@ -2164,28 +2120,6 @@
return true;
}
- // We want to return true if the user has set the screen brightness.
- // RBC on, off, and intensity changes will return false.
- // Slider interactions whilst in RBC will return true, just as when in non-rbc.
- private boolean updateUserSetScreenBrightness() {
- if ((Float.isNaN(mPendingScreenBrightnessSetting)
- || mPendingScreenBrightnessSetting < 0.0f)) {
- return false;
- }
- if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
- mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mDisplayBrightnessController
- .setTemporaryBrightness(PowerManager.BRIGHTNESS_INVALID_FLOAT);
- return false;
- }
- setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
- mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
- mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mDisplayBrightnessController
- .setTemporaryBrightness(PowerManager.BRIGHTNESS_INVALID_FLOAT);
- return true;
- }
-
private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
@@ -2230,7 +2164,6 @@
pw.println();
pw.println("Display Power Controller Configuration:");
- pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
@@ -2261,9 +2194,6 @@
pw.println();
pw.println("Display Power Controller Thread State:");
pw.println(" mPowerRequest=" + mPowerRequest);
- pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
- pw.println(" mPendingScreenBrightnessSetting="
- + mPendingScreenBrightnessSetting);
pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
pw.println(" mBrightnessReason=" + mBrightnessReason);
pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
@@ -2380,11 +2310,6 @@
}
}
- private static float clampAbsoluteBrightness(float value) {
- return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX);
- }
-
private static float clampAutoBrightnessAdjustment(float value) {
return MathUtils.constrain(value, -1.0f, 1.0f);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index e750ee2..7b019846 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -31,10 +31,14 @@
public interface DisplayPowerControllerInterface {
/**
- * Notified when the display is changed. We use this to apply any changes that might be needed
+ * Notified when the display is changed.
+ * We use this to apply any changes that might be needed
* when displays get swapped on foldable devices.
+ * We also pass the High brightness mode metadata like
+ * remaining time and hbm events for the corresponding
+ * physical display, to update the values correctly.
*/
- void onDisplayChanged();
+ void onDisplayChanged(HighBrightnessModeMetadata hbmInfo);
/**
* Unregisters all listeners and interrupts all running threads; halting future work.
diff --git a/services/core/java/com/android/server/display/HbmEvent.java b/services/core/java/com/android/server/display/HbmEvent.java
new file mode 100644
index 0000000..5675e2f
--- /dev/null
+++ b/services/core/java/com/android/server/display/HbmEvent.java
@@ -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.display;
+
+
+/**
+ * Represents an event in which High Brightness Mode was enabled.
+ */
+class HbmEvent {
+ private long mStartTimeMillis;
+ private long mEndTimeMillis;
+
+ HbmEvent(long startTimeMillis, long endTimeMillis) {
+ this.mStartTimeMillis = startTimeMillis;
+ this.mEndTimeMillis = endTimeMillis;
+ }
+
+ public long getStartTimeMillis() {
+ return mStartTimeMillis;
+ }
+
+ public long getEndTimeMillis() {
+ return mEndTimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "HbmEvent: {startTimeMillis:" + mStartTimeMillis + ", endTimeMillis: "
+ + mEndTimeMillis + "}, total: "
+ + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
+ }
+}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index f98c7df..2c843a4 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -105,30 +105,23 @@
private int mHbmStatsState = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
/**
- * If HBM is currently running, this is the start time for the current HBM session.
+ * If HBM is currently running, this is the start time and set of all events,
+ * for the current HBM session.
*/
- private long mRunningStartTimeMillis = -1;
-
- /**
- * Queue of previous HBM-events ordered from most recent to least recent.
- * Meant to store only the events that fall into the most recent
- * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
- */
- private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
-
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadata = null;
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
- Runnable hbmChangeCallback, Context context) {
+ Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
- brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
+ brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
- Runnable hbmChangeCallback, Context context) {
+ Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
mInjector = injector;
mContext = context;
mClock = injector.getClock();
@@ -137,6 +130,7 @@
mBrightnessMin = brightnessMin;
mBrightnessMax = brightnessMax;
mHbmChangeCallback = hbmChangeCallback;
+ mHighBrightnessModeMetadata = hbmMetadata;
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mRecalcRunnable = this::recalculateTimeAllowance;
@@ -222,19 +216,22 @@
// If we are starting or ending a high brightness mode session, store the current
// session in mRunningStartTimeMillis, or the old one in mEvents.
- final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
+ final long runningStartTime = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+ final boolean wasHbmDrainingAvailableTime = runningStartTime != -1;
final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
&& !mIsHdrLayerPresent;
if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
final long currentTime = mClock.uptimeMillis();
if (shouldHbmDrainAvailableTime) {
- mRunningStartTimeMillis = currentTime;
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
} else {
- mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
- mRunningStartTimeMillis = -1;
+ final HbmEvent hbmEvent = new HbmEvent(runningStartTime, currentTime);
+ mHighBrightnessModeMetadata.addHbmEvent(hbmEvent);
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(-1);
if (DEBUG) {
- Slog.d(TAG, "New HBM event: " + mEvents.peekFirst());
+ Slog.d(TAG, "New HBM event: "
+ + mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst());
}
}
}
@@ -260,6 +257,10 @@
mSettingsObserver.stopObserving();
}
+ void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
+ mHighBrightnessModeMetadata = hbmInfo;
+ }
+
void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
mWidth = width;
@@ -316,20 +317,22 @@
pw.println(" mBrightnessMax=" + mBrightnessMax);
pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
pw.println(" mIsTimeAvailable= " + mIsTimeAvailable);
- pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
+ pw.println(" mRunningStartTimeMillis="
+ + TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis()));
pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
pw.println(" width*height=" + mWidth + "*" + mHeight);
pw.println(" mEvents=");
final long currentTime = mClock.uptimeMillis();
long lastStartTime = currentTime;
- if (mRunningStartTimeMillis != -1) {
- lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime));
+ long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+ if (runningStartTimeMillis != -1) {
+ lastStartTime = dumpHbmEvent(pw, new HbmEvent(runningStartTimeMillis, currentTime));
}
- for (HbmEvent event : mEvents) {
- if (lastStartTime > event.endTimeMillis) {
+ for (HbmEvent event : mHighBrightnessModeMetadata.getHbmEventQueue()) {
+ if (lastStartTime > event.getEndTimeMillis()) {
pw.println(" event: [normal brightness]: "
- + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis));
+ + TimeUtils.formatDuration(lastStartTime - event.getEndTimeMillis()));
}
lastStartTime = dumpHbmEvent(pw, event);
}
@@ -338,12 +341,12 @@
}
private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
- final long duration = event.endTimeMillis - event.startTimeMillis;
+ final long duration = event.getEndTimeMillis() - event.getStartTimeMillis();
pw.println(" event: ["
- + TimeUtils.formatUptime(event.startTimeMillis) + ", "
- + TimeUtils.formatUptime(event.endTimeMillis) + "] ("
+ + TimeUtils.formatUptime(event.getStartTimeMillis()) + ", "
+ + TimeUtils.formatUptime(event.getEndTimeMillis()) + "] ("
+ TimeUtils.formatDuration(duration) + ")");
- return event.startTimeMillis;
+ return event.getStartTimeMillis();
}
private boolean isCurrentlyAllowed() {
@@ -372,13 +375,15 @@
// First, lets see how much time we've taken for any currently running
// session of HBM.
- if (mRunningStartTimeMillis > 0) {
- if (mRunningStartTimeMillis > currentTime) {
+ long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
+ if (runningStartTimeMillis > 0) {
+ if (runningStartTimeMillis > currentTime) {
Slog.e(TAG, "Start time set to the future. curr: " + currentTime
- + ", start: " + mRunningStartTimeMillis);
- mRunningStartTimeMillis = currentTime;
+ + ", start: " + runningStartTimeMillis);
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
+ runningStartTimeMillis = currentTime;
}
- timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
+ timeAlreadyUsed = currentTime - runningStartTimeMillis;
}
if (DEBUG) {
@@ -387,18 +392,19 @@
// Next, lets iterate through the history of previous sessions and add those times.
final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
- Iterator<HbmEvent> it = mEvents.iterator();
+ Iterator<HbmEvent> it = mHighBrightnessModeMetadata.getHbmEventQueue().iterator();
while (it.hasNext()) {
final HbmEvent event = it.next();
// If this event ended before the current Timing window, discard forever and ever.
- if (event.endTimeMillis < windowstartTimeMillis) {
+ if (event.getEndTimeMillis() < windowstartTimeMillis) {
it.remove();
continue;
}
- final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis);
- timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
+ final long startTimeMillis = Math.max(event.getStartTimeMillis(),
+ windowstartTimeMillis);
+ timeAlreadyUsed += event.getEndTimeMillis() - startTimeMillis;
}
if (DEBUG) {
@@ -425,17 +431,18 @@
// Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
// brightness change doesn't happen before then.
long nextTimeout = -1;
+ final ArrayDeque<HbmEvent> hbmEvents = mHighBrightnessModeMetadata.getHbmEventQueue();
if (mBrightness > mHbmData.transitionPoint) {
// if we're in high-lux now, timeout when we run out of allowed time.
nextTimeout = currentTime + remainingTime;
- } else if (!mIsTimeAvailable && mEvents.size() > 0) {
+ } else if (!mIsTimeAvailable && hbmEvents.size() > 0) {
// If we are not allowed...timeout when the oldest event moved outside of the timing
// window by at least minTime. Basically, we're calculating the soonest time we can
// get {@code timeMinMillis} back to us.
final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
- final HbmEvent lastEvent = mEvents.peekLast();
+ final HbmEvent lastEvent = hbmEvents.peekLast();
final long startTimePlusMinMillis =
- Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
+ Math.max(windowstartTimeMillis, lastEvent.getStartTimeMillis())
+ mHbmData.timeMinMillis;
final long timeWhenMinIsGainedBack =
currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime;
@@ -459,9 +466,10 @@
+ ", mUnthrottledBrightness: " + mUnthrottledBrightness
+ ", mThrottlingReason: "
+ BrightnessInfo.briMaxReasonToString(mThrottlingReason)
- + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+ + ", RunningStartTimeMillis: "
+ + mHighBrightnessModeMetadata.getRunningStartTimeMillis()
+ ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
- + ", events: " + mEvents);
+ + ", events: " + hbmEvents);
}
if (nextTimeout != -1) {
@@ -588,25 +596,6 @@
}
}
- /**
- * Represents an event in which High Brightness Mode was enabled.
- */
- private static class HbmEvent {
- public long startTimeMillis;
- public long endTimeMillis;
-
- HbmEvent(long startTimeMillis, long endTimeMillis) {
- this.startTimeMillis = startTimeMillis;
- this.endTimeMillis = endTimeMillis;
- }
-
- @Override
- public String toString() {
- return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: "
- + ((endTimeMillis - startTimeMillis) / 1000) + "]";
- }
- }
-
@VisibleForTesting
class HdrListener extends SurfaceControlHdrLayerInfoListener {
@Override
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
new file mode 100644
index 0000000..37234ff
--- /dev/null
+++ b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
@@ -0,0 +1,58 @@
+/*
+ * 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.display;
+
+import java.util.ArrayDeque;
+
+
+/**
+ * Represents High Brightness Mode metadata associated
+ * with a specific internal physical display.
+ * Required for separately storing data like time information,
+ * and related events when display was in HBM mode per
+ * physical internal display.
+ */
+class HighBrightnessModeMetadata {
+ /**
+ * Queue of previous HBM-events ordered from most recent to least recent.
+ * Meant to store only the events that fall into the most recent
+ * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
+ */
+ private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
+
+ /**
+ * If HBM is currently running, this is the start time for the current HBM session.
+ */
+ private long mRunningStartTimeMillis = -1;
+
+ public long getRunningStartTimeMillis() {
+ return mRunningStartTimeMillis;
+ }
+
+ public void setRunningStartTimeMillis(long setTime) {
+ mRunningStartTimeMillis = setTime;
+ }
+
+ public ArrayDeque<HbmEvent> getHbmEventQueue() {
+ return mEvents;
+ }
+
+ public void addHbmEvent(HbmEvent hbmEvent) {
+ mEvents.addFirst(hbmEvent);
+ }
+}
+
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
index fd4e296..d5aeba1 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness;
import android.os.PowerManager;
+import android.util.MathUtils;
import com.android.server.display.DisplayBrightnessState;
@@ -33,6 +34,14 @@
}
/**
+ * Clamps the brightness value in the maximum and the minimum brightness range
+ */
+ public static float clampAbsoluteBrightness(float value) {
+ return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX);
+ }
+
+ /**
* A utility to construct the DisplayBrightnessState
*/
public static DisplayBrightnessState constructDisplayBrightnessState(
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index bdc8d9d..e003ecb 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -18,9 +18,12 @@
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
+import android.os.PowerManager;
import android.util.IndentingPrintWriter;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.BrightnessSetting;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
@@ -31,19 +34,67 @@
* display. Applies the chosen brightness.
*/
public final class DisplayBrightnessController {
+ // The ID of the display tied to this DisplayBrightnessController
private final int mDisplayId;
+
+ // The lock which is to be used to synchronize the resources being used in this class
+ private final Object mLock = new Object();
+
+ // The default screen brightness to be used when no value is available in BrightnessSetting.
+ private final float mScreenBrightnessDefault;
+
+ // This is used to persist the changes happening to the brightness.
+ private final BrightnessSetting mBrightnessSetting;
+
+ // A runnable to update the clients registered via DisplayManagerGlobal
+ // .EVENT_DISPLAY_BRIGHTNESS_CHANGED about the brightness change. Called when
+ // mCurrentScreenBrightness is updated.
+ private Runnable mOnBrightnessChangeRunnable;
+
+ // The screen brightness that has changed but not taken effect yet. If this is different
+ // from the current screen brightness then this is coming from something other than us
+ // and should be considered a user interaction.
+ @GuardedBy("mLock")
+ private float mPendingScreenBrightness;
+
+ // The last observed screen brightness, either set by us or by the settings app on
+ // behalf of the user.
+ @GuardedBy("mLock")
+ private float mCurrentScreenBrightness;
+
+ // The last brightness that was set by the user and not temporary. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
+ @GuardedBy("mLock")
+ private float mLastUserSetScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+ // The listener which is to be notified everytime there is a change in the brightness in the
+ // BrightnessSetting.
+ private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
+
// Selects an appropriate strategy based on the request provided by the clients.
+ @GuardedBy("mLock")
private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
+
+ // Currently selected DisplayBrightnessStrategy.
+ @GuardedBy("mLock")
private DisplayBrightnessStrategy mDisplayBrightnessStrategy;
/**
* The constructor of DisplayBrightnessController.
*/
- public DisplayBrightnessController(Context context, Injector injector, int displayId) {
+ public DisplayBrightnessController(Context context, Injector injector, int displayId,
+ float defaultScreenBrightness, BrightnessSetting brightnessSetting,
+ Runnable onBrightnessChangeRunnable) {
if (injector == null) {
injector = new Injector();
}
mDisplayId = displayId;
+ // TODO: b/186428377 update brightness setting when display changes
+ mBrightnessSetting = brightnessSetting;
+ mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mCurrentScreenBrightness = getScreenBrightnessSetting();
+ mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
+ mScreenBrightnessDefault = BrightnessUtils.clampAbsoluteBrightness(defaultScreenBrightness);
mDisplayBrightnessStrategySelector = injector.getDisplayBrightnessStrategySelector(context,
displayId);
}
@@ -61,25 +112,20 @@
public DisplayBrightnessState updateBrightness(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
int targetDisplayState) {
- mDisplayBrightnessStrategy =
- mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
- targetDisplayState);
- return mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest);
+ synchronized (mLock) {
+ mDisplayBrightnessStrategy = mDisplayBrightnessStrategySelector.selectStrategy(
+ displayPowerRequest, targetDisplayState);
+ return mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest);
+ }
}
/**
* Sets the temporary brightness
*/
public void setTemporaryBrightness(Float temporaryBrightness) {
- mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()
- .setTemporaryScreenBrightness(temporaryBrightness);
- }
-
- /**
- * Returns the current selected DisplayBrightnessStrategy
- */
- public DisplayBrightnessStrategy getCurrentDisplayBrightnessStrategy() {
- return mDisplayBrightnessStrategy;
+ synchronized (mLock) {
+ setTemporaryBrightnessLocked(temporaryBrightness);
+ }
}
/**
@@ -87,7 +133,140 @@
* brightness when dozing
*/
public boolean isAllowAutoBrightnessWhileDozingConfig() {
- return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozingConfig();
+ synchronized (mLock) {
+ return mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozingConfig();
+ }
+ }
+
+ /**
+ * Sets the current screen brightness to the supplied value, and notifies all the listeners
+ * requesting for change events on brightness change.
+ */
+ public void setCurrentScreenBrightness(float brightnessValue) {
+ synchronized (mLock) {
+ if (brightnessValue != mCurrentScreenBrightness) {
+ mCurrentScreenBrightness = brightnessValue;
+ mOnBrightnessChangeRunnable.run();
+ }
+ }
+ }
+
+ /**
+ * Returns the last observed screen brightness.
+ */
+ public float getCurrentBrightness() {
+ synchronized (mLock) {
+ return mCurrentScreenBrightness;
+ }
+ }
+
+ /**
+ * Returns the screen brightness which has changed but has not taken any effect so far.
+ */
+ public float getPendingScreenBrightness() {
+ synchronized (mLock) {
+ return mPendingScreenBrightness;
+ }
+ }
+
+ /**
+ * Sets the pending screen brightness setting, representing a value which is requested, but not
+ * yet processed.
+ * @param brightnessValue The value to which the pending screen brightness is to be set.
+ */
+ public void setPendingScreenBrightness(float brightnessValue) {
+ synchronized (mLock) {
+ mPendingScreenBrightness = brightnessValue;
+ }
+ }
+
+ /**
+ * We want to return true if the user has set the screen brightness.
+ * RBC on, off, and intensity changes will return false.
+ * Slider interactions whilst in RBC will return true, just as when in non-rbc.
+ */
+ public boolean updateUserSetScreenBrightness() {
+ synchronized (mLock) {
+ if (!BrightnessUtils.isValidBrightnessValue(mPendingScreenBrightness)) {
+ return false;
+ }
+ if (mCurrentScreenBrightness == mPendingScreenBrightness) {
+ mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ setTemporaryBrightnessLocked(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ return false;
+ }
+ setCurrentScreenBrightness(mPendingScreenBrightness);
+ mLastUserSetScreenBrightness = mPendingScreenBrightness;
+ mPendingScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ setTemporaryBrightnessLocked(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ return true;
+ }
+ }
+
+ /**
+ * Registers the BrightnessSettingListener with the BrightnessSetting, which will be notified
+ * everytime there is a change in the brightness.
+ */
+ public void registerBrightnessSettingChangeListener(
+ BrightnessSetting.BrightnessSettingListener brightnessSettingListener) {
+ mBrightnessSettingListener = brightnessSettingListener;
+ mBrightnessSetting.registerListener(mBrightnessSettingListener);
+ }
+
+ /**
+ * Returns the last user set brightness which is not temporary.
+ */
+ public float getLastUserSetScreenBrightness() {
+ synchronized (mLock) {
+ return mLastUserSetScreenBrightness;
+ }
+ }
+
+ /**
+ * Returns the current screen brightnessSetting which is responsible for saving the brightness
+ * in the persistent store
+ */
+ public float getScreenBrightnessSetting() {
+ float brightness = mBrightnessSetting.getBrightness();
+ synchronized (mLock) {
+ if (Float.isNaN(brightness)) {
+ brightness = mScreenBrightnessDefault;
+ }
+ return BrightnessUtils.clampAbsoluteBrightness(brightness);
+ }
+ }
+
+ /**
+ * Notifies the brightnessSetting to persist the supplied brightness value.
+ */
+ public void setBrightness(float brightnessValue) {
+ // Update the setting, which will eventually call back into DPC to have us actually
+ // update the display with the new value.
+ mBrightnessSetting.setBrightness(brightnessValue);
+ }
+
+ /**
+ * Sets the current screen brightness, and notifies the BrightnessSetting about the change.
+ */
+ public void updateScreenBrightnessSetting(float brightnessValue) {
+ synchronized (mLock) {
+ if (!BrightnessUtils.isValidBrightnessValue(brightnessValue)
+ || brightnessValue == mCurrentScreenBrightness) {
+ return;
+ }
+ setCurrentScreenBrightness(brightnessValue);
+ setBrightness(brightnessValue);
+ }
+ }
+
+ /**
+ * Stops the associated listeners when the display is stopped. Invoked when the {@link
+ * #mDisplayId} is being removed.
+ */
+ public void stop() {
+ if (mBrightnessSetting != null) {
+ mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+ }
}
/**
@@ -99,12 +278,19 @@
writer.println();
writer.println("DisplayBrightnessController:");
writer.println(" mDisplayId=: " + mDisplayId);
- if (mDisplayBrightnessStrategy != null) {
- writer.println(" Last selected DisplayBrightnessStrategy= "
- + mDisplayBrightnessStrategy.getName());
+ writer.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault);
+ synchronized (mLock) {
+ writer.println(" mPendingScreenBrightness=" + mPendingScreenBrightness);
+ writer.println(" mCurrentScreenBrightness=" + mCurrentScreenBrightness);
+ writer.println(" mLastUserSetScreenBrightness="
+ + mLastUserSetScreenBrightness);
+ if (mDisplayBrightnessStrategy != null) {
+ writer.println(" Last selected DisplayBrightnessStrategy= "
+ + mDisplayBrightnessStrategy.getName());
+ }
+ IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
+ mDisplayBrightnessStrategySelector.dump(ipw);
}
- IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
- mDisplayBrightnessStrategySelector.dump(ipw);
}
@VisibleForTesting
@@ -114,4 +300,26 @@
return new DisplayBrightnessStrategySelector(context, /* injector= */ null, displayId);
}
}
+
+ @VisibleForTesting
+ BrightnessSetting.BrightnessSettingListener getBrightnessSettingListenerLocked() {
+ return mBrightnessSettingListener;
+ }
+
+ /**
+ * Returns the current selected DisplayBrightnessStrategy
+ */
+ @VisibleForTesting
+ DisplayBrightnessStrategy getCurrentDisplayBrightnessStrategyLocked() {
+ synchronized (mLock) {
+ return mDisplayBrightnessStrategy;
+ }
+ }
+
+ private void setTemporaryBrightnessLocked(float temporaryBrightness) {
+ synchronized (mLock) {
+ mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()
+ .setTemporaryScreenBrightness(temporaryBrightness);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index c1780a3..e5357f6 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,6 +16,9 @@
package com.android.server.dreams;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+
+import android.app.ActivityTaskManager;
import android.app.BroadcastOptions;
import android.content.ComponentName;
import android.content.Context;
@@ -62,6 +65,7 @@
private final Context mContext;
private final Handler mHandler;
private final Listener mListener;
+ private final ActivityTaskManager mActivityTaskManager;
private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -89,6 +93,7 @@
mContext = context;
mHandler = handler;
mListener = listener;
+ mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mCloseNotificationShadeIntent.putExtra("reason", "dream");
}
@@ -272,6 +277,9 @@
mSentStartBroadcast = false;
}
+ mActivityTaskManager.removeRootTasksWithActivityTypes(
+ new int[] {ACTIVITY_TYPE_DREAM});
+
mListener.onDreamStopped(dream.mToken);
}
} finally {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index c47d749..a4d2d03 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -462,7 +462,7 @@
}
protected void requestStopDreamFromShell() {
- stopDreamInternal(true, "stopping dream from shell");
+ stopDreamInternal(false, "stopping dream from shell");
}
private void stopDreamInternal(boolean immediate, String reason) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ad8e35d..b919330 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1152,8 +1152,8 @@
throw new IOException("Root has not present");
}
return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash);
- } catch (IOException ignore) {
- Slog.e(TAG, "ERROR: could not load root hash from incremental install");
+ } catch (IOException e) {
+ Slog.i(TAG, "Could not obtain verity root hash", e);
}
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3340e12..762d1f6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -262,7 +262,7 @@
// We need to keep process uid within Integer.MAX_VALUE.
@VisibleForTesting
- static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
+ static final int MAX_USER_ID = UserHandle.MAX_SECONDARY_USER_ID;
// Max size of the queue of recently removed users
@VisibleForTesting
@@ -1779,28 +1779,6 @@
}
@Override
- public boolean isMediaSharedWithParent(@UserIdInt int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
- "isMediaSharedWithParent");
- synchronized (mUsersLock) {
- UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
- return userTypeDetails != null ? userTypeDetails.isProfile()
- && userTypeDetails.isMediaSharedWithParent() : false;
- }
- }
-
- @Override
- public boolean isCredentialSharableWithParent(@UserIdInt int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
- "isCredentialSharableWithParent");
- synchronized (mUsersLock) {
- UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
- return userTypeDetails != null && userTypeDetails.isProfile()
- && userTypeDetails.isCredentialSharableWithParent();
- }
- }
-
- @Override
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
"isUserUnlockingOrUnlocked");
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index ddf3692..f86ee90 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -151,20 +151,6 @@
private final @Nullable int[] mDarkThemeBadgeColors;
/**
- * Denotes if the user shares media with its parent user.
- *
- * <p> Default value is false
- */
- private final boolean mIsMediaSharedWithParent;
-
- /**
- * Denotes if the user shares encryption credentials with its parent user.
- *
- * <p> Default value is false
- */
- private final boolean mIsCredentialSharableWithParent;
-
- /**
* The default {@link UserProperties} for the user type.
* <p> The uninitialized value of each property is implied by {@link UserProperties.Builder}.
*/
@@ -180,8 +166,6 @@
@Nullable Bundle defaultSystemSettings,
@Nullable Bundle defaultSecureSettings,
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
- boolean isMediaSharedWithParent,
- boolean isCredentialSharableWithParent,
@NonNull UserProperties defaultUserProperties) {
this.mName = name;
this.mEnabled = enabled;
@@ -201,8 +185,6 @@
this.mBadgeLabels = badgeLabels;
this.mBadgeColors = badgeColors;
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
- this.mIsMediaSharedWithParent = isMediaSharedWithParent;
- this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
this.mDefaultUserProperties = defaultUserProperties;
}
@@ -309,21 +291,6 @@
return mDarkThemeBadgeColors[Math.min(badgeIndex, mDarkThemeBadgeColors.length - 1)];
}
- /**
- * Returns true if the user has shared media with parent user or false otherwise.
- */
- public boolean isMediaSharedWithParent() {
- return mIsMediaSharedWithParent;
- }
-
- /**
- * Returns true if the user has shared encryption credential with parent user or
- * false otherwise.
- */
- public boolean isCredentialSharableWithParent() {
- return mIsCredentialSharableWithParent;
- }
-
/**
* Returns the reference to the default {@link UserProperties} for this type of user.
@@ -437,8 +404,6 @@
private @DrawableRes int mIconBadge = Resources.ID_NULL;
private @DrawableRes int mBadgePlain = Resources.ID_NULL;
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
- private boolean mIsMediaSharedWithParent = false;
- private boolean mIsCredentialSharableWithParent = false;
// Default UserProperties cannot be null but for efficiency we don't initialize it now.
// If it isn't set explicitly, {@link UserProperties.Builder#build()} will be used.
private @Nullable UserProperties mDefaultUserProperties = null;
@@ -533,24 +498,6 @@
}
/**
- * Sets shared media property for the user.
- * @param isMediaSharedWithParent the value to be set, true or false
- */
- public Builder setIsMediaSharedWithParent(boolean isMediaSharedWithParent) {
- mIsMediaSharedWithParent = isMediaSharedWithParent;
- return this;
- }
-
- /**
- * Sets shared media property for the user.
- * @param isCredentialSharableWithParent the value to be set, true or false
- */
- public Builder setIsCredentialSharableWithParent(boolean isCredentialSharableWithParent) {
- mIsCredentialSharableWithParent = isCredentialSharableWithParent;
- return this;
- }
-
- /**
* Sets (replacing if necessary) the default UserProperties object for this user type.
* Takes a builder, rather than a built object, to efficiently ensure that a fresh copy of
* properties is stored (since it later might be modified by UserProperties#updateFromXml).
@@ -609,8 +556,6 @@
mDefaultSystemSettings,
mDefaultSecureSettings,
mDefaultCrossProfileIntentFilters,
- mIsMediaSharedWithParent,
- mIsCredentialSharableWithParent,
getDefaultUserProperties());
}
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index edb2a4be3b..b8c57b8 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -122,8 +122,6 @@
.setMaxAllowedPerParent(1)
.setLabel(0)
.setDefaultRestrictions(null)
- .setIsMediaSharedWithParent(true)
- .setIsCredentialSharableWithParent(true)
.setDefaultCrossProfileIntentFilters(getDefaultCloneCrossProfileIntentFilter())
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
@@ -135,7 +133,10 @@
.setCrossProfileIntentFilterAccessControl(
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
.setCrossProfileIntentResolutionStrategy(UserProperties
- .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING));
+ .CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
+ .setMediaSharedWithParent(true)
+ .setCredentialShareableWithParent(true)
+ );
}
/**
@@ -167,11 +168,11 @@
.setDefaultRestrictions(getDefaultManagedProfileRestrictions())
.setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
.setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter())
- .setIsCredentialSharableWithParent(true)
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
- .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE));
+ .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+ .setCredentialShareableWithParent(true));
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 60751e6..5a0c344 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -664,7 +664,7 @@
dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
break;
case MSG_DISPATCH_SHOW_RECENTS:
- showRecents();
+ showRecentApps(false);
break;
case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS:
showGlobalActionsInternal();
@@ -2927,7 +2927,7 @@
break;
case KeyEvent.KEYCODE_RECENT_APPS:
if (down && repeatCount == 0) {
- showRecents();
+ showRecentApps(false /* triggeredFromAltTab */);
}
return key_consumed;
case KeyEvent.KEYCODE_APP_SWITCH:
@@ -3110,23 +3110,22 @@
}
break;
case KeyEvent.KEYCODE_TAB:
- if (down) {
- if (event.isMetaPressed()) {
- if (!keyguardOn && isUserSetupComplete()) {
- showRecents();
+ if (down && event.isMetaPressed()) {
+ if (!keyguardOn && isUserSetupComplete()) {
+ showRecentApps(false);
+ return key_consumed;
+ }
+ } else if (down && repeatCount == 0) {
+ // Display task switcher for ALT-TAB.
+ if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
+ final int shiftlessModifiers =
+ event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
+ if (KeyEvent.metaStateHasModifiers(
+ shiftlessModifiers, KeyEvent.META_ALT_ON)) {
+ mRecentAppsHeldModifiers = shiftlessModifiers;
+ showRecentApps(true);
return key_consumed;
}
- } else {
- // Display task switcher for ALT-TAB.
- if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
- final int modifiers = event.getModifiers();
- if (KeyEvent.metaStateHasModifiers(modifiers, KeyEvent.META_ALT_ON)) {
- mRecentAppsHeldModifiers = modifiers;
- showRecentsFromAltTab(KeyEvent.metaStateHasModifiers(modifiers,
- KeyEvent.META_SHIFT_ON));
- return key_consumed;
- }
- }
}
}
break;
@@ -3662,19 +3661,11 @@
mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS).sendToTarget();
}
- private void showRecents() {
+ private void showRecentApps(boolean triggeredFromAltTab) {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
- statusbar.showRecentApps(false /* triggeredFromAltTab */, false /* forward */);
- }
- }
-
- private void showRecentsFromAltTab(boolean forward) {
- mPreloadedRecentApps = false; // preloading no longer needs to be canceled
- StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
- if (statusbar != null) {
- statusbar.showRecentApps(true /* triggeredFromAltTab */, forward);
+ statusbar.showRecentApps(triggeredFromAltTab);
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index fd64c75..7beb1ed 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -36,12 +36,14 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.SystemConfig;
import com.android.server.pm.ApexManager;
import java.io.BufferedReader;
@@ -358,6 +360,13 @@
private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
@FailureReasons int rollbackReason) {
assertInWorkerThread();
+
+ if (isAutomaticRollbackDenied(SystemConfig.getInstance(), failedPackage)) {
+ Slog.d(TAG, "Automatic rollback not allowed for package "
+ + failedPackage.getPackageName());
+ return;
+ }
+
final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
final String failedPackageToLog;
@@ -420,6 +429,17 @@
}
/**
+ * Returns true if this package is not eligible for automatic rollback.
+ */
+ @VisibleForTesting
+ @AnyThread
+ public static boolean isAutomaticRollbackDenied(SystemConfig systemConfig,
+ VersionedPackage versionedPackage) {
+ return systemConfig.getAutomaticRollbackDenylistedPackages()
+ .contains(versionedPackage.getPackageName());
+ }
+
+ /**
* Two-phase rollback:
* 1. roll back rebootless apexes first
* 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index f973f5c..8068c6f 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -249,7 +249,7 @@
targetDir.mkdirs();
File targetFile = new File(targetDir, sourceFile.getName());
- boolean fallbackToCopy = !isLinkPossible(sourceFile, targetFile);
+ boolean fallbackToCopy = !isLinkPossible(sourceFile, targetDir);
if (!fallbackToCopy) {
try {
// Create a hard link to avoid copy
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 411b2fa..e148a48 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -72,8 +72,7 @@
new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE
- | Context.BIND_INCLUDE_CAPABILITIES
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+ | Context.BIND_INCLUDE_CAPABILITIES,
userId,
IRecognitionService.Stub::asInterface);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 05b3ce7..5521384 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -39,7 +39,7 @@
void cancelPreloadRecentApps();
- void showRecentApps(boolean triggeredFromAltTab, boolean forward);
+ void showRecentApps(boolean triggeredFromAltTab);
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 0f49981..83f4805 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -454,10 +454,10 @@
}
@Override
- public void showRecentApps(boolean triggeredFromAltTab, boolean forward) {
+ public void showRecentApps(boolean triggeredFromAltTab) {
if (mBar != null) {
try {
- mBar.showRecentApps(triggeredFromAltTab, forward);
+ mBar.showRecentApps(triggeredFromAltTab);
} catch (RemoteException ex) {}
}
}
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index ca4a32f..099c9ae 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -16,9 +16,6 @@
package com.android.server.vcn;
-import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
@@ -48,7 +45,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import java.util.ArrayList;
@@ -109,6 +105,12 @@
@NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
+ @NonNull
+ private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+ (int logicalSlotIndex, int subscriptionId, int carrierId, int specificCarrierId) ->
+ handleActionCarrierConfigChanged(logicalSlotIndex, subscriptionId);
+
+
public TelephonySubscriptionTracker(
@NonNull Context context,
@NonNull Handler handler,
@@ -149,13 +151,14 @@
public void register() {
final HandlerExecutor executor = new HandlerExecutor(mHandler);
final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);
mContext.registerReceiver(this, filter, null, mHandler);
mSubscriptionManager.addOnSubscriptionsChangedListener(
executor, mSubscriptionChangedListener);
mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
+ mCarrierConfigManager.registerCarrierConfigChangeListener(executor,
+ mCarrierConfigChangeListener);
registerCarrierPrivilegesCallbacks();
}
@@ -197,6 +200,7 @@
mContext.unregisterReceiver(this);
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
unregisterCarrierPrivilegesCallbacks();
}
@@ -273,7 +277,7 @@
}
/**
- * Broadcast receiver for ACTION_CARRIER_CONFIG_CHANGED
+ * Broadcast receiver for ACTION_MULTI_SIM_CONFIG_CHANGED
*
* <p>The broadcast receiver is registered with mHandler, so callbacks & broadcasts are all
* serialized on mHandler, avoiding the need for locking.
@@ -281,9 +285,6 @@
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
- case ACTION_CARRIER_CONFIG_CHANGED:
- handleActionCarrierConfigChanged(context, intent);
- break;
case ACTION_MULTI_SIM_CONFIG_CHANGED:
handleActionMultiSimConfigChanged(context, intent);
break;
@@ -310,26 +311,21 @@
handleSubscriptionsChanged();
}
- private void handleActionCarrierConfigChanged(Context context, Intent intent) {
- // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
- // already was for an identified carrier, we can stop waiting for initial load to complete
- final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
- final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
-
+ private void handleActionCarrierConfigChanged(int slotId, int subId) {
if (slotId == INVALID_SIM_SLOT_INDEX) {
return;
}
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
+ // Get only configs as needed to save memory.
+ final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
- final PersistableBundle minimized =
- PersistableBundleUtils.minimizeBundle(
- carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
- if (minimized != null) {
- mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized));
+ if (!carrierConfig.isEmpty()) {
+ mSubIdToCarrierConfigMap.put(subId,
+ new PersistableBundleWrapper(carrierConfig));
}
handleSubscriptionsChanged();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
new file mode 100644
index 0000000..49b125c
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -0,0 +1,290 @@
+/*
+ * 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.wallpaper;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE;
+import static com.android.server.wallpaper.WallpaperUtils.RECORD_LOCK_FILE;
+import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
+import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
+import android.graphics.Rect;
+import android.os.FileUtils;
+import android.os.SELinux;
+import android.util.Slog;
+import android.view.DisplayInfo;
+
+import com.android.server.utils.TimingsTraceAndSlog;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * Helper file for wallpaper cropping
+ * Meant to have a single instance, only used by the WallpaperManagerService
+ */
+class WallpaperCropper {
+
+ private static final String TAG = WallpaperCropper.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_CROP = true;
+
+ private final WallpaperDisplayHelper mWallpaperDisplayHelper;
+
+ WallpaperCropper(WallpaperDisplayHelper wallpaperDisplayHelper) {
+ mWallpaperDisplayHelper = wallpaperDisplayHelper;
+ }
+
+ /**
+ * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
+ * for display.
+ *
+ * This will generate the crop and write it in the file
+ */
+ void generateCrop(WallpaperData wallpaper) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.generateCrop");
+ generateCropInternal(wallpaper);
+ t.traceEnd();
+ }
+
+ private void generateCropInternal(WallpaperData wallpaper) {
+ boolean success = false;
+
+ // Only generate crop for default display.
+ final WallpaperDisplayHelper.DisplayData wpData =
+ mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
+ final Rect cropHint = new Rect(wallpaper.cropHint);
+ final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
+
+ if (DEBUG) {
+ Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
+ + Integer.toHexString(wallpaper.mWhich)
+ + " to " + wallpaper.cropFile.getName()
+ + " crop=(" + cropHint.width() + 'x' + cropHint.height()
+ + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
+ }
+
+ // Analyse the source; needed in multiple cases
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
+ if (options.outWidth <= 0 || options.outHeight <= 0) {
+ Slog.w(TAG, "Invalid wallpaper data");
+ success = false;
+ } else {
+ boolean needCrop = false;
+ boolean needScale;
+
+ // Empty crop means use the full image
+ if (cropHint.isEmpty()) {
+ cropHint.left = cropHint.top = 0;
+ cropHint.right = options.outWidth;
+ cropHint.bottom = options.outHeight;
+ } else {
+ // force the crop rect to lie within the measured bounds
+ int dx = cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0;
+ int dy = cropHint.bottom > options.outHeight
+ ? options.outHeight - cropHint.bottom : 0;
+ cropHint.offset(dx, dy);
+
+ // If the crop hint was larger than the image we just overshot. Patch things up.
+ if (cropHint.left < 0) {
+ cropHint.left = 0;
+ }
+ if (cropHint.top < 0) {
+ cropHint.top = 0;
+ }
+
+ // Don't bother cropping if what we're left with is identity
+ needCrop = (options.outHeight > cropHint.height()
+ || options.outWidth > cropHint.width());
+ }
+
+ // scale if the crop height winds up not matching the recommended metrics
+ needScale = cropHint.height() > wpData.mHeight
+ || cropHint.height() > GLHelper.getMaxTextureSize()
+ || cropHint.width() > GLHelper.getMaxTextureSize();
+
+ //make sure screen aspect ratio is preserved if width is scaled under screen size
+ if (needScale) {
+ final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
+ final int newWidth = (int) (cropHint.width() * scaleByHeight);
+ if (newWidth < displayInfo.logicalWidth) {
+ final float screenAspectRatio =
+ (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
+ cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
+ needCrop = true;
+ }
+ }
+
+ if (DEBUG_CROP) {
+ Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
+ Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
+ Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
+ Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
+ }
+
+ if (!needCrop && !needScale) {
+ // Simple case: the nominal crop fits what we want, so we take
+ // the whole thing and just copy the image file directly.
+
+ // TODO: It is not accurate to estimate bitmap size without decoding it,
+ // may be we can try to remove this optimized way in the future,
+ // that means, we will always go into the 'else' block.
+
+ success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+
+ if (!success) {
+ wallpaper.cropFile.delete();
+ // TODO: fall back to default wallpaper in this case
+ }
+
+ if (DEBUG) {
+ long estimateSize = (long) options.outWidth * options.outHeight * 4;
+ Slog.v(TAG, "Null crop of new wallpaper, estimate size="
+ + estimateSize + ", success=" + success);
+ }
+ } else {
+ // Fancy case: crop and scale. First, we decode and scale down if appropriate.
+ FileOutputStream f = null;
+ BufferedOutputStream bos = null;
+ try {
+ // This actually downsamples only by powers of two, but that's okay; we do
+ // a proper scaling blit later. This is to minimize transient RAM use.
+ // We calculate the largest power-of-two under the actual ratio rather than
+ // just let the decode take care of it because we also want to remap where the
+ // cropHint rectangle lies in the decoded [super]rect.
+ final int actualScale = cropHint.height() / wpData.mHeight;
+ int scale = 1;
+ while (2 * scale <= actualScale) {
+ scale *= 2;
+ }
+ options.inSampleSize = scale;
+ options.inJustDecodeBounds = false;
+
+ final Rect estimateCrop = new Rect(cropHint);
+ estimateCrop.scale(1f / options.inSampleSize);
+ final float hRatio = (float) wpData.mHeight / estimateCrop.height();
+ final int destHeight = (int) (estimateCrop.height() * hRatio);
+ final int destWidth = (int) (estimateCrop.width() * hRatio);
+
+ // We estimated an invalid crop, try to adjust the cropHint to get a valid one.
+ if (destWidth > GLHelper.getMaxTextureSize()) {
+ int newHeight = (int) (wpData.mHeight / hRatio);
+ int newWidth = (int) (wpData.mWidth / hRatio);
+
+ if (DEBUG) {
+ Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
+ }
+
+ estimateCrop.set(cropHint);
+ estimateCrop.left += (cropHint.width() - newWidth) / 2;
+ estimateCrop.top += (cropHint.height() - newHeight) / 2;
+ estimateCrop.right = estimateCrop.left + newWidth;
+ estimateCrop.bottom = estimateCrop.top + newHeight;
+ cropHint.set(estimateCrop);
+ estimateCrop.scale(1f / options.inSampleSize);
+ }
+
+ // We've got the safe cropHint; now we want to scale it properly to
+ // the desired rectangle.
+ // That's a height-biased operation: make it fit the hinted height.
+ final int safeHeight = (int) (estimateCrop.height() * hRatio);
+ final int safeWidth = (int) (estimateCrop.width() * hRatio);
+
+ if (DEBUG_CROP) {
+ Slog.v(TAG, "Decode parameters:");
+ Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
+ Slog.v(TAG, " down sampling=" + options.inSampleSize
+ + ", hRatio=" + hRatio);
+ Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
+ Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
+ Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
+ }
+
+ //Create a record file and will delete if ImageDecoder work well.
+ final String recordName =
+ (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
+ ? RECORD_FILE : RECORD_LOCK_FILE);
+ final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
+ record.createNewFile();
+ Slog.v(TAG, "record path =" + record.getPath()
+ + ", record name =" + record.getName());
+
+ final ImageDecoder.Source srcData =
+ ImageDecoder.createSource(wallpaper.wallpaperFile);
+ final int sampleSize = scale;
+ Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
+ decoder.setTargetSampleSize(sampleSize);
+ decoder.setCrop(estimateCrop);
+ });
+
+ record.delete();
+
+ if (cropped == null) {
+ Slog.e(TAG, "Could not decode new wallpaper");
+ } else {
+ // We are safe to create final crop with safe dimensions now.
+ final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
+ safeWidth, safeHeight, true);
+ if (DEBUG) {
+ Slog.v(TAG, "Final extract:");
+ Slog.v(TAG, " dims: w=" + wpData.mWidth
+ + " h=" + wpData.mHeight);
+ Slog.v(TAG, " out: w=" + finalCrop.getWidth()
+ + " h=" + finalCrop.getHeight());
+ }
+
+ f = new FileOutputStream(wallpaper.cropFile);
+ bos = new BufferedOutputStream(f, 32 * 1024);
+ finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
+ // don't rely on the implicit flush-at-close when noting success
+ bos.flush();
+ success = true;
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Slog.e(TAG, "Error decoding crop", e);
+ }
+ } finally {
+ IoUtils.closeQuietly(bos);
+ IoUtils.closeQuietly(f);
+ }
+ }
+ }
+
+ if (!success) {
+ Slog.e(TAG, "Unable to apply new wallpaper");
+ wallpaper.cropFile.delete();
+ }
+
+ if (wallpaper.cropFile.exists()) {
+ boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
+ if (DEBUG) {
+ Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index a380dea..f02ee66 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -27,7 +27,6 @@
import android.view.Display;
import android.view.DisplayInfo;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.WindowManagerInternal;
import java.util.function.Consumer;
@@ -36,7 +35,6 @@
*/
class WallpaperDisplayHelper {
- @VisibleForTesting
static final class DisplayData {
int mWidth = -1;
int mHeight = -1;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b146767..bf09b67 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -74,7 +74,6 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
-import android.graphics.ImageDecoder;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
@@ -109,7 +108,6 @@
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
-import android.view.DisplayInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -132,7 +130,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -308,7 +305,7 @@
}
loadSettingsLocked(wallpaper.userId, true);
}
- generateCrop(wallpaper);
+ mWallpaperCropper.generateCrop(wallpaper);
if (DEBUG) {
Slog.v(TAG, "Crop done; invoking completion callback");
}
@@ -593,233 +590,6 @@
return colors;
}
- /**
- * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
- * for display.
- */
- void generateCrop(WallpaperData wallpaper) {
- TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
- t.traceBegin("WPMS.generateCrop");
- generateCropInternal(wallpaper);
- t.traceEnd();
- }
-
- private void generateCropInternal(WallpaperData wallpaper) {
- boolean success = false;
-
- // Only generate crop for default display.
- final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
- final Rect cropHint = new Rect(wallpaper.cropHint);
- final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
-
- if (DEBUG) {
- Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
- + Integer.toHexString(wallpaper.mWhich)
- + " to " + wallpaper.cropFile.getName()
- + " crop=(" + cropHint.width() + 'x' + cropHint.height()
- + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
- }
-
- // Analyse the source; needed in multiple cases
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
- if (options.outWidth <= 0 || options.outHeight <= 0) {
- Slog.w(TAG, "Invalid wallpaper data");
- success = false;
- } else {
- boolean needCrop = false;
- boolean needScale = false;
-
- // Empty crop means use the full image
- if (cropHint.isEmpty()) {
- cropHint.left = cropHint.top = 0;
- cropHint.right = options.outWidth;
- cropHint.bottom = options.outHeight;
- } else {
- // force the crop rect to lie within the measured bounds
- cropHint.offset(
- (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
- (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
-
- // If the crop hint was larger than the image we just overshot. Patch things up.
- if (cropHint.left < 0) {
- cropHint.left = 0;
- }
- if (cropHint.top < 0) {
- cropHint.top = 0;
- }
-
- // Don't bother cropping if what we're left with is identity
- needCrop = (options.outHeight > cropHint.height()
- || options.outWidth > cropHint.width());
- }
-
- // scale if the crop height winds up not matching the recommended metrics
- needScale = cropHint.height() > wpData.mHeight
- || cropHint.height() > GLHelper.getMaxTextureSize()
- || cropHint.width() > GLHelper.getMaxTextureSize();
-
- //make sure screen aspect ratio is preserved if width is scaled under screen size
- if (needScale) {
- final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
- final int newWidth = (int) (cropHint.width() * scaleByHeight);
- if (newWidth < displayInfo.logicalWidth) {
- final float screenAspectRatio =
- (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
- cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
- needCrop = true;
- }
- }
-
- if (DEBUG_CROP) {
- Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
- Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
- Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
- Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
- }
-
- if (!needCrop && !needScale) {
- // Simple case: the nominal crop fits what we want, so we take
- // the whole thing and just copy the image file directly.
-
- // TODO: It is not accurate to estimate bitmap size without decoding it,
- // may be we can try to remove this optimized way in the future,
- // that means, we will always go into the 'else' block.
-
- success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
-
- if (!success) {
- wallpaper.cropFile.delete();
- // TODO: fall back to default wallpaper in this case
- }
-
- if (DEBUG) {
- long estimateSize = (long) options.outWidth * options.outHeight * 4;
- Slog.v(TAG, "Null crop of new wallpaper, estimate size="
- + estimateSize + ", success=" + success);
- }
- } else {
- // Fancy case: crop and scale. First, we decode and scale down if appropriate.
- FileOutputStream f = null;
- BufferedOutputStream bos = null;
- try {
- // This actually downsamples only by powers of two, but that's okay; we do
- // a proper scaling blit later. This is to minimize transient RAM use.
- // We calculate the largest power-of-two under the actual ratio rather than
- // just let the decode take care of it because we also want to remap where the
- // cropHint rectangle lies in the decoded [super]rect.
- final int actualScale = cropHint.height() / wpData.mHeight;
- int scale = 1;
- while (2 * scale <= actualScale) {
- scale *= 2;
- }
- options.inSampleSize = scale;
- options.inJustDecodeBounds = false;
-
- final Rect estimateCrop = new Rect(cropHint);
- estimateCrop.scale(1f / options.inSampleSize);
- final float hRatio = (float) wpData.mHeight / estimateCrop.height();
- final int destHeight = (int) (estimateCrop.height() * hRatio);
- final int destWidth = (int) (estimateCrop.width() * hRatio);
-
- // We estimated an invalid crop, try to adjust the cropHint to get a valid one.
- if (destWidth > GLHelper.getMaxTextureSize()) {
- int newHeight = (int) (wpData.mHeight / hRatio);
- int newWidth = (int) (wpData.mWidth / hRatio);
-
- if (DEBUG) {
- Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
- }
-
- estimateCrop.set(cropHint);
- estimateCrop.left += (cropHint.width() - newWidth) / 2;
- estimateCrop.top += (cropHint.height() - newHeight) / 2;
- estimateCrop.right = estimateCrop.left + newWidth;
- estimateCrop.bottom = estimateCrop.top + newHeight;
- cropHint.set(estimateCrop);
- estimateCrop.scale(1f / options.inSampleSize);
- }
-
- // We've got the safe cropHint; now we want to scale it properly to
- // the desired rectangle.
- // That's a height-biased operation: make it fit the hinted height.
- final int safeHeight = (int) (estimateCrop.height() * hRatio);
- final int safeWidth = (int) (estimateCrop.width() * hRatio);
-
- if (DEBUG_CROP) {
- Slog.v(TAG, "Decode parameters:");
- Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
- Slog.v(TAG, " down sampling=" + options.inSampleSize
- + ", hRatio=" + hRatio);
- Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
- Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
- Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
- }
-
- //Create a record file and will delete if ImageDecoder work well.
- final String recordName =
- (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
- ? RECORD_FILE : RECORD_LOCK_FILE);
- final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
- record.createNewFile();
- Slog.v(TAG, "record path =" + record.getPath()
- + ", record name =" + record.getName());
-
- final ImageDecoder.Source srcData =
- ImageDecoder.createSource(wallpaper.wallpaperFile);
- final int sampleSize = scale;
- Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
- decoder.setTargetSampleSize(sampleSize);
- decoder.setCrop(estimateCrop);
- });
-
- record.delete();
-
- if (cropped == null) {
- Slog.e(TAG, "Could not decode new wallpaper");
- } else {
- // We are safe to create final crop with safe dimensions now.
- final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
- safeWidth, safeHeight, true);
- if (DEBUG) {
- Slog.v(TAG, "Final extract:");
- Slog.v(TAG, " dims: w=" + wpData.mWidth
- + " h=" + wpData.mHeight);
- Slog.v(TAG, " out: w=" + finalCrop.getWidth()
- + " h=" + finalCrop.getHeight());
- }
-
- f = new FileOutputStream(wallpaper.cropFile);
- bos = new BufferedOutputStream(f, 32*1024);
- finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
- bos.flush(); // don't rely on the implicit flush-at-close when noting success
- success = true;
- }
- } catch (Exception e) {
- if (DEBUG) {
- Slog.e(TAG, "Error decoding crop", e);
- }
- } finally {
- IoUtils.closeQuietly(bos);
- IoUtils.closeQuietly(f);
- }
- }
- }
-
- if (!success) {
- Slog.e(TAG, "Unable to apply new wallpaper");
- wallpaper.cropFile.delete();
- }
-
- if (wallpaper.cropFile.exists()) {
- boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
- if (DEBUG) {
- Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
- }
- }
- }
-
private final Context mContext;
private final WindowManagerInternal mWindowManagerInternal;
private final IPackageManager mIPackageManager;
@@ -912,6 +682,7 @@
@VisibleForTesting
final WallpaperDisplayHelper mWallpaperDisplayHelper;
+ final WallpaperCropper mWallpaperCropper;
private boolean supportsMultiDisplay(WallpaperConnection connection) {
if (connection != null) {
@@ -1567,6 +1338,7 @@
DisplayManager dm = mContext.getSystemService(DisplayManager.class);
dm.registerDisplayListener(mDisplayListener, null /* handler */);
mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
+ mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
mActivityManager = mContext.getSystemService(ActivityManager.class);
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
@@ -1617,7 +1389,7 @@
if (DEBUG) {
Slog.i(TAG, "No crop; regenerating from source");
}
- generateCrop(wallpaper);
+ mWallpaperCropper.generateCrop(wallpaper);
}
// Still nothing? Fall back to default.
if (!wallpaper.cropExists()) {
@@ -3479,7 +3251,7 @@
mWallpaperMap.put(userId, wallpaper);
if (!wallpaper.cropExists()) {
if (wallpaper.sourceExists()) {
- generateCrop(wallpaper);
+ mWallpaperCropper.generateCrop(wallpaper);
} else {
Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
}
@@ -3700,7 +3472,7 @@
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ " id=" + wallpaper.wallpaperId);
if (success) {
- generateCrop(wallpaper); // based on the new image + metadata
+ mWallpaperCropper.generateCrop(wallpaper); // based on the new image + metadata
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
wallpaper, null);
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d65c2f9..2b8b59c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -901,9 +901,18 @@
* TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled.
*/
static boolean isTaskViewTask(WindowContainer wc) {
- // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
+ // Use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and
// it is not guaranteed to work this logic in the future version.
- return wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+ boolean isTaskViewTask = wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer;
+ if (isTaskViewTask) {
+ return true;
+ }
+
+ WindowContainer parent = wc.getParent();
+ boolean isParentATaskViewTask = parent != null
+ && parent instanceof Task
+ && ((Task) parent).mRemoveWithTaskOrganizer;
+ return isParentATaskViewTask;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0bd59a8..1794e2a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5264,6 +5264,11 @@
return b;
}
+ // WARNING: it says `mSurfaceControl` below, but this CHANGES meaning after construction!
+ // DisplayAreas are added in `configureSurface()` *before* `mSurfaceControl` gets replaced
+ // with a wrapper or magnification surface so they end up in the right place; however,
+ // anything added or reparented to "the display" *afterwards* needs to be reparented to
+ // `getWindowinglayer()` (unless it's an overlay DisplayArea).
return b.setName(child.getName())
.setParent(mSurfaceControl);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a76d836..1b59d8d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3271,12 +3271,17 @@
// We intend to let organizer manage task visibility but it doesn't
// have enough information until we finish shell transitions.
// In the mean time we do an easy fix here.
- final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS | CHILDREN);
+ final boolean visible = isVisible();
+ final boolean show = visible || isAnimating(TRANSITION | PARENTS | CHILDREN);
if (mSurfaceControl != null) {
if (show != mLastSurfaceShowing) {
t.setVisibility(mSurfaceControl, show);
}
}
+ // Only show the overlay if the task has other visible children
+ if (mOverlayHost != null) {
+ mOverlayHost.setVisibility(t, visible);
+ }
mLastSurfaceShowing = show;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7d3367f..7b56b0c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1650,6 +1650,12 @@
// DisplayContent is the "root", so we reinterpret it's wc as the window layer
// making the parent surface the displaycontent's surface.
return wc.getSurfaceControl();
+ } else if (wc.getParent().asDisplayContent() != null) {
+ // DisplayContent is kinda split into 2 pieces, the "real root" and the
+ // "windowing layer". So if the parent of the window is DC, then it really belongs on
+ // the windowing layer (unless it's an overlay display area, but those can't be in
+ // transitions anyways).
+ return wc.getParent().asDisplayContent().getWindowingLayer();
}
return wc.getParent().getSurfaceControl();
}
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 975b21c..88c410b 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -80,6 +80,12 @@
}
}
+ void setVisibility(SurfaceControl.Transaction t, boolean visible) {
+ if (mSurfaceControl != null) {
+ t.setVisibility(mSurfaceControl, visible);
+ }
+ }
+
void addOverlay(SurfaceControlViewHost.SurfacePackage p, SurfaceControl currentParent) {
requireOverlaySurfaceControl();
mOverlays.add(p);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8767096..bded45e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3174,7 +3174,8 @@
/**
* Moves the given display to the top. If it cannot be moved to the top this method does
- * nothing.
+ * nothing (e.g. if the display has the flag FLAG_STEAL_TOP_FOCUS_DISABLED set).
+ * @param displayId The display to move to the top.
*/
void moveDisplayToTopInternal(int displayId) {
synchronized (mGlobalLock) {
@@ -3189,14 +3190,6 @@
return;
}
- if (mPerDisplayFocusEnabled) {
- ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
- "Not moving display (displayId=%d) to top. Top focused displayId=%d. "
- + "Reason: config_perDisplayFocusEnabled", displayId,
- mRoot.getTopFocusedDisplayContent().getDisplayId());
- return;
- }
-
// Nothing prevented us from moving the display to the top. Let's do it!
displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
displayContent, true /* includingParents */);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 223352e..33af563 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -6241,7 +6241,6 @@
@Override
public void handleTapOutsideFocusInsideSelf() {
- final DisplayContent displayContent = getDisplayContent();
mWmService.moveDisplayToTopInternal(getDisplayId());
mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
}
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index d3d69ae..4af685e 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -146,6 +146,7 @@
// vmas vector, instead it iterates on it until the end.
class VmaBatchCreator {
const std::vector<Vma>* sourceVmas;
+ const int totalVmasInSource;
// This is the destination array where batched VMAs will be stored
// it gets encapsulated into a VmaBatch which is the object
// meant to be used by client code.
@@ -156,8 +157,13 @@
uint64_t currentOffset_;
public:
- VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec)
- : sourceVmas(vmasToBatch), destVmas(destVmasVec), currentIndex_(0), currentOffset_(0) {}
+ VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec,
+ int vmasInSource)
+ : sourceVmas(vmasToBatch),
+ totalVmasInSource(vmasInSource),
+ destVmas(destVmasVec),
+ currentIndex_(0),
+ currentOffset_(0) {}
int currentIndex() { return currentIndex_; }
uint64_t currentOffset() { return currentOffset_; }
@@ -177,7 +183,7 @@
// Add VMAs to the batch up until we consumed all the VMAs or
// reached any imposed limit of VMAs per batch.
- while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < vmas.size()) {
+ while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < totalVmasInSource) {
uint64_t vmaStart = vmas[currentIndex_].start + currentOffset_;
uint64_t vmaSize = vmas[currentIndex_].end - vmaStart;
uint64_t bytesAvailableInBatch = MAX_BYTES_PER_BATCH - totalBytesInBatch;
@@ -272,8 +278,9 @@
//
// If any VMA fails compaction due to -EINVAL it will be skipped and continue.
// However, if it fails for any other reason, it will bail out and forward the error
-static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
- if (vmas.empty()) {
+static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType,
+ int totalVmas) {
+ if (totalVmas == 0) {
return 0;
}
@@ -286,7 +293,7 @@
struct iovec destVmas[MAX_VMAS_PER_BATCH];
VmaBatch batch;
- VmaBatchCreator batcher(&vmas, destVmas);
+ VmaBatchCreator batcher(&vmas, destVmas, totalVmas);
int64_t totalBytesProcessed = 0;
while (batcher.createNextBatch(batch)) {
@@ -346,34 +353,53 @@
// Returns the total number of bytes compacted on success. On error
// returns process_madvise errno code or if compaction was cancelled
// it returns ERROR_COMPACTION_CANCELLED.
+//
+// Not thread safe. We reuse vectors so we assume this is called only
+// on one thread at most.
static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
cancelRunningCompaction.store(false);
-
+ static std::string mapsBuffer;
ATRACE_BEGIN("CollectVmas");
ProcMemInfo meminfo(pid);
- std::vector<Vma> pageoutVmas, coldVmas;
- auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {
+ static std::vector<Vma> pageoutVmas(2000), coldVmas(2000);
+ int coldVmaIndex = 0;
+ int pageoutVmaIndex = 0;
+ auto vmaCollectorCb = [&vmaToAdviseFunc, &pageoutVmaIndex, &coldVmaIndex](const Vma& vma) {
int advice = vmaToAdviseFunc(vma);
switch (advice) {
case MADV_COLD:
- coldVmas.push_back(vma);
+ if (coldVmaIndex < coldVmas.size()) {
+ coldVmas[coldVmaIndex] = vma;
+ } else {
+ coldVmas.push_back(vma);
+ }
+ ++coldVmaIndex;
break;
case MADV_PAGEOUT:
- pageoutVmas.push_back(vma);
+ if (pageoutVmaIndex < pageoutVmas.size()) {
+ pageoutVmas[pageoutVmaIndex] = vma;
+ } else {
+ pageoutVmas.push_back(vma);
+ }
+ ++pageoutVmaIndex;
break;
}
};
- meminfo.ForEachVmaFromMaps(vmaCollectorCb);
+ meminfo.ForEachVmaFromMaps(vmaCollectorCb, mapsBuffer);
ATRACE_END();
+#ifdef DEBUG_COMPACTION
+ ALOGE("Total VMAs sent for compaction anon=%d file=%d", pageoutVmaIndex,
+ coldVmaIndex);
+#endif
- int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
+ int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT, pageoutVmaIndex);
if (pageoutBytes < 0) {
// Error, just forward it.
cancelRunningCompaction.store(false);
return pageoutBytes;
}
- int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);
+ int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD, coldVmaIndex);
if (coldBytes < 0) {
// Error, just forward it.
cancelRunningCompaction.store(false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
deleted file mode 100644
index 834f65f..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ /dev/null
@@ -1,209 +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.server.devicepolicy;
-
-import android.accounts.Account;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyDrawableResource;
-import android.app.admin.DevicePolicySafetyChecker;
-import android.app.admin.DevicePolicyStringResource;
-import android.app.admin.FullyManagedDeviceProvisioningParams;
-import android.app.admin.IDevicePolicyManager;
-import android.app.admin.ManagedProfileProvisioningParams;
-import android.app.admin.ParcelableResource;
-import android.content.ComponentName;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.server.SystemService;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Defines the required interface for IDevicePolicyManager implemenation.
- *
- * <p>The interface consists of public parts determined by {@link IDevicePolicyManager} and also
- * several package private methods required by internal infrastructure.
- *
- * <p>Whenever adding an AIDL method to {@link IDevicePolicyManager}, an empty override method
- * should be added here to avoid build breakage in downstream branches.
- */
-abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
-
- private static final String TAG = BaseIDevicePolicyManager.class.getSimpleName();
-
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
- *
- * @see {@link SystemService#onBootPhase}.
- */
- abstract void systemReady(int phase);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when a new user starts.
- *
- * @see {@link SystemService#onUserStarting}
- */
- abstract void handleStartUser(int userId);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being unlocked.
- *
- * @see {@link SystemService#onUserUnlocking}
- */
- abstract void handleUnlockUser(int userId);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} after a user is being unlocked.
- *
- * @see {@link SystemService#onUserUnlocked}
- */
- abstract void handleOnUserUnlocked(int userId);
- /**
- * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped.
- *
- * @see {@link SystemService#onUserStopping}
- */
- abstract void handleStopUser(int userId);
-
- /**
- * Sets the {@link DevicePolicySafetyChecker}.
- *
- * <p>Currently, it's called only by {@code SystemServer} on
- * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}
- */
- public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
- Slog.w(TAG, "setDevicePolicySafetyChecker() not implemented by " + getClass());
- }
-
- public void clearSystemUpdatePolicyFreezePeriodRecord() {
- }
-
- public boolean setKeyGrantForApp(ComponentName admin, String callerPackage, String alias,
- String packageName, boolean hasGrant) {
- return false;
- }
-
- public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
-
- public boolean isOrganizationOwnedDeviceWithManagedProfile() {
- return false;
- }
-
- public int getPersonalAppsSuspendedReasons(ComponentName admin) {
- return 0;
- }
-
- public void setPersonalAppsSuspended(ComponentName admin, boolean suspended) {
- }
-
- public void setManagedProfileMaximumTimeOff(ComponentName admin, long timeoutMs) {
- }
-
- public long getManagedProfileMaximumTimeOff(ComponentName admin) {
- return 0;
- }
-
- @Override
- public void acknowledgeDeviceCompliant() {}
-
- @Override
- public boolean isComplianceAcknowledgementRequired() {
- return false;
- }
-
- public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
- return false;
- }
-
- public String getEnrollmentSpecificId(String callerPackage) {
- return "";
- }
-
- public void setOrganizationIdForUser(
- @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
-
- public UserHandle createAndProvisionManagedProfile(
- @NonNull ManagedProfileProvisioningParams provisioningParams, String callerPackage) {
- return null;
- }
-
- public void finalizeWorkProfileProvisioning(
- UserHandle managedProfileUser, Account migratedAccount) {
-
- }
-
- public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
- }
-
- @Override
- public void setDeviceOwnerType(@NonNull ComponentName admin, int deviceOwnerType) {
- }
-
- @Override
- public int getDeviceOwnerType(@NonNull ComponentName admin) {
- return 0;
- }
-
- public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
-
- public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
- return false;
- }
-
- @Override
- public boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant) {
- return false;
- }
-
- @Override
- public boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias) {
- return false;
- }
-
- @Override
- public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){}
-
- @Override
- public void resetDrawables(@NonNull List<String> drawableIds){}
-
- @Override
- public ParcelableResource getDrawable(
- String drawableId, String drawableStyle, String drawableSource) {
- return null;
- }
-
- @Override
- public void setStrings(@NonNull List<DevicePolicyStringResource> strings){}
-
- @Override
- public void resetStrings(@NonNull List<String> stringIds){}
-
- @Override
- public ParcelableResource getString(String stringId) {
- return null;
- }
-
- @Override
- public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification() {
- return false;
- }
-
- @Override
- public List<UserHandle> getPolicyManagedProfiles(UserHandle userHandle) {
- return Collections.emptyList();
- }
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1406a396..3470b04 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -221,6 +221,7 @@
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.IDevicePolicyManager;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.NetworkEvent;
@@ -442,7 +443,7 @@
/**
* Implementation of the device policy APIs.
*/
-public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
protected static final String LOG_TAG = "DevicePolicyManager";
@@ -739,6 +740,10 @@
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
+ private static final String ENABLE_WORK_PROFILE_TELEPHONY_FLAG =
+ "enable_work_profile_telephony";
+ private static final boolean DEFAULT_WORK_PROFILE_TELEPHONY_FLAG = false;
+
// TODO(b/261999445) remove the flag after rollout.
private static final String HEADLESS_FLAG = "headless";
private static final boolean DEFAULT_HEADLESS_FLAG = true;
@@ -920,23 +925,24 @@
private final ArrayList<Object> mPendingUserCreatedCallbackTokens = new ArrayList<>();
public static final class Lifecycle extends SystemService {
- private BaseIDevicePolicyManager mService;
+ private DevicePolicyManagerService mService;
public Lifecycle(Context context) {
super(context);
String dpmsClassName = context.getResources()
.getString(R.string.config_deviceSpecificDevicePolicyManagerService);
if (TextUtils.isEmpty(dpmsClassName)) {
- dpmsClassName = DevicePolicyManagerService.class.getName();
- }
- try {
- Class<?> serviceClass = Class.forName(dpmsClassName);
- Constructor<?> constructor = serviceClass.getConstructor(Context.class);
- mService = (BaseIDevicePolicyManager) constructor.newInstance(context);
- } catch (Exception e) {
- throw new IllegalStateException(
- "Failed to instantiate DevicePolicyManagerService with class name: "
- + dpmsClassName, e);
+ mService = new DevicePolicyManagerService(context);
+ } else {
+ try {
+ Class<?> serviceClass = Class.forName(dpmsClassName);
+ Constructor<?> constructor = serviceClass.getConstructor(Context.class);
+ mService = (DevicePolicyManagerService) constructor.newInstance(context);
+ } catch (Exception e) {
+ throw new IllegalStateException(
+ "Failed to instantiate DevicePolicyManagerService with class name: "
+ + dpmsClassName, e);
+ }
}
}
@@ -1095,13 +1101,13 @@
// (ACTION_DATE_CHANGED), or when manual clock adjustment is made
// (ACTION_TIME_CHANGED)
updateSystemUpdateFreezePeriodsRecord(/* saveIfChanged */ true);
- final int userId = getManagedUserId(mUserManager.getMainUser().getIdentifier());
+ final int userId = getManagedUserId(getMainUserId());
if (userId >= 0) {
updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
}
} else if (ACTION_PROFILE_OFF_DEADLINE.equals(action)) {
Slogf.i(LOG_TAG, "Profile off deadline alarm was triggered");
- final int userId = getManagedUserId(mUserManager.getMainUser().getIdentifier());
+ final int userId = getManagedUserId(getMainUserId());
if (userId >= 0) {
updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
} else {
@@ -1348,7 +1354,6 @@
}
}
- @Override
public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
CallerIdentity callerIdentity = getCallerIdentity();
Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set "
@@ -3090,7 +3095,6 @@
}
@VisibleForTesting
- @Override
void systemReady(int phase) {
if (!mHasFeature) {
return;
@@ -3100,7 +3104,9 @@
onLockSettingsReady();
loadAdminDataAsync();
mOwners.systemReady();
- applyManagedSubscriptionsPolicyIfRequired();
+ if (isWorkProfileTelephonyFlagEnabled()) {
+ applyManagedSubscriptionsPolicyIfRequired();
+ }
break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
@@ -3289,7 +3295,6 @@
}
}
- @Override
void handleStartUser(int userId) {
synchronized (getLockObject()) {
pushScreenCapturePolicy(userId);
@@ -3337,7 +3342,6 @@
targetUserId, protectedPackages));
}
- @Override
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
if (isCoexistenceFlagEnabled()) {
@@ -3345,12 +3349,10 @@
}
}
- @Override
void handleOnUserUnlocked(int userId) {
showNewUserDisclaimerIfNecessary(userId);
}
- @Override
void handleStopUser(int userId) {
updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
@@ -7018,8 +7020,9 @@
}
mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
- clearManagedSubscriptionsPolicy();
-
+ if (isWorkProfileTelephonyFlagEnabled()) {
+ clearManagedSubscriptionsPolicy();
+ }
Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
}
@@ -8879,6 +8882,15 @@
}
}
+ private @UserIdInt int getMainUserId() {
+ UserHandle mainUser = mUserManager.getMainUser();
+ if (mainUser == null) {
+ Slogf.d(LOG_TAG, "getMainUserId(): no main user, returning USER_SYSTEM");
+ return UserHandle.USER_SYSTEM;
+ }
+ return mainUser.getIdentifier();
+ }
+
// TODO(b/240562946): Remove api as owner name is not used.
/**
* Returns the "name" of the device owner. It'll work for non-DO users too, but requires
@@ -10123,6 +10135,9 @@
synchronized (mSubscriptionsChangedListenerLock) {
pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
}
+ pw.println(
+ "Flag enable_work_profile_telephony : " + isWorkProfileTelephonyFlagEnabled());
+
mHandler.post(() -> handleDump(pw));
dumpResources(pw);
}
@@ -20095,6 +20110,13 @@
DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
}
+ private static boolean isWorkProfileTelephonyFlagEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_DEVICE_POLICY_MANAGER,
+ ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
+ DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+ }
+
@Override
public void setMtePolicy(int flags) {
final Set<Integer> allowedModes =
@@ -20175,10 +20197,12 @@
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
- synchronized (getLockObject()) {
- ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
- if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
- return admin.mManagedSubscriptionsPolicy;
+ if (isWorkProfileTelephonyFlagEnabled()) {
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
+ if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
+ return admin.mManagedSubscriptionsPolicy;
+ }
}
}
return new ManagedSubscriptionsPolicy(
@@ -20187,9 +20211,13 @@
@Override
public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
+ if (!isWorkProfileTelephonyFlagEnabled()) {
+ throw new UnsupportedOperationException("This api is not enabled");
+ }
CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller),
- "This policy can only be set by a profile owner on an organization-owned device.");
+ "This policy can only be set by a profile owner on an organization-owned "
+ + "device.");
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
@@ -20307,4 +20335,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3725756..a15c6d2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1021,9 +1021,7 @@
Runnable runnable = new Runnable() {
@Override
public void run() {
- synchronized (this) {
- ShutdownThread.rebootOrShutdown(null, reboot, reason);
- }
+ ShutdownThread.rebootOrShutdown(null, reboot, reason);
}
};
@@ -1446,6 +1444,9 @@
boolean isArc = context.getPackageManager().hasSystemFeature(
"org.chromium.arc");
+ boolean isTv = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK);
+
boolean enableVrService = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 681bfcf..f915298 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -51,6 +51,7 @@
"kotlin-test",
"mockingservicestests-utils-mockito",
"mockito-target-extended-minus-junit4",
+ "platform-compat-test-rules",
"platform-test-annotations",
"service-blobstore",
"service-jobscheduler",
diff --git a/services/tests/servicestests/res/raw/backup_file_with_long_name b/services/tests/mockingservicestests/res/raw/backup_file_with_long_name
similarity index 100%
rename from services/tests/servicestests/res/raw/backup_file_with_long_name
rename to services/tests/mockingservicestests/res/raw/backup_file_with_long_name
Binary files differ
diff --git a/services/tests/servicestests/res/raw/backup_telephony_no_password b/services/tests/mockingservicestests/res/raw/backup_telephony_no_password
similarity index 100%
rename from services/tests/servicestests/res/raw/backup_telephony_no_password
rename to services/tests/mockingservicestests/res/raw/backup_telephony_no_password
Binary files differ
diff --git a/services/tests/servicestests/res/raw/backup_telephony_with_password b/services/tests/mockingservicestests/res/raw/backup_telephony_with_password
similarity index 100%
rename from services/tests/servicestests/res/raw/backup_telephony_with_password
rename to services/tests/mockingservicestests/res/raw/backup_telephony_with_password
Binary files differ
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 05cad16..64e39ef 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -194,7 +194,7 @@
public void init_withDeviceConfigSetsParameters() {
// When the DeviceConfig already has a flag value stored (note this test will need to
// change if the default value changes from false).
- assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isFalse();
+ assertThat(CachedAppOptimizer.DEFAULT_USE_COMPACTION).isTrue();
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -372,9 +372,8 @@
CachedAppOptimizer.KEY_USE_COMPACTION, "foobar", false);
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- // Then we set the default.
- assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
- CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ // Invalid value is mapped to false
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/DataChangedJournalTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/DataChangedJournalTest.java
index 9daa4ba..4b0b760 100644
--- a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/DataChangedJournalTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.backup;
diff --git a/services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/ProcessedPackagesJournalTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
index 5800aca..2c5ae37 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
diff --git a/services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/internal/LifecycleOperationStorageTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
index 00c6391..4d6845c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformAdbRestoreTaskTest.java
@@ -25,7 +25,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.servicestests.R;
+import com.android.frameworks.mockingservicestests.R;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/mockingservicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 48b0aad..6093f4b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -58,7 +58,7 @@
@RunWith(AndroidJUnit4.class)
public class BackupEligibilityRulesTest {
private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent";
- private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests";
+ private static final String TEST_PACKAGE_NAME = "com.android.frameworks.mockingservicestests";
private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupObserverUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FileUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/FileUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupRestoreObserverUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index e2536f8..30c6975 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -51,7 +51,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.servicestests.R;
+import com.android.frameworks.mockingservicestests.R;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.restore.PerformAdbRestoreTask;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index f2cba40..2a790a1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -80,6 +80,8 @@
@Mock
private DisplayBlanker mDisplayBlankerMock;
@Mock
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
+ @Mock
private LogicalDisplay mLogicalDisplayMock;
@Mock
private DisplayDevice mDisplayDeviceMock;
@@ -169,7 +171,7 @@
mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
- });
+ }, mHighBrightnessModeMetadataMock);
when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 4f8cb88..d99ed78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -84,6 +84,8 @@
@Mock
private DisplayDevice mDisplayDeviceMock;
@Mock
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
+ @Mock
private BrightnessTracker mBrightnessTrackerMock;
@Mock
private BrightnessSetting mBrightnessSettingMock;
@@ -151,7 +153,7 @@
mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
- });
+ }, mHighBrightnessModeMetadataMock);
when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 19b5ad6..de54537 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -427,7 +427,8 @@
doReturn(true).when(mService)
.bindWallpaperComponentLocked(any(), anyBoolean(), anyBoolean(), any(), any());
doNothing().when(mService).saveSettingsLocked(wallpaper.userId);
- doNothing().when(mService).generateCrop(wallpaper);
+ spyOn(mService.mWallpaperCropper);
+ doNothing().when(mService.mWallpaperCropper).generateCrop(wallpaper);
// timestamps of {ACTION_WALLPAPER_CHANGED, onWallpaperColorsChanged}
final long[] timestamps = new long[2];
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 80305fa..ced2a4b 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -60,7 +60,6 @@
"truth-prebuilt",
"junit",
"junit-params",
- "platform-compat-test-rules",
"ActivityContext",
"coretests-aidl",
],
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 450cc40..908e717 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -36,6 +36,8 @@
useParentsContacts='false'
crossProfileIntentFilterAccessControl='20'
crossProfileIntentResolutionStrategy='0'
+ mediaSharedWithParent='true'
+ credentialShareableWithParent='false'
/>
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
diff --git a/services/tests/servicestests/src/com/android/server/backup/OWNERS b/services/tests/servicestests/src/com/android/server/backup/OWNERS
deleted file mode 100644
index d99779e..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/backup/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
new file mode 100644
index 0000000..c7fb97f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.Presubmit;
+import android.telecom.Call;
+import android.telecom.ParcelableCall;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CrossDeviceCallTest {
+
+ private static final String CALLER_DISPLAY_NAME = "name";
+ private static final String CONTACT_DISPLAY_NAME = "contact";
+
+ @Test
+ public void updateCallDetails_uninitialized() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()).isEmpty();
+ }
+
+ @Test
+ public void updateCallDetails_ringing() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.RINGING);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT,
+ android.companion.Telecom.Call.REJECT,
+ android.companion.Telecom.Call.SILENCE));
+ }
+
+ @Test
+ public void updateCallDetails_ongoing() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.ONGOING);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+ android.companion.Telecom.Call.MUTE,
+ android.companion.Telecom.Call.PUT_ON_HOLD));
+ }
+
+ @Test
+ public void updateCallDetails_holding() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_HOLDING,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.ON_HOLD);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+ android.companion.Telecom.Call.TAKE_OFF_HOLD));
+ }
+
+ @Test
+ public void updateCallDetails_cannotHold() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_MUTE));
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.ONGOING);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+ android.companion.Telecom.Call.MUTE));
+ }
+
+ @Test
+ public void updateCallDetails_cannotMute() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD));
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.ONGOING);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+ android.companion.Telecom.Call.PUT_ON_HOLD));
+ }
+
+ @Test
+ public void updateCallDetails_transitionRingingToOngoing() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ assertWithMessage("Wrong status for ringing state").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.RINGING);
+ assertWithMessage("Wrong controls for ringing state").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT,
+ android.companion.Telecom.Call.REJECT,
+ android.companion.Telecom.Call.SILENCE));
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ assertWithMessage("Wrong status for active state").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.ONGOING);
+ assertWithMessage("Wrong controls for active state").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+ android.companion.Telecom.Call.MUTE,
+ android.companion.Telecom.Call.PUT_ON_HOLD));
+ }
+
+ @Test
+ public void updateSilencedIfRinging_ringing_silenced() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ crossDeviceCall.updateSilencedIfRinging();
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.RINGING_SILENCED);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT,
+ android.companion.Telecom.Call.REJECT));
+ }
+
+ @Test
+ public void updateSilencedIfRinging_notRinging_notSilenced() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
+ Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
+ crossDeviceCall.updateSilencedIfRinging();
+ assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.ONGOING);
+ assertWithMessage("Wrong controls").that(crossDeviceCall.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.END,
+ android.companion.Telecom.Call.MUTE,
+ android.companion.Telecom.Call.PUT_ON_HOLD));
+ }
+
+ @Test
+ public void getReadableCallerId_enterpriseCall_adminBlocked_ott() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = true;
+ crossDeviceCall.mIsOtt = true;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(true);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CALLER_DISPLAY_NAME);
+ }
+
+ @Test
+ public void getReadableCallerId_enterpriseCall_adminUnblocked_ott() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = true;
+ crossDeviceCall.mIsOtt = true;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(false);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CALLER_DISPLAY_NAME);
+ }
+
+ @Test
+ public void getReadableCallerId_enterpriseCall_adminBlocked_pstn() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = true;
+ crossDeviceCall.mIsOtt = false;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(true);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CALLER_DISPLAY_NAME);
+ }
+
+ @Test
+ public void getReadableCallerId_nonEnterpriseCall_adminBlocked_ott() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = false;
+ crossDeviceCall.mIsOtt = true;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(true);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CALLER_DISPLAY_NAME);
+ }
+
+ @Test
+ public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_ott() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = false;
+ crossDeviceCall.mIsOtt = true;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(false);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CALLER_DISPLAY_NAME);
+ }
+
+ @Test
+ public void getReadableCallerId_nonEnterpriseCall_adminBlocked_pstn() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = false;
+ crossDeviceCall.mIsOtt = false;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(true);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CONTACT_DISPLAY_NAME);
+ }
+
+ @Test
+ public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_pstn() {
+ final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
+ InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
+ null, /* callAudioState= */ null);
+ crossDeviceCall.mIsEnterprise = false;
+ crossDeviceCall.mIsOtt = false;
+ crossDeviceCall.updateCallDetails(
+ createCallDetails(Call.STATE_ACTIVE, /* capabilities= */ 0));
+
+ final String result = crossDeviceCall.getReadableCallerId(false);
+
+ assertWithMessage("Wrong caller id").that(result)
+ .isEqualTo(CONTACT_DISPLAY_NAME);
+ }
+
+ private Call.Details createCallDetails(int state, int capabilities) {
+ final ParcelableCall.ParcelableCallBuilder parcelableCallBuilder =
+ new ParcelableCall.ParcelableCallBuilder();
+ parcelableCallBuilder.setCallerDisplayName(CALLER_DISPLAY_NAME);
+ parcelableCallBuilder.setContactDisplayName(CONTACT_DISPLAY_NAME);
+ parcelableCallBuilder.setCapabilities(capabilities);
+ parcelableCallBuilder.setState(state);
+ parcelableCallBuilder.setConferenceableCallIds(Collections.emptyList());
+ return Call.Details.createFromParcelableCall(parcelableCallBuilder.createParcelableCall());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 6a4435f..dad7977 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -634,6 +634,8 @@
@Test
public void onAppsOnVirtualDeviceChanged_multipleVirtualDevices_listenersNotified() {
+ createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2);
+
ArraySet<Integer> uidsOnDevice1 = new ArraySet<>(Arrays.asList(UID_1, UID_2));
ArraySet<Integer> uidsOnDevice2 = new ArraySet<>(Arrays.asList(UID_3, UID_4));
mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
@@ -645,7 +647,7 @@
new ArraySet<>(Arrays.asList(UID_1, UID_2)));
// Notifies that the running apps on the second virtual device has changed.
- mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId() + 1, uidsOnDevice2);
+ mVdms.notifyRunningAppsChanged(VIRTUAL_DEVICE_ID_2, uidsOnDevice2);
TestableLooper.get(this).processAllMessages();
// The union of the apps running on both virtual devices are sent to the listeners.
verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(
@@ -1059,6 +1061,16 @@
}
@Test
+ public void closedDevice_lateCallToRunningAppsChanged_isIgnored() {
+ mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
+ int deviceId = mDeviceImpl.getDeviceId();
+ mDeviceImpl.close();
+ mVdms.notifyRunningAppsChanged(deviceId, Sets.newArraySet(UID_1));
+ TestableLooper.get(this).processAllMessages();
+ verify(mAppsOnVirtualDeviceListener, never()).onAppsOnAnyVirtualDeviceChanged(any());
+ }
+
+ @Test
public void sendKeyEvent_noFd() {
assertThrows(
IllegalArgumentException.class,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4998a6c..60483f1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -134,6 +134,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.security.KeyChain;
import android.security.keystore.AttestationUtils;
@@ -259,6 +260,8 @@
private static final String PROFILE_OFF_SUSPENSION_TITLE = "suspension_title";
private static final String PROFILE_OFF_SUSPENSION_TEXT = "suspension_text";
private static final String PROFILE_OFF_SUSPENSION_SOON_TEXT = "suspension_tomorrow_text";
+ private static final String FLAG_ENABLE_WORK_PROFILE_TELEPHONY =
+ "enable_work_profile_telephony";
@Before
public void setUp() throws Exception {
@@ -4982,7 +4985,8 @@
public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
-
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
// Even if the caller is the managed profile, the current user is the user 0
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
@@ -5043,6 +5047,8 @@
verify(getServices().packageManagerInternal)
.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
new file mode 100644
index 0000000..24fc348
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HbmEventTest {
+ private long mStartTimeMillis;
+ private long mEndTimeMillis;
+ private HbmEvent mHbmEvent;
+
+ @Before
+ public void setUp() {
+ mStartTimeMillis = 10;
+ mEndTimeMillis = 20;
+ mHbmEvent = new HbmEvent(mStartTimeMillis, mEndTimeMillis);
+ }
+
+ @Test
+ public void getCorrectValues() {
+ assertEquals(mHbmEvent.getStartTimeMillis(), mStartTimeMillis);
+ assertEquals(mHbmEvent.getEndTimeMillis(), mEndTimeMillis);
+ }
+
+ @Test
+ public void toStringGeneratesExpectedString() {
+ String actualString = mHbmEvent.toString();
+ String expectedString = "HbmEvent: {startTimeMillis:" + mStartTimeMillis
+ + ", endTimeMillis: " + mEndTimeMillis + "}, total: "
+ + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
+ assertEquals(actualString, expectedString);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index a1e5ce7..2655c3f 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -96,6 +96,7 @@
private Binder mDisplayToken;
private String mDisplayUniqueId;
private Context mContextSpy;
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -118,6 +119,7 @@
mTestLooper = new TestLooper(mClock::now);
mDisplayToken = null;
mDisplayUniqueId = "unique_id";
+
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -134,7 +136,8 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
+ null, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@@ -144,7 +147,8 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
+ null, mContextSpy);
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -699,9 +703,12 @@
// Creates instance with standard initialization values.
private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
initHandler(clock);
+ if (mHighBrightnessModeMetadata == null) {
+ mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
+ }
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
- DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
+ DEFAULT_HBM_DATA, null, () -> {}, mHighBrightnessModeMetadata, mContextSpy);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
new file mode 100644
index 0000000..ede54e0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HighBrightnessModeMetadataTest {
+ private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
+
+ private long mRunningStartTimeMillis = -1;
+
+ @Before
+ public void setUp() {
+ mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
+ }
+
+ @Test
+ public void checkDefaultValues() {
+ assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
+ mRunningStartTimeMillis);
+ assertEquals(mHighBrightnessModeMetadata.getHbmEventQueue().size(), 0);
+ }
+
+ @Test
+ public void checkSetValues() {
+ mRunningStartTimeMillis = 10;
+ mHighBrightnessModeMetadata.setRunningStartTimeMillis(mRunningStartTimeMillis);
+ assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
+ mRunningStartTimeMillis);
+ HbmEvent expectedHbmEvent = new HbmEvent(10, 20);
+ mHighBrightnessModeMetadata.addHbmEvent(expectedHbmEvent);
+ HbmEvent actualHbmEvent = mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst();
+ assertEquals(expectedHbmEvent.toString(), actualHbmEvent.toString());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index cbeaf7b..c24d83f 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -16,18 +16,26 @@
package com.android.server.display.brightness;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.PowerManager;
import android.view.Display;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.display.BrightnessSetting;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy;
import org.junit.Before;
import org.junit.Test;
@@ -39,11 +47,16 @@
@RunWith(AndroidJUnit4.class)
public final class DisplayBrightnessControllerTest {
private static final int DISPLAY_ID = 1;
+ private static final float DEFAULT_BRIGHTNESS = 0.4f;
@Mock
private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector;
@Mock
private Context mContext;
+ @Mock
+ private BrightnessSetting mBrightnessSetting;
+ @Mock
+ private Runnable mOnBrightnessChangeRunnable;
private DisplayBrightnessController mDisplayBrightnessController;
@@ -58,11 +71,11 @@
}
};
mDisplayBrightnessController = new DisplayBrightnessController(mContext, injector,
- DISPLAY_ID);
+ DISPLAY_ID, DEFAULT_BRIGHTNESS, mBrightnessSetting, mOnBrightnessChangeRunnable);
}
@Test
- public void updateBrightnessWorksAsExpected() {
+ public void updateBrightness() {
DisplayPowerRequest displayPowerRequest = mock(DisplayPowerRequest.class);
DisplayBrightnessStrategy displayBrightnessStrategy = mock(DisplayBrightnessStrategy.class);
int targetDisplayState = Display.STATE_DOZE;
@@ -70,6 +83,8 @@
targetDisplayState)).thenReturn(displayBrightnessStrategy);
mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState);
verify(displayBrightnessStrategy).updateBrightness(displayPowerRequest);
+ assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategyLocked(),
+ displayBrightnessStrategy);
}
@Test
@@ -77,4 +92,154 @@
mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
verify(mDisplayBrightnessStrategySelector).isAllowAutoBrightnessWhileDozingConfig();
}
+
+ @Test
+ public void setTemporaryBrightness() {
+ float temporaryBrightness = 0.4f;
+ TemporaryBrightnessStrategy temporaryBrightnessStrategy = mock(
+ TemporaryBrightnessStrategy.class);
+ when(mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()).thenReturn(
+ temporaryBrightnessStrategy);
+ mDisplayBrightnessController.setTemporaryBrightness(temporaryBrightness);
+ verify(temporaryBrightnessStrategy).setTemporaryScreenBrightness(temporaryBrightness);
+ }
+
+ @Test
+ public void setCurrentScreenBrightness() {
+ // Current Screen brightness is set as expected when a different value than the current
+ // is set
+ float currentScreenBrightness = 0.4f;
+ mDisplayBrightnessController.setCurrentScreenBrightness(currentScreenBrightness);
+ assertEquals(mDisplayBrightnessController.getCurrentBrightness(),
+ currentScreenBrightness, 0.0f);
+ verify(mOnBrightnessChangeRunnable).run();
+
+ // No change to the current screen brightness is same as the existing one
+ mDisplayBrightnessController.setCurrentScreenBrightness(currentScreenBrightness);
+ verifyNoMoreInteractions(mOnBrightnessChangeRunnable);
+ }
+
+ @Test
+ public void setPendingScreenBrightnessSetting() {
+ float pendingScreenBrightness = 0.4f;
+ mDisplayBrightnessController.setPendingScreenBrightness(pendingScreenBrightness);
+ assertEquals(mDisplayBrightnessController.getPendingScreenBrightness(),
+ pendingScreenBrightness, 0.0f);
+ }
+
+ @Test
+ public void updateUserSetScreenBrightness() {
+ // No brightness is set if the pending brightness is invalid
+ mDisplayBrightnessController.setPendingScreenBrightness(Float.NaN);
+ assertFalse(mDisplayBrightnessController.updateUserSetScreenBrightness());
+
+ // user set brightness is not set if the current and the pending brightness are same.
+ float currentBrightness = 0.4f;
+ TemporaryBrightnessStrategy temporaryBrightnessStrategy = mock(
+ TemporaryBrightnessStrategy.class);
+ when(mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy()).thenReturn(
+ temporaryBrightnessStrategy);
+ mDisplayBrightnessController.setCurrentScreenBrightness(currentBrightness);
+ mDisplayBrightnessController.setPendingScreenBrightness(currentBrightness);
+ mDisplayBrightnessController.setTemporaryBrightness(currentBrightness);
+ assertFalse(mDisplayBrightnessController.updateUserSetScreenBrightness());
+ verify(temporaryBrightnessStrategy).setTemporaryScreenBrightness(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertEquals(mDisplayBrightnessController.getPendingScreenBrightness(),
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, 0.0f);
+
+ // user set brightness is set as expected
+ currentBrightness = 0.4f;
+ float pendingScreenBrightness = 0.3f;
+ float temporaryScreenBrightness = 0.2f;
+ mDisplayBrightnessController.setCurrentScreenBrightness(currentBrightness);
+ mDisplayBrightnessController.setPendingScreenBrightness(pendingScreenBrightness);
+ mDisplayBrightnessController.setTemporaryBrightness(temporaryScreenBrightness);
+ assertTrue(mDisplayBrightnessController.updateUserSetScreenBrightness());
+ assertEquals(mDisplayBrightnessController.getCurrentBrightness(),
+ pendingScreenBrightness, 0.0f);
+ assertEquals(mDisplayBrightnessController.getLastUserSetScreenBrightness(),
+ pendingScreenBrightness, 0.0f);
+ verify(mOnBrightnessChangeRunnable, times(2)).run();
+ verify(temporaryBrightnessStrategy, times(2))
+ .setTemporaryScreenBrightness(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertEquals(mDisplayBrightnessController.getPendingScreenBrightness(),
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, 0.0f);
+ }
+
+ @Test
+ public void registerBrightnessSettingChangeListener() {
+ BrightnessSetting.BrightnessSettingListener brightnessSettingListener = mock(
+ BrightnessSetting.BrightnessSettingListener.class);
+ mDisplayBrightnessController.registerBrightnessSettingChangeListener(
+ brightnessSettingListener);
+ verify(mBrightnessSetting).registerListener(brightnessSettingListener);
+ assertEquals(mDisplayBrightnessController.getBrightnessSettingListenerLocked(),
+ brightnessSettingListener);
+ }
+
+ @Test
+ public void getScreenBrightnessSetting() {
+ // getScreenBrightnessSetting returns the value relayed by BrightnessSetting, if the
+ // valid is valid and in range
+ float brightnessSetting = 0.2f;
+ when(mBrightnessSetting.getBrightness()).thenReturn(brightnessSetting);
+ assertEquals(mDisplayBrightnessController.getScreenBrightnessSetting(), brightnessSetting,
+ 0.0f);
+
+ // getScreenBrightnessSetting value is clamped if BrightnessSetting returns value beyond max
+ brightnessSetting = 1.1f;
+ when(mBrightnessSetting.getBrightness()).thenReturn(brightnessSetting);
+ assertEquals(mDisplayBrightnessController.getScreenBrightnessSetting(), 1.0f,
+ 0.0f);
+
+ // getScreenBrightnessSetting returns default value is BrightnessSetting returns invalid
+ // value.
+ brightnessSetting = Float.NaN;
+ when(mBrightnessSetting.getBrightness()).thenReturn(brightnessSetting);
+ assertEquals(mDisplayBrightnessController.getScreenBrightnessSetting(), DEFAULT_BRIGHTNESS,
+ 0.0f);
+ }
+
+ @Test
+ public void setBrightnessSetsInBrightnessSetting() {
+ float brightnessValue = 0.3f;
+ mDisplayBrightnessController.setBrightness(brightnessValue);
+ verify(mBrightnessSetting).setBrightness(brightnessValue);
+ }
+
+ @Test
+ public void updateScreenBrightnessSetting() {
+ // This interaction happens in the constructor itself
+ verify(mBrightnessSetting).getBrightness();
+
+ // Sets the appropriate value when valid, and not equal to the current brightness
+ float brightnessValue = 0.3f;
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ assertEquals(mDisplayBrightnessController.getCurrentBrightness(), brightnessValue,
+ 0.0f);
+ verify(mOnBrightnessChangeRunnable).run();
+ verify(mBrightnessSetting).setBrightness(brightnessValue);
+
+ // Does nothing if the value is invalid
+ mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
+ verifyNoMoreInteractions(mOnBrightnessChangeRunnable, mBrightnessSetting);
+
+ // Does nothing if the value is same as the current brightness
+ brightnessValue = 0.2f;
+ mDisplayBrightnessController.setCurrentScreenBrightness(brightnessValue);
+ verify(mOnBrightnessChangeRunnable, times(2)).run();
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ verifyNoMoreInteractions(mOnBrightnessChangeRunnable, mBrightnessSetting);
+ }
+
+ @Test
+ public void stop() {
+ BrightnessSetting.BrightnessSettingListener brightnessSettingListener = mock(
+ BrightnessSetting.BrightnessSettingListener.class);
+ mDisplayBrightnessController.registerBrightnessSettingChangeListener(
+ brightnessSettingListener);
+ mDisplayBrightnessController.stop();
+ verify(mBrightnessSetting).unregisterListener(brightnessSettingListener);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index f2e03aa..e871fc5 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,7 +37,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -165,18 +164,6 @@
awaitJobStart(DEFAULT_WAIT_TIMEOUT));
}
- @FlakyTest
- @Test
- public void testFeatureFlag() throws Exception {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.FORCED_APP_STANDBY_ENABLED, 0);
- scheduleAndAssertJobStarted();
- setAppOpsModeAllowed(false);
- mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
- assertFalse("Job stopped even when feature flag was disabled",
- awaitJobStop(DEFAULT_WAIT_TIMEOUT, JobParameters.STOP_REASON_UNDEFINED));
- }
-
@After
public void tearDown() throws Exception {
final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
@@ -187,8 +174,6 @@
Thread.sleep(500); // To avoid race with register in the next setUp
setAppOpsModeAllowed(true);
setPowerExemption(false);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.FORCED_APP_STANDBY_ENABLED, 1);
}
private void setPowerExemption(boolean exempt) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 26d0ddf..ade1bd4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -64,6 +64,8 @@
.setUseParentsContacts(false)
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(0)
+ .setMediaSharedWithParent(false)
+ .setCredentialShareableWithParent(true)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
@@ -72,6 +74,8 @@
actualProps.setUseParentsContacts(true);
actualProps.setCrossProfileIntentFilterAccessControl(20);
actualProps.setCrossProfileIntentResolutionStrategy(1);
+ actualProps.setMediaSharedWithParent(true);
+ actualProps.setCredentialShareableWithParent(false);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -111,6 +115,7 @@
.setStartWithParent(true)
.setShowInSettings(3452)
.setInheritDevicePolicy(1732)
+ .setMediaSharedWithParent(true)
.build();
final UserProperties orig = new UserProperties(defaultProps);
orig.setShowInLauncher(2841);
@@ -169,7 +174,10 @@
// Items with no permission requirements.
assertEqualGetterOrThrows(orig::getShowInLauncher, copy::getShowInLauncher, true);
-
+ assertEqualGetterOrThrows(orig::isMediaSharedWithParent,
+ copy::isMediaSharedWithParent, true);
+ assertEqualGetterOrThrows(orig::isCredentialShareableWithParent,
+ copy::isCredentialShareableWithParent, true);
}
/**
@@ -215,5 +223,9 @@
.isEqualTo(actual.getCrossProfileIntentFilterAccessControl());
assertThat(expected.getCrossProfileIntentResolutionStrategy())
.isEqualTo(actual.getCrossProfileIntentResolutionStrategy());
+ assertThat(expected.isMediaSharedWithParent())
+ .isEqualTo(actual.isMediaSharedWithParent());
+ assertThat(expected.isCredentialShareableWithParent())
+ .isEqualTo(actual.isCredentialShareableWithParent());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 928c6ef..702059d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -86,7 +86,9 @@
.setShowInLauncher(17)
.setUseParentsContacts(true)
.setCrossProfileIntentFilterAccessControl(10)
- .setCrossProfileIntentResolutionStrategy(1);
+ .setCrossProfileIntentResolutionStrategy(1)
+ .setMediaSharedWithParent(true)
+ .setCredentialShareableWithParent(false);
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(1)
@@ -148,6 +150,8 @@
.getCrossProfileIntentFilterAccessControl());
assertEquals(1, type.getDefaultUserPropertiesReference()
.getCrossProfileIntentResolutionStrategy());
+ assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+ assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -196,6 +200,8 @@
assertEquals(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT, props.getShowInLauncher());
assertEquals(UserProperties.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT,
props.getCrossProfileIntentResolutionStrategy());
+ assertFalse(props.isMediaSharedWithParent());
+ assertFalse(props.isCredentialShareableWithParent());
assertFalse(type.hasBadge());
}
@@ -279,7 +285,9 @@
.setStartWithParent(true)
.setUseParentsContacts(true)
.setCrossProfileIntentFilterAccessControl(10)
- .setCrossProfileIntentResolutionStrategy(1);
+ .setCrossProfileIntentResolutionStrategy(1)
+ .setMediaSharedWithParent(false)
+ .setCredentialShareableWithParent(true);
final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
.setName(userTypeAosp1)
@@ -312,6 +320,9 @@
assertTrue(aospType.getDefaultUserPropertiesReference().getStartWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.getUseParentsContacts());
+ assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+ assertTrue(aospType.getDefaultUserPropertiesReference()
+ .isCredentialShareableWithParent());
// userTypeAosp2 should be modified.
aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -348,6 +359,9 @@
assertFalse(aospType.getDefaultUserPropertiesReference().getStartWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.getUseParentsContacts());
+ assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
+ assertFalse(aospType.getDefaultUserPropertiesReference()
+ .isCredentialShareableWithParent());
// userTypeOem1 should be created.
UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 1305e07..ac5bcff 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.testng.Assert.assertThrows;
@@ -189,6 +188,10 @@
cloneUserProperties::getCrossProfileIntentFilterAccessControl);
assertThrows(SecurityException.class,
cloneUserProperties::getCrossProfileIntentResolutionStrategy);
+ assertThat(typeProps.isMediaSharedWithParent())
+ .isEqualTo(cloneUserProperties.isMediaSharedWithParent());
+ assertThat(typeProps.isCredentialShareableWithParent())
+ .isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
// Verify clone user parent
assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -834,11 +837,13 @@
// provided that the test caller has the necessary permissions.
assertThat(userProps.getShowInLauncher()).isEqualTo(typeProps.getShowInLauncher());
assertThat(userProps.getShowInSettings()).isEqualTo(typeProps.getShowInSettings());
- assertFalse(userProps.getUseParentsContacts());
+ assertThat(userProps.getUseParentsContacts()).isFalse();
assertThrows(SecurityException.class, userProps::getCrossProfileIntentFilterAccessControl);
assertThrows(SecurityException.class, userProps::getCrossProfileIntentResolutionStrategy);
assertThrows(SecurityException.class, userProps::getStartWithParent);
assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
+ assertThat(userProps.isMediaSharedWithParent()).isFalse();
+ assertThat(userProps.isCredentialShareableWithParent()).isTrue();
}
// Make sure only max managed profiles can be created
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
new file mode 100644
index 0000000..0be678a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.rollback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.VersionedPackage;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemConfig;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Scanner;
+
+@RunWith(AndroidJUnit4.class)
+public class RollbackPackageHealthObserverTest {
+ private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
+
+ private SystemConfig mSysConfig;
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Before
+ public void setup() {
+ mSysConfig = new SystemConfigTestClass();
+ }
+
+ /**
+ * Subclass of SystemConfig without running the constructor.
+ */
+ private class SystemConfigTestClass extends SystemConfig {
+ SystemConfigTestClass() {
+ super(false);
+ }
+ }
+
+ /**
+ * Test that isAutomaticRollbackDenied works correctly when packages that are not
+ * denied are sent.
+ */
+ @Test
+ public void isRollbackAllowedTest_false() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
+ new VersionedPackage("com.test.package", 1))).isEqualTo(false);
+ }
+
+ /**
+ * Test that isAutomaticRollbackDenied works correctly when packages that are
+ * denied are sent.
+ */
+ @Test
+ public void isRollbackAllowedTest_true() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
+ new VersionedPackage("com.android.vending", 1))).isEqualTo(true);
+ }
+
+ /**
+ * Test that isAutomaticRollbackDenied works correctly when no config is present
+ */
+ @Test
+ public void isRollbackAllowedTest_noConfig() throws IOException {
+ final File folder = createTempSubfolder("folder");
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
+ new VersionedPackage("com.android.vending", 1))).isEqualTo(false);
+ }
+
+ /**
+ * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+ *
+ * @param folder pre-existing subdirectory of mTemporaryFolder to put the file
+ * @param fileName name of the file (e.g. filename.xml) to create
+ * @param contents contents to write to the file
+ * @return the newly created file
+ */
+ private File createTempFile(File folder, String fileName, String contents)
+ throws IOException {
+ File file = new File(folder, fileName);
+ BufferedWriter bw = new BufferedWriter(new FileWriter(file));
+ bw.write(contents);
+ bw.close();
+
+ // Print to logcat for test debugging.
+ Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath());
+ Scanner input = new Scanner(file);
+ while (input.hasNextLine()) {
+ Log.d(LOG_TAG, input.nextLine());
+ }
+
+ return file;
+ }
+
+ private void readPermissions(File libraryDir, int permissionFlag) {
+ final XmlPullParser parser = Xml.newPullParser();
+ mSysConfig.readPermissions(parser, libraryDir, permissionFlag);
+ }
+
+ /**
+ * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+ *
+ * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
+ * @return the folder
+ */
+ private File createTempSubfolder(String folderName)
+ throws IOException {
+ File folder = new File(mTemporaryFolder.getRoot(), folderName);
+ folder.mkdirs();
+ return folder;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index d073f5b..aca96ad 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -595,6 +595,56 @@
}
/**
+ * Test that getRollbackDenylistedPackages works correctly for the tag:
+ * {@code automatic-rollback-denylisted-app}.
+ */
+ @Test
+ public void automaticRollbackDeny_vending() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages())
+ .containsExactly("com.android.vending");
+ }
+
+ /**
+ * Test that getRollbackDenylistedPackages works correctly for the tag:
+ * {@code automatic-rollback-denylisted-app} without any packages.
+ */
+ @Test
+ public void automaticRollbackDeny_empty() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <automatic-rollback-denylisted-app />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
+ }
+
+ /**
+ * Test that getRollbackDenylistedPackages works correctly for the tag:
+ * {@code automatic-rollback-denylisted-app} without the corresponding config.
+ */
+ @Test
+ public void automaticRollbackDeny_noConfig() throws IOException {
+ final File folder = createTempSubfolder("folder");
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
+ }
+
+ /**
* Tests that readPermissions works correctly for the tag: {@code update-ownership}.
*/
@Test
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index dcf1b35..9570ff6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -87,6 +87,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.hardware.display.DisplayManager;
+import android.os.BatteryStats;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -119,6 +120,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -249,6 +251,8 @@
.setLong("elapsed_threshold_rare", RARE_THRESHOLD)
.setLong("elapsed_threshold_restricted", RESTRICTED_THRESHOLD);
DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+ String mExpectedNoteEventPackage = null;
+ int mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -320,6 +324,9 @@
@Override
void noteEvent(int event, String packageName, int uid) throws RemoteException {
+ if (Objects.equals(mExpectedNoteEventPackage, packageName)) {
+ mLastNoteEvent = event;
+ }
}
@Override
@@ -2103,6 +2110,50 @@
assertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_BACKGROUND_LOCATION);
}
+ @Test
+ public void testBatteryStatsNoteEvent() throws Exception {
+ mInjector.mExpectedNoteEventPackage = PACKAGE_1;
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+
+ // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
+ // Reset the last event to confirm the method isn't called.
+ mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+ // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
+ // Reset the last event to confirm the method isn't called.
+ mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_EXEMPTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index fd1ca68..a76b82b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -407,7 +407,7 @@
void assertShowRecentApps() {
waitForIdle();
- verify(mStatusBarManagerInternal).showRecentApps(anyBoolean(), anyBoolean());
+ verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
}
void assertSwitchKeyboardLayout() {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
index 3163820..20f03d8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.usage;
+import android.annotation.UserIdInt;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -35,65 +36,71 @@
public class UsageStatsIdleService extends JobService {
/**
- * Base job ID for the pruning job - must be unique within the system server uid.
+ * Namespace for prune job
*/
- private static final int PRUNE_JOB_ID = 546357475;
+ private static final String PRUNE_JOB_NS = "usagestats_prune";
+
/**
- * Job ID for the update mappings job - must be unique within the system server uid.
- * Incrementing PRUNE_JOB_ID by 21475 (MAX_USER_ID) to ensure there is no overlap in job ids.
+ * Namespace for update mappings job
*/
- private static final int UPDATE_MAPPINGS_JOB_ID = 546378950;
+ private static final String UPDATE_MAPPINGS_JOB_NS = "usagestats_mapping";
private static final String USER_ID_KEY = "user_id";
- static void scheduleJob(Context context, int userId) {
- final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+ /** Schedule a prune job */
+ static void schedulePruneJob(Context context, @UserIdInt int userId) {
final ComponentName component = new ComponentName(context.getPackageName(),
UsageStatsIdleService.class.getName());
final PersistableBundle bundle = new PersistableBundle();
bundle.putInt(USER_ID_KEY, userId);
- final JobInfo pruneJob = new JobInfo.Builder(userJobId, component)
+ final JobInfo pruneJob = new JobInfo.Builder(userId, component)
.setRequiresDeviceIdle(true)
.setExtras(bundle)
.setPersisted(true)
.build();
- scheduleJobInternal(context, pruneJob, userJobId);
+ scheduleJobInternal(context, pruneJob, PRUNE_JOB_NS, userId);
}
- static void scheduleUpdateMappingsJob(Context context) {
+ static void scheduleUpdateMappingsJob(Context context, @UserIdInt int userId) {
final ComponentName component = new ComponentName(context.getPackageName(),
UsageStatsIdleService.class.getName());
- final JobInfo updateMappingsJob = new JobInfo.Builder(UPDATE_MAPPINGS_JOB_ID, component)
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(USER_ID_KEY, userId);
+ final JobInfo updateMappingsJob = new JobInfo.Builder(userId, component)
.setPersisted(true)
.setMinimumLatency(TimeUnit.DAYS.toMillis(1))
.setOverrideDeadline(TimeUnit.DAYS.toMillis(2))
+ .setExtras(bundle)
.build();
- scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_ID);
+ scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_NS, userId);
}
- private static void scheduleJobInternal(Context context, JobInfo pruneJob, int jobId) {
- final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
- final JobInfo pendingPruneJob = jobScheduler.getPendingJob(jobId);
- // only schedule a new prune job if one doesn't exist already for this user
- if (!pruneJob.equals(pendingPruneJob)) {
- jobScheduler.cancel(jobId); // cancel any previously scheduled prune job
- jobScheduler.schedule(pruneJob);
+ private static void scheduleJobInternal(Context context, JobInfo jobInfo,
+ String namespace, int jobId) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ jobScheduler = jobScheduler.forNamespace(namespace);
+ final JobInfo pendingJob = jobScheduler.getPendingJob(jobId);
+ // only schedule a new job if one doesn't exist already for this user
+ if (!jobInfo.equals(pendingJob)) {
+ jobScheduler.cancel(jobId); // cancel any previously scheduled job
+ jobScheduler.schedule(jobInfo);
}
}
- static void cancelJob(Context context, int userId) {
- cancelJobInternal(context, PRUNE_JOB_ID + userId);
+ static void cancelPruneJob(Context context, @UserIdInt int userId) {
+ cancelJobInternal(context, PRUNE_JOB_NS, userId);
}
- static void cancelUpdateMappingsJob(Context context) {
- cancelJobInternal(context, UPDATE_MAPPINGS_JOB_ID);
+ static void cancelUpdateMappingsJob(Context context, @UserIdInt int userId) {
+ cancelJobInternal(context, UPDATE_MAPPINGS_JOB_NS, userId);
}
- private static void cancelJobInternal(Context context, int jobId) {
- final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ private static void cancelJobInternal(Context context, String namespace, int jobId) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if (jobScheduler != null) {
+ jobScheduler = jobScheduler.forNamespace(namespace);
jobScheduler.cancel(jobId);
}
}
@@ -102,15 +109,19 @@
public boolean onStartJob(JobParameters params) {
final PersistableBundle bundle = params.getExtras();
final int userId = bundle.getInt(USER_ID_KEY, -1);
- if (userId == -1 && params.getJobId() != UPDATE_MAPPINGS_JOB_ID) {
+
+ if (userId == -1) { // legacy job
return false;
}
+ // Do async
AsyncTask.execute(() -> {
final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
UsageStatsManagerInternal.class);
- if (params.getJobId() == UPDATE_MAPPINGS_JOB_ID) {
- final boolean jobFinished = usageStatsManagerInternal.updatePackageMappingsData();
+ final String jobNs = params.getJobNamespace();
+ if (UPDATE_MAPPINGS_JOB_NS.equals(jobNs)) {
+ final boolean jobFinished =
+ usageStatsManagerInternal.updatePackageMappingsData(userId);
jobFinished(params, !jobFinished); // reschedule if data was not updated
} else {
final boolean jobFinished =
@@ -118,6 +129,8 @@
jobFinished(params, !jobFinished); // reschedule if data was not pruned
}
});
+
+ // Job is running asynchronously
return true;
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b3a1f2b..7ff5b4a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -433,11 +433,9 @@
private void onUserUnlocked(int userId) {
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
- // delay updating of package mappings for user 0 since their data is not likely to be stale.
- // this also makes it less likely for restored data to be erased on unexpected reboots.
- if (userId == UserHandle.USER_SYSTEM) {
- UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
- }
+
+ UsageStatsIdleService.scheduleUpdateMappingsJob(getContext(), userId);
+
final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId));
synchronized (mLock) {
// This should be safe to add this early. Other than reportEventOrAddToQueue and
@@ -1261,8 +1259,8 @@
}
mAppStandby.onUserRemoved(userId);
// Cancel any scheduled jobs for this user since the user is being removed.
- UsageStatsIdleService.cancelJob(getContext(), userId);
- UsageStatsIdleService.cancelUpdateMappingsJob(getContext());
+ UsageStatsIdleService.cancelPruneJob(getContext(), userId);
+ UsageStatsIdleService.cancelUpdateMappingsJob(getContext(), userId);
}
/**
@@ -1300,7 +1298,7 @@
// Schedule a job to prune any data related to this package.
if (tokenRemoved != PackagesTokenData.UNASSIGNED_TOKEN) {
- UsageStatsIdleService.scheduleJob(getContext(), userId);
+ UsageStatsIdleService.schedulePruneJob(getContext(), userId);
}
}
@@ -1325,19 +1323,19 @@
/**
* Called by the Binder stub.
*/
- private boolean updatePackageMappingsData() {
+ private boolean updatePackageMappingsData(@UserIdInt int userId) {
// don't update the mappings if a profile user is defined
- if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) {
+ if (!shouldDeleteObsoleteData(UserHandle.of(userId))) {
return true; // return true so job scheduler doesn't reschedule the job
}
// fetch the installed packages outside the lock so it doesn't block package manager.
- final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
+ final HashMap<String, Long> installedPkgs = getInstalledPackages(userId);
synchronized (mLock) {
- if (!mUserUnlockedStates.contains(UserHandle.USER_SYSTEM)) {
+ if (!mUserUnlockedStates.contains(userId)) {
return false; // user is no longer unlocked
}
- final UserUsageStatsService userService = mUserState.get(UserHandle.USER_SYSTEM);
+ final UserUsageStatsService userService = mUserState.get(userId);
if (userService == null) {
return false; // user was stopped or removed
}
@@ -3055,44 +3053,35 @@
}
@Override
- public byte[] getBackupPayload(int user, String key) {
- if (!mUserUnlockedStates.contains(user)) {
- Slog.w(TAG, "Failed to get backup payload for locked user " + user);
+ public byte[] getBackupPayload(@UserIdInt int userId, String key) {
+ if (!mUserUnlockedStates.contains(userId)) {
+ Slog.w(TAG, "Failed to get backup payload for locked user " + userId);
return null;
}
synchronized (mLock) {
- // Check to ensure that only user 0's data is b/r for now
- // Note: if backup and restore is enabled for users other than the system user, the
- // #onUserUnlocked logic, specifically when the update mappings job is scheduled via
- // UsageStatsIdleService.scheduleUpdateMappingsJob, will have to be updated.
- if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
- if (userStats == null) {
- return null; // user was stopped or removed
- }
- return userStats.getBackupPayload(key);
- } else {
- return null;
+ final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId);
+ if (userStats == null) {
+ return null; // user was stopped or removed
}
+ Slog.i(TAG, "Returning backup payload for u=" + userId);
+ return userStats.getBackupPayload(key);
}
}
@Override
- public void applyRestoredPayload(int user, String key, byte[] payload) {
+ public void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload) {
synchronized (mLock) {
- if (!mUserUnlockedStates.contains(user)) {
- Slog.w(TAG, "Failed to apply restored payload for locked user " + user);
+ if (!mUserUnlockedStates.contains(userId)) {
+ Slog.w(TAG, "Failed to apply restored payload for locked user " + userId);
return;
}
- if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
- if (userStats == null) {
- return; // user was stopped or removed
- }
- final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
- mAppStandby.restoreAppsToRare(restoredApps, user);
+ final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId);
+ if (userStats == null) {
+ return; // user was stopped or removed
}
+ final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
+ mAppStandby.restoreAppsToRare(restoredApps, userId);
}
}
@@ -3165,8 +3154,8 @@
}
@Override
- public boolean updatePackageMappingsData() {
- return UsageStatsService.this.updatePackageMappingsData();
+ public boolean updatePackageMappingsData(@UserIdInt int userId) {
+ return UsageStatsService.this.updatePackageMappingsData(userId);
}
/**
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index fd2907c..95a8e16 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -32,10 +32,8 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
/**
* A unified virtual device providing a means of voice (and other) communication on a device.
@@ -150,14 +148,6 @@
private final Object mLock = new Object();
- // Future used to delay terminating the InCallService before the call disconnect tone
- // finishes playing.
- private static Map<String, CompletableFuture<Void>> sDisconnectedToneFutures = new ArrayMap<>();
-
- // Timeout value to be used to ensure future completion for sDisconnectedToneFutures. This is
- // set to 4 seconds to account for the exceptional case (TONE_CONGESTION).
- private static final int DISCONNECTED_TONE_TIMEOUT = 4000;
-
Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
mInCallAdapter = adapter;
mCallingPackage = callingPackage;
@@ -466,45 +456,9 @@
}
private void fireCallRemoved(Call call) {
- String callId = call.internalGetCallId();
- CompletableFuture<Void> disconnectedToneFuture = initializeDisconnectedToneFuture(callId);
- // delay the InCallService termination until after the disconnect tone finishes playing
- disconnectedToneFuture.thenRunAsync(() -> {
- for (Listener listener : mListeners) {
- listener.onCallRemoved(this, call);
- }
- // clean up the future after
- sDisconnectedToneFutures.remove(callId);
- });
- }
-
- /**
- * Initialize disconnect tone future to be used in delaying ICS termination.
- *
- * @return CompletableFuture to delay InCallService termination until after the disconnect tone
- * finishes playing. A timeout of 4s is used to handle the use case when we play
- * TONE_CONGESTION and to ensure completion so that we don't block the removal of the service.
- */
- private CompletableFuture<Void> initializeDisconnectedToneFuture(String callId) {
- // create the future and map (sDisconnectedToneFutures) it to the corresponding call id
- CompletableFuture<Void> disconnectedToneFuture = new CompletableFuture<Void>()
- .completeOnTimeout(null, DISCONNECTED_TONE_TIMEOUT, TimeUnit.MILLISECONDS);
- // we should not encounter duplicate insertions since call ids are unique
- sDisconnectedToneFutures.put(callId, disconnectedToneFuture);
- return disconnectedToneFuture;
- }
-
- /**
- * Completes disconnected tone future with passed in result.
- * @hide
- * @return true if future was completed, false otherwise
- */
- public static boolean completeDisconnectedToneFuture(String callId) {
- if (sDisconnectedToneFutures.containsKey(callId)) {
- sDisconnectedToneFutures.get(callId).complete(null);
- return true;
+ for (Listener listener : mListeners) {
+ listener.onCallRemoved(this, call);
}
- return false;
}
private void fireCallAudioStateChanged(CallAudioState audioState) {
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 26b4bbc..40488b1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2547,8 +2547,6 @@
/**
* User is not associated with the subscription.
- * TODO(b/263279115): Make this error code public.
- * @hide
*/
public static final int RESULT_USER_NOT_ALLOWED = 33;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a3b7260..189a08c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2680,10 +2680,10 @@
* @param columnName Column name in subscription database.
*
* @return Value in string format associated with {@code subscriptionId} and {@code columnName}
- * from the database.
+ * from the database. Empty string if the {@code subscriptionId} is invalid (for backward
+ * compatible).
*
- * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
- * exposed.
+ * @throws IllegalArgumentException if the field is not exposed.
*
* @see android.provider.Telephony.SimInfo for all the columns.
*
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 83b9098..37d1e94 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3242,17 +3242,17 @@
case NETWORK_TYPE_CDMA:
return "CDMA";
case NETWORK_TYPE_EVDO_0:
- return "CDMA - EvDo rev. 0";
+ return "EVDO-0";
case NETWORK_TYPE_EVDO_A:
- return "CDMA - EvDo rev. A";
+ return "EVDO-A";
case NETWORK_TYPE_EVDO_B:
- return "CDMA - EvDo rev. B";
+ return "EVDO-B";
case NETWORK_TYPE_1xRTT:
- return "CDMA - 1xRTT";
+ return "1xRTT";
case NETWORK_TYPE_LTE:
return "LTE";
case NETWORK_TYPE_EHRPD:
- return "CDMA - eHRPD";
+ return "eHRPD";
case NETWORK_TYPE_IDEN:
return "iDEN";
case NETWORK_TYPE_HSPAP:
@@ -3260,7 +3260,7 @@
case NETWORK_TYPE_GSM:
return "GSM";
case NETWORK_TYPE_TD_SCDMA:
- return "TD_SCDMA";
+ return "TD-SCDMA";
case NETWORK_TYPE_IWLAN:
return "IWLAN";
case NETWORK_TYPE_LTE_CA:
@@ -9574,10 +9574,10 @@
*/
public static boolean isValidAllowedNetworkTypesReason(@AllowedNetworkTypesReason int reason) {
switch (reason) {
- case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
- case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
- case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
- case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
+ case ALLOWED_NETWORK_TYPES_REASON_USER:
+ case ALLOWED_NETWORK_TYPES_REASON_POWER:
+ case ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+ case ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS:
return true;
}
@@ -18118,4 +18118,44 @@
return "UNKNOWN(" + state + ")";
}
}
+
+ /**
+ * Convert the allowed network types reason to string.
+ *
+ * @param reason The allowed network types reason.
+ * @return The converted string.
+ *
+ * @hide
+ */
+ @NonNull
+ public static String allowedNetworkTypesReasonToString(@AllowedNetworkTypesReason int reason) {
+ switch (reason) {
+ case ALLOWED_NETWORK_TYPES_REASON_USER: return "user";
+ case ALLOWED_NETWORK_TYPES_REASON_POWER: return "power";
+ case ALLOWED_NETWORK_TYPES_REASON_CARRIER: return "carrier";
+ case ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: return "enable_2g";
+ case ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS: return "user_restrictions";
+ default: return "unknown(" + reason + ")";
+ }
+ }
+
+ /**
+ * Convert the allowed network types reason from string.
+ *
+ * @param reason The reason in string format.
+ * @return The allowed network types reason.
+ *
+ * @hide
+ */
+ @AllowedNetworkTypesReason
+ public static int allowedNetworkTypesReasonFromString(@NonNull String reason) {
+ switch (reason) {
+ case "user": return ALLOWED_NETWORK_TYPES_REASON_USER;
+ case "power": return ALLOWED_NETWORK_TYPES_REASON_POWER;
+ case "carrier": return ALLOWED_NETWORK_TYPES_REASON_CARRIER;
+ case "enable_2g": return ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G;
+ case "user_restrictions": return ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS;
+ default: return -1;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a2d2019..cdb7d7c 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1570,8 +1570,8 @@
/**
* Returns whether the passing portIndex is available.
- * A port is available if it is active without enabled profile on it or
- * calling app has carrier privilege over the profile installed on the selected port.
+ * A port is available if it is active without an enabled profile on it or calling app can
+ * activate a new profile on the selected port without any user interaction.
* Always returns false if the cardId is a physical card.
*
* @param portIndex is an enumeration of the ports available on the UICC.
diff --git a/tests/VectorDrawableTest/Android.bp b/tests/VectorDrawableTest/Android.bp
index 099d874..9da7c5f 100644
--- a/tests/VectorDrawableTest/Android.bp
+++ b/tests/VectorDrawableTest/Android.bp
@@ -26,7 +26,5 @@
android_test {
name: "VectorDrawableTest",
srcs: ["**/*.java"],
- // certificate set as platform to allow testing of @hidden APIs
- certificate: "platform",
platform_apis: true,
}
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 163e438..5334dac 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -158,15 +158,6 @@
<category android:name="com.android.test.dynamic.TEST"/>
</intent-filter>
</activity>
- <activity android:name="LottieDrawableTest"
- android:label="Lottie test bed"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="com.android.test.dynamic.TEST" />
- </intent-filter>
- </activity>
</application>
</manifest>
diff --git a/tests/VectorDrawableTest/res/raw/lottie.json b/tests/VectorDrawableTest/res/raw/lottie.json
deleted file mode 100644
index fea571c..0000000
--- a/tests/VectorDrawableTest/res/raw/lottie.json
+++ /dev/null
@@ -1,123 +0,0 @@
-{
- "v":"4.6.9",
- "fr":60,
- "ip":0,
- "op":200,
- "w":800,
- "h":600,
- "nm":"Loader 1 JSON",
- "ddd":0,
-
-
- "layers":[
- {
- "ddd":0,
- "ind":1,
- "ty":4,
- "nm":"Custom Path 1",
- "ao": 0,
- "ip": 0,
- "op": 300,
- "st": 0,
- "sr": 1,
- "bm": 0,
- "ks": {
- "o": { "a":0, "k":100 },
- "r": { "a":1, "k": [
- { "s": [ 0 ], "e": [ 360], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
- { "t": 200 }
- ] },
- "p": { "a":0, "k":[ 300, 300, 0 ] },
- "a": { "a":0, "k":[ 100, 100, 0 ] },
- "s": { "a":1, "k":[
- { "s": [ 100, 100 ], "e": [ 200, 200 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
- { "s": [ 200, 200 ], "e": [ 100, 100 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 },
- { "t": 200 }
- ] }
- },
-
- "shapes":[
- {
- "ty":"gr",
- "it":[
- {
- "ty" : "sh",
- "nm" : "Path 1",
- "ks" : {
- "a" : 1,
- "k" : [
- {
- "s": [ {
- "i": [ [ 0, 50 ], [ -50, 0 ], [ 0, -50 ], [ 50, 0 ] ],
- "o": [ [ 0, -50 ], [ 50, 0 ], [ 0, 50 ], [ -50, 0 ] ],
- "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ],
- "c": true
- } ],
- "e": [ {
- "i": [ [ 50, 50 ], [ -50, 0 ], [ -50, -50 ], [ 50, 50 ] ],
- "o": [ [ 50, -50 ], [ 50, 0 ], [ -50, 50 ], [ -50, 50 ] ],
- "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ],
- "c": true
- } ],
- "i": { "x":0.5, "y":0.5 },
- "o": { "x":0.5, "y":0.5 },
- "t": 0
- },
- {
- "s": [ {
- "i": [ [ 50, 50 ], [ -50, 0 ], [ -50, -50 ], [ 50, 50 ] ],
- "o": [ [ 50, -50 ], [ 50, 0 ], [ -50, 50 ], [ -50, 50 ] ],
- "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ],
- "c": true
- } ],
- "e": [ {
- "i": [ [ 0, 50 ], [ -50, 0 ], [ 0, -50 ], [ 50, 0 ] ],
- "o": [ [ 0, -50 ], [ 50, 0 ], [ 0, 50 ], [ -50, 0 ] ],
- "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ],
- "c": true
- } ],
- "i": { "x":0.5, "y":0.5 },
- "o": { "x":0.5, "y":0.5 },
- "t": 100
- },
- {
- "t": 200
- }
- ]
- }
- },
-
- {
- "ty": "st",
- "nm": "Stroke 1",
- "lc": 1,
- "lj": 1,
- "ml": 4,
- "w" : { "a": 1, "k": [
- { "s": [ 30 ], "e": [ 50 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
- { "s": [ 50 ], "e": [ 30 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 },
- { "t": 200 }
- ] },
- "o" : { "a": 0, "k": 100 },
- "c" : { "a": 1, "k": [
- { "s": [ 0, 1, 0 ], "e": [ 1, 0, 0 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 },
- { "s": [ 1, 0, 0 ], "e": [ 0, 1, 0 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 },
- { "t": 200 }
- ] }
- },
-
- {
- "ty":"tr",
- "p" : { "a":0, "k":[ 0, 0 ] },
- "a" : { "a":0, "k":[ 0, 0 ] },
- "s" : { "a":0, "k":[ 100, 100 ] },
- "r" : { "a":0, "k": 0 },
- "o" : { "a":0, "k":100 },
- "nm": "Transform"
- }
- ]
- }
- ]
- }
- ]
- }
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java
deleted file mode 100644
index 05eae7b..0000000
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.test.dynamic;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.LottieDrawable;
-import android.os.Bundle;
-import android.view.View;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-
-@SuppressWarnings({"UnusedDeclaration"})
-public class LottieDrawableTest extends Activity {
- private static final String TAG = "LottieDrawableTest";
- static final int BACKGROUND = 0xFFF44336;
-
- class LottieDrawableView extends View {
- private Rect mLottieBounds;
-
- private LottieDrawable mLottie;
-
- LottieDrawableView(Context context, InputStream is) {
- super(context);
- Scanner s = new Scanner(is).useDelimiter("\\A");
- String json = s.hasNext() ? s.next() : "";
- try {
- mLottie = LottieDrawable.makeLottieDrawable(json);
- } catch (IOException e) {
- throw new RuntimeException(TAG + ": error parsing test Lottie");
- }
- mLottie.start();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawColor(BACKGROUND);
-
- mLottie.setBounds(mLottieBounds);
- mLottie.draw(canvas);
- }
-
- public void setLottieSize(Rect bounds) {
- mLottieBounds = bounds;
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- InputStream is = getResources().openRawResource(R.raw.lottie);
-
- LottieDrawableView view = new LottieDrawableView(this, is);
- view.setLottieSize(new Rect(0, 0, 900, 900));
- setContentView(view);
- }
-}
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 965b073..34f884b 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -19,9 +19,6 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY;
-import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
@@ -143,6 +140,8 @@
@NonNull private TelephonySubscriptionTrackerCallback mCallback;
@NonNull private TelephonySubscriptionTracker mTelephonySubscriptionTracker;
+ @NonNull private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+
public TelephonySubscriptionTrackerTest() {
mContext = mock(Context.class);
mTestLooper = new TestLooper();
@@ -173,7 +172,7 @@
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
doReturn(TEST_CARRIER_CONFIG)
.when(mCarrierConfigManager)
- .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
// subId 1, 2 are in same subGrp, only subId 1 is active
doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -189,9 +188,15 @@
doReturn(2).when(mTelephonyManager).getActiveModemCount();
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
+ // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mTelephonySubscriptionTracker =
new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
mTelephonySubscriptionTracker.register();
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
doReturn(true).when(mDeps).isConfigForIdentifiedCarrier(any());
doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
@@ -239,14 +244,11 @@
return intent;
}
- private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
- Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
- intent.putExtra(
- EXTRA_SUBSCRIPTION_INDEX,
- hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID);
-
- return intent;
+ private void sendCarrierConfigChange(boolean hasValidSubscription) {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(
+ TEST_SIM_SLOT_INDEX,
+ hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
@@ -302,14 +304,15 @@
any(),
eq(mHandler));
final IntentFilter filter = getIntentFilter();
- assertEquals(2, filter.countActions());
- assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+ assertEquals(1, filter.countActions());
assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
verify(mSubscriptionManager)
.addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
assertNotNull(getOnSubscriptionsChangedListener());
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(), any());
+
verify(mTelephonyManager, times(2))
.registerCarrierPrivilegesCallback(anyInt(), any(HandlerExecutor.class), any());
verify(mTelephonyManager)
@@ -442,7 +445,7 @@
@Test
public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
@@ -454,7 +457,7 @@
.when(mSubscriptionManager)
.getAllSubscriptionInfoList();
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
// Expect an empty snapshot
@@ -465,7 +468,7 @@
public void testReceiveBroadcast_SlotCleared() throws Exception {
setupReadySubIds();
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ sendCarrierConfigChange(false /* hasValidSubscription */);
mTestLooper.dispatchAll();
verifyNoActiveSubscriptions();
@@ -476,7 +479,7 @@
public void testReceiveBroadcast_ConfigNotReady() throws Exception {
doReturn(false).when(mDeps).isConfigForIdentifiedCarrier(any());
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
// No interactions expected; config was not loaded
@@ -485,21 +488,21 @@
@Test
public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
assertNotNull(
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList();
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
}
@Test
public void testCarrierConfigUpdatedAfterValidTriggersCallbacks() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
reset(mCallback);
@@ -510,12 +513,12 @@
new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR});
doReturn(updatedConfig)
.when(mCarrierConfigManager)
- .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
subIdToCarrierConfigMap.put(
TEST_SUBSCRIPTION_ID_1, new PersistableBundleWrapper(updatedConfig));
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback)
@@ -530,13 +533,13 @@
@Test
public void testSlotClearedAfterValidTriggersCallbacks() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
assertNotNull(
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ sendCarrierConfigChange(false /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback)
.onNewSnapshot(