summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java22
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java45
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java81
-rw-r--r--core/java/android/app/ActivityClient.java9
-rw-r--r--core/java/android/app/ActivityThread.java1
-rw-r--r--core/java/android/app/AppOpsManager.java19
-rw-r--r--core/java/android/app/IActivityClientController.aidl1
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/app/usage/OWNERS1
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java97
-rw-r--r--core/java/android/hardware/Sensor.java6
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java11
-rw-r--r--core/java/android/hardware/input/TEST_MAPPING7
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java13
-rw-r--r--core/java/android/os/PowerManager.java73
-rw-r--r--core/java/android/os/PowerManagerInternal.java3
-rw-r--r--core/java/android/os/UserManager.java6
-rw-r--r--core/java/android/speech/RecognitionService.java5
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java8
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java6
-rw-r--r--core/java/com/android/server/SystemConfig.java16
-rw-r--r--core/proto/android/server/vibrator/vibratormanagerservice.proto3
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml24
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt142
-rw-r--r--libs/hwui/Properties.cpp2
-rw-r--r--libs/input/TEST_MAPPING10
-rw-r--r--media/jni/android_media_MediaCodec.cpp23
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml7
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java40
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java99
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java63
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java2
-rw-r--r--packages/SystemUI/res/values/strings.xml10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java96
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt131
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java10
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java15
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java38
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java8
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java17
-rw-r--r--services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java (renamed from services/companion/java/com/android/server/companion/AssociationCleanUpService.java)29
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java71
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java20
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java34
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java24
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java21
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java17
-rw-r--r--services/core/java/com/android/server/input/TEST_MAPPING7
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java9
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterBase.java45
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java257
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterLocked.java63
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java28
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java26
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java53
-rw-r--r--services/core/java/com/android/server/pm/FileInstallArgs.java19
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java57
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java10
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java4
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java21
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java30
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java56
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java19
-rw-r--r--services/core/java/com/android/server/wm/SplashScreenExceptionList.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java91
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java86
-rw-r--r--services/usage/OWNERS3
-rw-r--r--services/usage/java/com/android/server/usage/BroadcastEvent.java11
-rw-r--r--services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java145
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java45
-rw-r--r--services/usage/java/com/android/server/usage/UserBroadcastEvents.java31
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java37
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl14
139 files changed, 3017 insertions, 1096 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index c3fc4d1603b0..e4306e5e2bda 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -247,6 +247,28 @@ public interface AppStandbyInternal {
int getBroadcastResponseFgThresholdState();
/**
+ * Returns the duration within which any broadcasts occurred will be treated as one broadcast
+ * session.
+ */
+ long getBroadcastSessionsDurationMs();
+
+ /**
+ * Returns the duration within which any broadcasts occurred (with a corresponding response
+ * event) will be treated as one broadcast session. This similar to
+ * {@link #getBroadcastSessionsDurationMs()}, except that this duration will be used to group
+ * only broadcasts that have a corresponding response event into sessions.
+ */
+ long getBroadcastSessionsWithResponseDurationMs();
+
+ /**
+ * Returns {@code true} if the response event should be attributed to all the broadcast
+ * sessions that occurred within the broadcast response window and {@code false} if the
+ * response event should be attributed to only the earliest broadcast session within the
+ * broadcast response window.
+ */
+ boolean shouldNoteResponseEventForAllBroadcastSessions();
+
+ /**
* Return the last known value corresponding to the {@code key} from
* {@link android.provider.DeviceConfig#NAMESPACE_APP_STANDBY} in AppStandbyController.
*/
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index afe36b5fa25a..d5a7f2851d03 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -959,7 +959,7 @@ class JobConcurrencyManager {
for (int i = 0; i < mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJobLocked();
- if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
+ if (executing == job) {
jsc.cancelExecutingJobLocked(reason, internalReasonCode, debugReason);
return true;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 428f2cbaefc2..0f385efae5cc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -17,6 +17,7 @@
package com.android.server.job.controllers;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
@@ -90,6 +91,14 @@ public class PrefetchController extends StateController {
@CurrentTimeMillisLong
private long mLaunchTimeThresholdMs = PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS;
+ /**
+ * The additional time we'll add to a launch time estimate before considering it obsolete and
+ * try to get a new estimate. This will help make prefetch jobs more viable in case an estimate
+ * is a few minutes early.
+ */
+ @GuardedBy("mLock")
+ private long mLaunchTimeAllowanceMs = PcConstants.DEFAULT_LAUNCH_TIME_ALLOWANCE_MS;
+
@SuppressWarnings("FieldCanBeLocal")
private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener =
new EstimatedLaunchTimeChangedListener() {
@@ -204,7 +213,8 @@ public class PrefetchController extends StateController {
private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName,
@CurrentTimeMillisLong long now) {
final Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName);
- if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) {
+ if (nextEstimatedLaunchTime == null
+ || nextEstimatedLaunchTime < now - mLaunchTimeAllowanceMs) {
// Don't query usage stats here because it may have to read from disk.
mHandler.obtainMessage(MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME, userId, 0, pkgName)
.sendToTarget();
@@ -335,7 +345,9 @@ public class PrefetchController extends StateController {
}
final long nextEstimatedLaunchTime = getNextEstimatedLaunchTimeLocked(userId, pkgName, now);
- if (nextEstimatedLaunchTime - now > mLaunchTimeThresholdMs) {
+ // Avoid setting an alarm for the end of time.
+ if (nextEstimatedLaunchTime != Long.MAX_VALUE
+ && nextEstimatedLaunchTime - now > mLaunchTimeThresholdMs) {
// Set alarm to be notified when this crosses the threshold.
final long timeToCrossThresholdMs =
nextEstimatedLaunchTime - (now + mLaunchTimeThresholdMs);
@@ -354,7 +366,7 @@ public class PrefetchController extends StateController {
private boolean willBeLaunchedSoonLocked(int userId, @NonNull String pkgName,
@CurrentTimeMillisLong long now) {
return getNextEstimatedLaunchTimeLocked(userId, pkgName, now)
- <= now + mLaunchTimeThresholdMs;
+ <= now + mLaunchTimeThresholdMs - mLaunchTimeAllowanceMs;
}
@Override
@@ -494,16 +506,37 @@ public class PrefetchController extends StateController {
@VisibleForTesting
static final String KEY_LAUNCH_TIME_THRESHOLD_MS =
PC_CONSTANT_PREFIX + "launch_time_threshold_ms";
+ @VisibleForTesting
+ static final String KEY_LAUNCH_TIME_ALLOWANCE_MS =
+ PC_CONSTANT_PREFIX + "launch_time_allowance_ms";
private static final long DEFAULT_LAUNCH_TIME_THRESHOLD_MS = 7 * HOUR_IN_MILLIS;
+ private static final long DEFAULT_LAUNCH_TIME_ALLOWANCE_MS = 20 * MINUTE_IN_MILLIS;
/** How much time each app will have to run jobs within their standby bucket window. */
public long LAUNCH_TIME_THRESHOLD_MS = DEFAULT_LAUNCH_TIME_THRESHOLD_MS;
+ /**
+ * How much additional time to add to an estimated launch time before considering it
+ * unusable.
+ */
+ public long LAUNCH_TIME_ALLOWANCE_MS = DEFAULT_LAUNCH_TIME_ALLOWANCE_MS;
+
@GuardedBy("mLock")
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
+ case KEY_LAUNCH_TIME_ALLOWANCE_MS:
+ LAUNCH_TIME_ALLOWANCE_MS =
+ properties.getLong(key, DEFAULT_LAUNCH_TIME_ALLOWANCE_MS);
+ // Limit the allowance to the range [0 minutes, 2 hours].
+ long newLaunchTimeAllowanceMs = Math.min(2 * HOUR_IN_MILLIS,
+ Math.max(0, LAUNCH_TIME_ALLOWANCE_MS));
+ if (mLaunchTimeAllowanceMs != newLaunchTimeAllowanceMs) {
+ mLaunchTimeAllowanceMs = newLaunchTimeAllowanceMs;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
case KEY_LAUNCH_TIME_THRESHOLD_MS:
LAUNCH_TIME_THRESHOLD_MS =
properties.getLong(key, DEFAULT_LAUNCH_TIME_THRESHOLD_MS);
@@ -528,6 +561,7 @@ public class PrefetchController extends StateController {
pw.increaseIndent();
pw.print(KEY_LAUNCH_TIME_THRESHOLD_MS, LAUNCH_TIME_THRESHOLD_MS).println();
+ pw.print(KEY_LAUNCH_TIME_ALLOWANCE_MS, LAUNCH_TIME_ALLOWANCE_MS).println();
pw.decreaseIndent();
}
@@ -536,6 +570,11 @@ public class PrefetchController extends StateController {
//////////////////////// TESTING HELPERS /////////////////////////////
@VisibleForTesting
+ long getLaunchTimeAllowanceMs() {
+ return mLaunchTimeAllowanceMs;
+ }
+
+ @VisibleForTesting
long getLaunchTimeThresholdMs() {
return mLaunchTimeThresholdMs;
}
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 1891e06a9420..e57724909306 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -377,6 +377,32 @@ public class AppStandbyController
ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE;
/**
+ * Duration (in millis) for the window within which any broadcasts occurred will be
+ * treated as one broadcast session.
+ */
+ volatile long mBroadcastSessionsDurationMs =
+ ConstantsObserver.DEFAULT_BROADCAST_SESSIONS_DURATION_MS;
+
+ /**
+ * Duration (in millis) for the window within which any broadcasts occurred ((with a
+ * corresponding response event) will be treated as one broadcast session. This similar to
+ * {@link #mBroadcastSessionsDurationMs}, except that this duration will be used to group only
+ * broadcasts that have a corresponding response event into sessions.
+ */
+ volatile long mBroadcastSessionsWithResponseDurationMs =
+ ConstantsObserver.DEFAULT_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS;
+
+ /**
+ * Denotes whether the response event should be attributed to all broadcast sessions or not.
+ * If this is {@code true}, then the response event should be attributed to all the broadcast
+ * sessions that occurred within the broadcast response window. Otherwise, the
+ * response event should be attributed to only the earliest broadcast session within the
+ * broadcast response window.
+ */
+ volatile boolean mNoteResponseEventForAllBroadcastSessions =
+ ConstantsObserver.DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS;
+
+ /**
* Map of last known values of keys in {@link DeviceConfig#NAMESPACE_APP_STANDBY}.
*
* Note: We are intentionally not guarding this by any lock since this is only updated on
@@ -1869,6 +1895,21 @@ public class AppStandbyController
}
@Override
+ public long getBroadcastSessionsDurationMs() {
+ return mBroadcastSessionsDurationMs;
+ }
+
+ @Override
+ public long getBroadcastSessionsWithResponseDurationMs() {
+ return mBroadcastSessionsWithResponseDurationMs;
+ }
+
+ @Override
+ public boolean shouldNoteResponseEventForAllBroadcastSessions() {
+ return mNoteResponseEventForAllBroadcastSessions;
+ }
+
+ @Override
@Nullable
public String getAppStandbyConstant(@NonNull String key) {
return mAppStandbyProperties.get(key);
@@ -2202,6 +2243,18 @@ public class AppStandbyController
pw.print(ActivityManager.procStateToString(mBroadcastResponseFgThresholdState));
pw.println();
+ pw.print(" mBroadcastSessionsDurationMs=");
+ TimeUtils.formatDuration(mBroadcastSessionsDurationMs, pw);
+ pw.println();
+
+ pw.print(" mBroadcastSessionsWithResponseDurationMs=");
+ TimeUtils.formatDuration(mBroadcastSessionsWithResponseDurationMs, pw);
+ pw.println();
+
+ pw.print(" mNoteResponseEventForAllBroadcastSessions=");
+ pw.print(mNoteResponseEventForAllBroadcastSessions);
+ pw.println();
+
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
pw.print(" mAllowRestrictedBucket=");
@@ -2672,6 +2725,13 @@ public class AppStandbyController
"broadcast_response_window_timeout_ms";
private static final String KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
"broadcast_response_fg_threshold_state";
+ private static final String KEY_BROADCAST_SESSIONS_DURATION_MS =
+ "broadcast_sessions_duration_ms";
+ private static final String KEY_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS =
+ "broadcast_sessions_with_response_duration_ms";
+ private static final String KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
+ "note_response_event_for_all_broadcast_sessions";
+
public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
public static final long DEFAULT_STRONG_USAGE_TIMEOUT =
@@ -2705,6 +2765,12 @@ public class AppStandbyController
2 * ONE_MINUTE;
public static final int DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
ActivityManager.PROCESS_STATE_TOP;
+ public static final long DEFAULT_BROADCAST_SESSIONS_DURATION_MS =
+ 2 * ONE_MINUTE;
+ public static final long DEFAULT_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS =
+ 2 * ONE_MINUTE;
+ public static final boolean DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
+ true;
ConstantsObserver(Handler handler) {
super(handler);
@@ -2832,6 +2898,21 @@ public class AppStandbyController
KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE,
DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE);
break;
+ case KEY_BROADCAST_SESSIONS_DURATION_MS:
+ mBroadcastSessionsDurationMs = properties.getLong(
+ KEY_BROADCAST_SESSIONS_DURATION_MS,
+ DEFAULT_BROADCAST_SESSIONS_DURATION_MS);
+ break;
+ case KEY_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS:
+ mBroadcastSessionsWithResponseDurationMs = properties.getLong(
+ KEY_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS,
+ DEFAULT_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS);
+ break;
+ case KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS:
+ mNoteResponseEventForAllBroadcastSessions = properties.getBoolean(
+ KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS,
+ DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS);
+ break;
default:
if (!timeThresholdsUpdated
&& (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 668dc6b8ec63..73678d9f2dda 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -98,6 +98,15 @@ public class ActivityClient {
}
}
+ /** Reports the activity starts local relaunch. */
+ public void activityLocalRelaunch(IBinder token) {
+ try {
+ getActivityClientController().activityLocalRelaunch(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
/** Reports the activity has completed relaunched. */
public void activityRelaunched(IBinder token) {
try {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b4cabada0522..3158bd7b21db 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5728,6 +5728,7 @@ public final class ActivityThread extends ClientTransactionHandler
return;
}
+ ActivityClient.getInstance().activityLocalRelaunch(r.token);
// Initialize a relaunch request.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
r.createdConfig != null
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a216021fc66b..cb64173b7809 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2709,7 +2709,7 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
- AppOpsManager.MODE_DEFAULT, // SYSTEM_ALERT_WINDOW /*Overridden in opToDefaultMode()*/
+ getSystemAlertWindowDefault(), // SYSTEM_ALERT_WINDOW
AppOpsManager.MODE_ALLOWED, // ACCESS_NOTIFICATIONS
AppOpsManager.MODE_ALLOWED, // CAMERA
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO
@@ -3163,8 +3163,6 @@ public class AppOpsManager {
private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
- private static volatile Integer sOpSystemAlertWindowDefaultMode;
-
/**
* Retrieve the op switch that controls the given operation.
* @hide
@@ -3263,9 +3261,6 @@ public class AppOpsManager {
* @hide
*/
public static @Mode int opToDefaultMode(int op) {
- if (op == OP_SYSTEM_ALERT_WINDOW) {
- return getSystemAlertWindowDefault();
- }
return sOpDefaultMode[op];
}
@@ -10283,11 +10278,6 @@ public class AppOpsManager {
}
private static int getSystemAlertWindowDefault() {
- // This is indeed racy but we aren't expecting the result to change so it's not worth
- // the synchronization.
- if (sOpSystemAlertWindowDefaultMode != null) {
- return sOpSystemAlertWindowDefaultMode;
- }
final Context context = ActivityThread.currentApplication();
if (context == null) {
return AppOpsManager.MODE_DEFAULT;
@@ -10298,11 +10288,10 @@ public class AppOpsManager {
// TVs are constantly plugged in and has less concern for memory/power
if (ActivityManager.isLowRamDeviceStatic()
&& !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0)) {
- sOpSystemAlertWindowDefaultMode = AppOpsManager.MODE_IGNORED;
- } else {
- sOpSystemAlertWindowDefaultMode = AppOpsManager.MODE_DEFAULT;
+ return AppOpsManager.MODE_IGNORED;
}
- return sOpSystemAlertWindowDefaultMode;
+
+ return AppOpsManager.MODE_DEFAULT;
}
/**
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 130716122ed2..0138186974a6 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -53,6 +53,7 @@ interface IActivityClientController {
oneway void activityStopped(in IBinder token, in Bundle state,
in PersistableBundle persistentState, in CharSequence description);
oneway void activityDestroyed(in IBinder token);
+ oneway void activityLocalRelaunch(in IBinder token);
oneway void activityRelaunched(in IBinder token);
oneway void reportSizeConfigurations(in IBinder token,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8647b9a2967c..93f91e5af3eb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11413,7 +11413,9 @@ public class DevicePolicyManager {
/**
* Called by a device owner or a profile owner of an organization-owned managed profile to
- * control whether the user can change networks configured by the admin.
+ * control whether the user can change networks configured by the admin. When this lockdown is
+ * enabled, the user can still configure and connect to other Wi-Fi networks, or use other Wi-Fi
+ * capabilities such as tethering.
* <p>
* WiFi network configuration lockdown is controlled by a global settings
* {@link android.provider.Settings.Global#WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN} and calling
diff --git a/core/java/android/app/usage/OWNERS b/core/java/android/app/usage/OWNERS
index 9668f80e562e..a4bf98504591 100644
--- a/core/java/android/app/usage/OWNERS
+++ b/core/java/android/app/usage/OWNERS
@@ -5,3 +5,4 @@ mwachens@google.com
varunshah@google.com
per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
+per-file *Broadcast* = sudheersai@google.com
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index dd00c3abd251..ac65933db136 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -21,8 +21,6 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.system.ErrnoException;
-import android.system.Os;
import java.io.Closeable;
import java.io.FileDescriptor;
@@ -54,11 +52,11 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
/**
* Create a new AssetFileDescriptor from the given values.
*
- * @param fd The underlying file descriptor.
+ * @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
- * This must be 0 if length is UNKNOWN_LENGTH.
- * @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length) {
@@ -68,13 +66,13 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
/**
* Create a new AssetFileDescriptor from the given values.
*
- * @param fd The underlying file descriptor.
+ * @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
- * This must be 0 if length is UNKNOWN_LENGTH.
- * @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
- * @param extras additional details that can be used to interpret the
- * underlying file descriptor. May be null.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+ * @param extras additional details that can be used to interpret the
+ * underlying file descriptor. May be null.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length, Bundle extras) {
@@ -205,24 +203,19 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
*/
public static class AutoCloseInputStream
extends ParcelFileDescriptor.AutoCloseInputStream {
- private final long mSizeFromStartOffset;
- private final long mStartOffset;
- private long mPosFromStartOffset;
+ private long mRemaining;
public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
- // this skip is necessary if getChannel() is called
super.skip(fd.getStartOffset());
- mSizeFromStartOffset = fd.getLength();
- mStartOffset = fd.getStartOffset();
+ mRemaining = (int) fd.getLength();
}
@Override
public int available() throws IOException {
- long available = mSizeFromStartOffset - mPosFromStartOffset;
- return available >= 0
- ? (available < 0x7fffffff ? (int) available : 0x7fffffff)
- : 0;
+ return mRemaining >= 0
+ ? (mRemaining < 0x7fffffff ? (int) mRemaining : 0x7fffffff)
+ : super.available();
}
@Override
@@ -234,24 +227,15 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
- int available = available();
-
- if (available <= 0) {
- return -1;
- } else {
- if (count > available) count = available;
- try {
- int res = Os.pread(getFD(), buffer, offset, count,
- mStartOffset + mPosFromStartOffset);
- // pread returns 0 at end of file, while java's InputStream interface
- // requires -1
- if (res == 0) res = -1;
- if (res >= 0) mPosFromStartOffset += res;
- return res;
- } catch (ErrnoException e) {
- throw new IOException(e);
- }
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return -1;
+ if (count > mRemaining) count = (int) mRemaining;
+ int res = super.read(buffer, offset, count);
+ if (res >= 0) mRemaining -= res;
+ return res;
}
+
+ return super.read(buffer, offset, count);
}
@Override
@@ -261,31 +245,41 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
@Override
public long skip(long count) throws IOException {
- int available = available();
- if (available <= 0) {
- return -1;
- } else {
- if (count > available) count = available;
- mPosFromStartOffset += count;
- return count;
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return -1;
+ if (count > mRemaining) count = mRemaining;
+ long res = super.skip(count);
+ if (res >= 0) mRemaining -= res;
+ return res;
}
+
+ return super.skip(count);
}
@Override
public void mark(int readlimit) {
- // Not supported.
- return;
+ if (mRemaining >= 0) {
+ // Not supported.
+ return;
+ }
+ super.mark(readlimit);
}
@Override
public boolean markSupported() {
- return false;
+ if (mRemaining >= 0) {
+ return false;
+ }
+ return super.markSupported();
}
@Override
public synchronized void reset() throws IOException {
- // Not supported.
- return;
+ if (mRemaining >= 0) {
+ // Not supported.
+ return;
+ }
+ super.reset();
}
}
@@ -381,7 +375,6 @@ public class AssetFileDescriptor implements Parcelable, Closeable {
public AssetFileDescriptor createFromParcel(Parcel in) {
return new AssetFileDescriptor(in);
}
-
public AssetFileDescriptor[] newArray(int size) {
return new AssetFileDescriptor[size];
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 0d3aaf575729..10c37301b0b0 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -712,14 +712,16 @@ public final class Sensor {
public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
/**
- * A constant describing a head tracker sensor.
+ * A constant describing a head tracker sensor. Note that this sensor type is typically not
+ * available for apps to use.
*
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
*/
public static final int TYPE_HEAD_TRACKER = 37;
/**
- * A constant string describing a head tracker sensor.
+ * A constant string describing a head tracker sensor. Note that this sensor type is typically
+ * not available for apps to use.
*
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
*/
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index bb0caa787e27..4a2517763aae 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -166,7 +166,16 @@ public final class OutputConfiguration implements Parcelable {
* {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
* {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For all other cases, the timestamp base is {@link #TIMESTAMP_BASE_SENSOR}, the same
- * as what's specified by {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE}.</li>
+ * as what's specified by {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE}.
+ * <ul><li> For a SurfaceTexture output surface, the camera system re-spaces the delivery
+ * of output frames based on image readout intervals, reducing viewfinder jitter. The timestamps
+ * of images remain to be {@link #TIMESTAMP_BASE_SENSOR}.</li></ul></li>
+ *
+ * <p>Note that the reduction of frame jitter for SurfaceView and SurfaceTexture comes with
+ * slight increase in photon-to-photon latency, which is the time from when photons hit the
+ * scene to when the corresponding pixels show up on the screen. If the photon-to-photon latency
+ * is more important than the smoothness of viewfinder, {@link #TIMESTAMP_BASE_SENSOR} should be
+ * used instead.</p>
*
* @see #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED
* @see #TIMESTAMP_BASE_MONOTONIC
diff --git a/core/java/android/hardware/input/TEST_MAPPING b/core/java/android/hardware/input/TEST_MAPPING
new file mode 100644
index 000000000000..9626d8dac787
--- /dev/null
+++ b/core/java/android/hardware/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/native/services/inputflinger"
+ }
+ ]
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 200fe22edaad..21ecf8b0888c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -592,7 +592,7 @@ public class InputMethodService extends AbstractInputMethodService {
private InlineSuggestionSessionController mInlineSuggestionSessionController;
- private boolean mAutomotiveHideNavBarForKeyboard;
+ private boolean mHideNavBarForKeyboard;
private boolean mIsAutomotive;
private @NonNull OptionalInt mHandwritingRequestId = OptionalInt.empty();
private InputEventReceiver mHandwritingEventReceiver;
@@ -1498,9 +1498,8 @@ public class InputMethodService extends AbstractInputMethodService {
// shown the first time (cold start).
mSettingsObserver.shouldShowImeWithHardKeyboard();
- mIsAutomotive = isAutomotive();
- mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
- com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+ mHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_hideNavBarForKeyboard);
// TODO(b/111364446) Need to address context lifecycle issue if need to re-create
// for update resources & configuration correctly when show soft input
@@ -1539,11 +1538,11 @@ public class InputMethodService extends AbstractInputMethodService {
window.setFlags(windowFlags, windowFlagsMask);
// Automotive devices may request the navigation bar to be hidden when the IME shows up
- // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the
- // visible screen real estate. When this happens, the IME window should animate from the
+ // (controlled via config_hideNavBarForKeyboard) in order to maximize the visible
+ // screen real estate. When this happens, the IME window should animate from the
// bottom of the screen to reduce the jank that happens from the lack of synchronization
// between the bottom system window and the IME window.
- if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
+ if (mHideNavBarForKeyboard) {
window.setDecorFitsSystemWindows(false);
}
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 8f2d218a20c1..13ca2c34b27e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -463,21 +463,22 @@ public final class PowerManager {
/**
* @hide
*/
- public static String sleepReasonToString(int sleepReason) {
+ public static String sleepReasonToString(@GoToSleepReason int sleepReason) {
switch (sleepReason) {
+ case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
case GO_TO_SLEEP_REASON_APPLICATION: return "application";
case GO_TO_SLEEP_REASON_DEVICE_ADMIN: return "device_admin";
- case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout";
+ case GO_TO_SLEEP_REASON_DEVICE_FOLD: return "device_folded";
+ case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
+ case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
+ case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
+ case GO_TO_SLEEP_REASON_HDMI: return "hdmi";
+ case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
case GO_TO_SLEEP_REASON_LID_SWITCH: return "lid_switch";
case GO_TO_SLEEP_REASON_POWER_BUTTON: return "power_button";
- case GO_TO_SLEEP_REASON_HDMI: return "hdmi";
+ case GO_TO_SLEEP_REASON_QUIESCENT: return "quiescent";
case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
- case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
- case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
- case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
- case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
- case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
- case GO_TO_SLEEP_REASON_DEVICE_FOLD: return "device_folded";
+ case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout";
default: return Integer.toString(sleepReason);
}
}
@@ -576,18 +577,20 @@ public final class PowerManager {
* @hide
*/
@IntDef(prefix = { "GO_TO_SLEEP_REASON_" }, value = {
+ GO_TO_SLEEP_REASON_ACCESSIBILITY,
GO_TO_SLEEP_REASON_APPLICATION,
GO_TO_SLEEP_REASON_DEVICE_ADMIN,
- GO_TO_SLEEP_REASON_TIMEOUT,
- GO_TO_SLEEP_REASON_LID_SWITCH,
- GO_TO_SLEEP_REASON_POWER_BUTTON,
- GO_TO_SLEEP_REASON_HDMI,
- GO_TO_SLEEP_REASON_SLEEP_BUTTON,
- GO_TO_SLEEP_REASON_ACCESSIBILITY,
+ GO_TO_SLEEP_REASON_DEVICE_FOLD,
+ GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED,
+ GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF,
GO_TO_SLEEP_REASON_FORCE_SUSPEND,
+ GO_TO_SLEEP_REASON_HDMI,
GO_TO_SLEEP_REASON_INATTENTIVE,
+ GO_TO_SLEEP_REASON_LID_SWITCH,
+ GO_TO_SLEEP_REASON_POWER_BUTTON,
GO_TO_SLEEP_REASON_QUIESCENT,
- GO_TO_SLEEP_REASON_DEVICE_FOLD
+ GO_TO_SLEEP_REASON_SLEEP_BUTTON,
+ GO_TO_SLEEP_REASON_TIMEOUT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface GoToSleepReason{}
@@ -704,6 +707,8 @@ public final class PowerManager {
}
/**
+ * Information related to the device waking up, triggered by {@link #wakeUp}.
+ *
* @hide
*/
public static class WakeData {
@@ -712,9 +717,9 @@ public final class PowerManager {
this.wakeReason = wakeReason;
this.sleepDuration = sleepDuration;
}
- public long wakeTime;
- public @WakeReason int wakeReason;
- public long sleepDuration;
+ public final long wakeTime;
+ public final @WakeReason int wakeReason;
+ public final long sleepDuration;
@Override
public boolean equals(@Nullable Object o) {
@@ -733,6 +738,35 @@ public final class PowerManager {
}
/**
+ * Information related to the device going to sleep, triggered by {@link #goToSleep}.
+ *
+ * @hide
+ */
+ public static class SleepData {
+ public SleepData(long goToSleepUptimeMillis, @GoToSleepReason int goToSleepReason) {
+ this.goToSleepUptimeMillis = goToSleepUptimeMillis;
+ this.goToSleepReason = goToSleepReason;
+ }
+ public final long goToSleepUptimeMillis;
+ public final @GoToSleepReason int goToSleepReason;
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof SleepData) {
+ final SleepData other = (SleepData) o;
+ return goToSleepUptimeMillis == other.goToSleepUptimeMillis
+ && goToSleepReason == other.goToSleepReason;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(goToSleepUptimeMillis, goToSleepReason);
+ }
+ }
+
+ /**
* The value to pass as the 'reason' argument to reboot() to reboot into
* recovery mode for tasks other than applying system updates, such as
* doing factory resets.
@@ -2644,6 +2678,7 @@ public final class PowerManager {
*
* @hide
*/
+ @GoToSleepReason
public int getLastSleepReason() {
try {
return mService.getLastSleepReason();
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index ec4d3b6a2441..5ca0da2d3f97 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -330,6 +330,9 @@ public abstract class PowerManagerInternal {
/** Returns information about the last wakeup event. */
public abstract PowerManager.WakeData getLastWakeup();
+ /** Returns information about the last event to go to sleep. */
+ public abstract PowerManager.SleepData getLastGoToSleep();
+
/** Allows power button to intercept a power key button press. */
public abstract boolean interceptPowerKeyDown(KeyEvent event);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 196f2f94120e..0ffdfc6cbcb1 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -257,7 +257,8 @@ public class UserManager {
public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
/**
- * Specifies if a user is disallowed from changing Wi-Fi access points via Settings.
+ * Specifies if a user is disallowed from changing Wi-Fi access points via Settings. This
+ * restriction does not affect Wi-Fi tethering settings.
*
* <p>A device owner and a profile owner can set this restriction, although the restriction has
* no effect in a managed profile. When it is set by a device owner, a profile owner on the
@@ -295,6 +296,9 @@ public class UserManager {
/**
* Specifies if a user is disallowed from using Wi-Fi tethering.
*
+ * <p>This restriction does not limit the user's ability to modify or connect to regular
+ * Wi-Fi networks, which is separately controlled by {@link #DISALLOW_CONFIG_WIFI}.
+ *
* <p>This restriction can only be set by a device owner,
* a profile owner of an organization-owned managed profile on the parent profile.
* When it is set by any of these owners, it prevents all users from using
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 6a65efb891ca..6b59f5480e04 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -129,8 +129,9 @@ public abstract class RecognitionService extends Service {
@NonNull AttributionSource attributionSource) {
try {
if (mCurrentCallback == null) {
- boolean preflightPermissionCheckPassed = checkPermissionForPreflightNotHardDenied(
- attributionSource);
+ boolean preflightPermissionCheckPassed =
+ intent.hasExtra(RecognizerIntent.EXTRA_AUDIO_SOURCE)
+ || checkPermissionForPreflightNotHardDenied(attributionSource);
if (preflightPermissionCheckPassed) {
if (DBG) {
Log.d(TAG, "created new mCurrentCallback, listener = "
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2ad1b3845bdc..7cc37f70f8fa 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -190,6 +190,7 @@ public class ChooserActivity extends ResolverActivity implements
private static final String SHORTCUT_TARGET = "shortcut_target";
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
+ private static final String SHARED_TEXT_KEY = "shared_text";
private static final String PLURALS_COUNT = "count";
private static final String PLURALS_FILE_NAME = "file_name";
@@ -2201,6 +2202,7 @@ public class ChooserActivity extends ResolverActivity implements
final IntentFilter filter = getTargetIntentFilter();
Bundle extras = new Bundle();
extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
+ populateTextContent(extras);
AppPredictionContext appPredictionContext = new AppPredictionContext.Builder(contextAsUser)
.setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
.setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT)
@@ -2219,6 +2221,12 @@ public class ChooserActivity extends ResolverActivity implements
return appPredictionSession;
}
+ private void populateTextContent(Bundle extras) {
+ final Intent intent = getTargetIntent();
+ String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
+ extras.putString(SHARED_TEXT_KEY, sharedText);
+ }
+
/**
* This will return an app predictor if it is enabled for direct share sorting
* and if one exists. Otherwise, it returns null.
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 2e4860a6da26..2dcc58564f82 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -74,6 +74,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.annotation.IntDef;
@@ -196,6 +197,7 @@ public class InteractionJankMonitor {
public static final int CUJ_SPLIT_SCREEN_RESIZE = 52;
public static final int CUJ_SETTINGS_SLIDER = 53;
public static final int CUJ_TAKE_SCREENSHOT = 54;
+ public static final int CUJ_VOLUME_CONTROL = 55;
private static final int NO_STATSD_LOGGING = -1;
@@ -259,6 +261,7 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL,
};
private static volatile InteractionJankMonitor sInstance;
@@ -334,6 +337,7 @@ public class InteractionJankMonitor {
CUJ_SPLIT_SCREEN_RESIZE,
CUJ_SETTINGS_SLIDER,
CUJ_TAKE_SCREENSHOT,
+ CUJ_VOLUME_CONTROL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -762,6 +766,8 @@ public class InteractionJankMonitor {
return "SETTINGS_SLIDER";
case CUJ_TAKE_SCREENSHOT:
return "TAKE_SCREENSHOT";
+ case CUJ_VOLUME_CONTROL:
+ return "VOLUME_CONTROL";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 06d12b5195ab..3436b9e75c65 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -682,7 +682,6 @@ public class SystemConfig {
readPermissions(parser, Environment.buildPath(f, "etc", "permissions"),
apexPermissionFlag);
}
- pruneVendorApexPrivappAllowlists();
}
@VisibleForTesting
@@ -1598,21 +1597,6 @@ public class SystemConfig {
}
}
- /**
- * Prunes out any privileged permission allowlists bundled in vendor apexes.
- */
- @VisibleForTesting
- public void pruneVendorApexPrivappAllowlists() {
- for (String moduleName: mAllowedVendorApexes.keySet()) {
- if (mApexPrivAppPermissions.containsKey(moduleName)
- || mApexPrivAppDenyPermissions.containsKey(moduleName)) {
- Slog.w(TAG, moduleName + " is a vendor apex, ignore its priv-app allowlist");
- mApexPrivAppPermissions.remove(moduleName);
- mApexPrivAppDenyPermissions.remove(moduleName);
- }
- }
- }
-
private void readInstallInUserType(XmlPullParser parser,
Map<String, Set<String>> doInstallMap,
Map<String, Set<String>> nonInstallMap)
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 2f2158d4d5a0..2a625b027c17 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -86,7 +86,7 @@ message VibrationAttributesProto {
optional int32 flags = 3;
}
-// Next id: 7
+// Next id: 8
message VibrationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 start_time = 1;
@@ -95,6 +95,7 @@ message VibrationProto {
optional CombinedVibrationEffectProto original_effect = 4;
optional VibrationAttributesProto attributes = 5;
optional int32 status = 6;
+ optional int64 duration_ms = 7;
}
// Next id: 25
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f20b824e6bf7..8db5f9a1f51b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7044,7 +7044,7 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
- <service android:name="com.android.server.companion.AssociationCleanUpService"
+ <service android:name="com.android.server.companion.InactiveAssociationsRemovalService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 178bfbc80e5f..b139e472460a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4894,7 +4894,7 @@
<!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
create additional screen real estate outside beyond the keyboard. Note that the user needs
to have a confirmed way to dismiss the keyboard when desired. -->
- <bool name="config_automotiveHideNavBarForKeyboard">false</bool>
+ <bool name="config_hideNavBarForKeyboard">false</bool>
<!-- Whether or not to show the built-in charging animation when the device begins charging
wirelessly. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dcd706c15ff0..77007afc2156 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4154,7 +4154,7 @@
<java-symbol type="bool" name="config_disable_all_cb_messages" />
<java-symbol type="drawable" name="ic_close" />
- <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
+ <java-symbol type="bool" name="config_hideNavBarForKeyboard" />
<java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 04ead1bf1e9c..58a2073981bf 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,30 +33,6 @@ applications that come with the platform
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
- <privapp-permissions package="com.android.bluetooth.services">
- <permission name="android.permission.DUMP"/>
- <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.permission.TETHER_PRIVILEGED"/>
- <permission name="android.permission.CALL_PRIVILEGED"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <permission name="android.permission.UPDATE_DEVICE_STATS"/>
- <permission name="android.permission.PACKAGE_USAGE_STATS"/>
- <permission name="android.permission.NFC_HANDOVER_STATUS"/>
- <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
- <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.permission.REAL_GET_TASKS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
- <permission name="android.permission.WRITE_APN_SETTINGS"/>
- <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.backupconfirm">
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.CRYPT_KEEPER"/>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 8ba41ab60c87..f03b7f66cdc8 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -100,4 +100,7 @@
<!-- The default gravity for the picture-in-picture window.
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
<integer name="config_defaultPictureInPictureGravity">0x55</integer>
+
+ <!-- Whether to dim a split-screen task when the other is the IME target -->
+ <bool name="config_dimNonImeAttachedSide">true</bool>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 8fa9f564fb22..a16841cb870a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -67,8 +67,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
SETTING_VALUE_ON) != SETTING_VALUE_OFF;
private static final int PROGRESS_THRESHOLD = SystemProperties
.getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
-
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
+ /**
+ * Max duration to wait for a transition to finish before accepting another gesture start
+ * request.
+ */
+ private static final long MAX_TRANSITION_DURATION = 2000;
/**
* Location of the initial touch event of the back gesture.
@@ -84,6 +88,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false;
+ /** Tracks if an uninterruptible transition is in progress */
+ private boolean mTransitionInProgress = false;
/** @see #setTriggerBack(boolean) */
private boolean mTriggerBack;
@@ -96,6 +102,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private IOnBackInvokedCallback mBackToLauncherCallback;
private float mTriggerThreshold;
private float mProgressThreshold;
+ private final Runnable mResetTransitionRunnable = () -> {
+ finishAnimation();
+ mTransitionInProgress = false;
+ };
public BackAnimationController(
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@@ -229,7 +239,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mBackToLauncherCallback = null;
}
- private void onBackToLauncherAnimationFinished() {
+ @VisibleForTesting
+ void onBackToLauncherAnimationFinished() {
if (mBackNavigationInfo != null) {
IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
if (mTriggerBack) {
@@ -246,6 +257,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* {@link BackAnimationController}
*/
public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
+ if (mTransitionInProgress) {
+ return;
+ }
if (action == MotionEvent.ACTION_MOVE) {
if (!mBackGestureStarted) {
// Let the animation initialized here to make sure the onPointerDownOutsideFocus
@@ -370,6 +384,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher
? mBackToLauncherCallback
: mBackNavigationInfo.getOnBackInvokedCallback();
+ if (shouldDispatchToLauncher) {
+ startTransition();
+ }
if (mTriggerBack) {
dispatchOnBackInvoked(targetCallback);
} else {
@@ -436,6 +453,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Sets to true when the back gesture has passed the triggering threshold, false otherwise.
*/
public void setTriggerBack(boolean triggerBack) {
+ if (mTransitionInProgress) {
+ return;
+ }
mTriggerBack = triggerBack;
}
@@ -467,6 +487,23 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mTransaction.remove(screenshotSurface);
}
mTransaction.apply();
+ stopTransition();
backNavigationInfo.onBackNavigationFinished(triggerBack);
}
+
+ private void startTransition() {
+ if (mTransitionInProgress) {
+ return;
+ }
+ mTransitionInProgress = true;
+ mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
+ }
+
+ private void stopTransition() {
+ if (!mTransitionInProgress) {
+ return;
+ }
+ mShellExecutor.removeCallbacks(mResetTransitionRunnable);
+ mTransitionInProgress = false;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index f407bdcb8852..f427a2c4bc95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -66,6 +66,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
@@ -96,6 +97,8 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.annotations.ShellBackgroundThread;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
@@ -145,6 +148,7 @@ public class BubbleController {
private final FloatingContentCoordinator mFloatingContentCoordinator;
private final BubbleDataRepository mDataRepository;
private final WindowManagerShellWrapper mWindowManagerShellWrapper;
+ private final UserManager mUserManager;
private final LauncherApps mLauncherApps;
private final IStatusBarService mBarService;
private final WindowManager mWindowManager;
@@ -158,6 +162,8 @@ public class BubbleController {
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
+ private final ShellExecutor mBackgroundExecutor;
+
private BubbleLogger mLogger;
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
@@ -227,6 +233,7 @@ public class BubbleController {
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
+ UserManager userManager,
LauncherApps launcherApps,
TaskStackListenerImpl taskStackListener,
UiEventLogger uiEventLogger,
@@ -234,8 +241,9 @@ public class BubbleController {
DisplayController displayController,
Optional<OneHandedController> oneHandedOptional,
DragAndDropController dragAndDropController,
- ShellExecutor mainExecutor,
- Handler mainHandler,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
@@ -243,9 +251,9 @@ public class BubbleController {
BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps, mainExecutor),
- statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, taskStackListener, organizer, positioner, displayController,
- oneHandedOptional, dragAndDropController, mainExecutor, mainHandler,
+ statusBarService, windowManager, windowManagerShellWrapper, userManager,
+ launcherApps, logger, taskStackListener, organizer, positioner, displayController,
+ oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
taskViewTransitions, syncQueue);
}
@@ -261,6 +269,7 @@ public class BubbleController {
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
+ UserManager userManager,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
TaskStackListenerImpl taskStackListener,
@@ -269,8 +278,9 @@ public class BubbleController {
DisplayController displayController,
Optional<OneHandedController> oneHandedOptional,
DragAndDropController dragAndDropController,
- ShellExecutor mainExecutor,
- Handler mainHandler,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
mContext = context;
@@ -281,11 +291,13 @@ public class BubbleController {
: statusBarService;
mWindowManager = windowManager;
mWindowManagerShellWrapper = windowManagerShellWrapper;
+ mUserManager = userManager;
mFloatingContentCoordinator = floatingContentCoordinator;
mDataRepository = dataRepository;
mLogger = bubbleLogger;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mBackgroundExecutor = bgExecutor;
mTaskStackListener = taskStackListener;
mTaskOrganizer = organizer;
mSurfaceSynchronizer = synchronizer;
@@ -440,6 +452,10 @@ public class BubbleController {
mOneHandedOptional.ifPresent(this::registerOneHandedState);
mDragAndDropController.addListener(this::collapseStack);
+
+ // Clear out any persisted bubbles on disk that no longer have a valid user.
+ List<UserInfo> users = mUserManager.getAliveUsers();
+ mDataRepository.sanitizeBubbles(users);
}
@VisibleForTesting
@@ -583,6 +599,17 @@ public class BubbleController {
mCurrentProfiles = currentProfiles;
}
+ /** Called when a user is removed from the device, including work profiles. */
+ public void onUserRemoved(int removedUserId) {
+ UserInfo parent = mUserManager.getProfileParent(removedUserId);
+ int parentUserId = parent != null ? parent.getUserHandle().getIdentifier() : -1;
+ mBubbleData.removeBubblesForUser(removedUserId);
+ // Typically calls from BubbleData would remove bubbles from the DataRepository as well,
+ // however, this gets complicated when users are removed (mCurrentUserId won't necessarily
+ // be correct for this) so we update the repo directly.
+ mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
+ }
+
/** Whether this userId belongs to the current user. */
private boolean isCurrentProfile(int userId) {
return userId == UserHandle.USER_ALL
@@ -725,7 +752,8 @@ public class BubbleController {
try {
mAddedToWindowManager = false;
- mContext.unregisterReceiver(mBroadcastReceiver);
+ // Put on background for this binder call, was causing jank
+ mBackgroundExecutor.execute(() -> mContext.unregisterReceiver(mBroadcastReceiver));
if (mStackView != null) {
mWindowManager.removeView(mStackView);
mBubbleData.getOverflow().cleanUpExpandedState();
@@ -1801,6 +1829,13 @@ public class BubbleController {
}
@Override
+ public void onUserRemoved(int removedUserId) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onUserRemoved(removedUserId);
+ });
+ }
+
+ @Override
public void onConfigChanged(Configuration newConfig) {
mMainExecutor.execute(() -> {
BubbleController.this.onConfigChanged(newConfig);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index e4a0fd03860c..fa86c8436647 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -465,7 +465,7 @@ public class BubbleData {
getOverflowBubbles(), invalidBubblesFromPackage, removeBubble);
}
- /** Dismisses all bubbles from the given package. */
+ /** Removes all bubbles from the given package. */
public void removeBubblesWithPackageName(String packageName, int reason) {
final Predicate<Bubble> bubbleMatchesPackage = bubble ->
bubble.getPackageName().equals(packageName);
@@ -477,6 +477,18 @@ public class BubbleData {
performActionOnBubblesMatching(getOverflowBubbles(), bubbleMatchesPackage, removeBubble);
}
+ /** Removes all bubbles for the given user. */
+ public void removeBubblesForUser(int userId) {
+ List<Bubble> removedBubbles = filterAllBubbles(bubble ->
+ userId == bubble.getUser().getIdentifier());
+ for (Bubble b : removedBubbles) {
+ doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED);
+ }
+ if (!removedBubbles.isEmpty()) {
+ dispatchPendingChanges();
+ }
+ }
+
private void doAdd(Bubble bubble) {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "doAdd: " + bubble);
@@ -552,7 +564,8 @@ public class BubbleData {
|| reason == Bubbles.DISMISS_BLOCKED
|| reason == Bubbles.DISMISS_SHORTCUT_REMOVED
|| reason == Bubbles.DISMISS_PACKAGE_REMOVED
- || reason == Bubbles.DISMISS_USER_CHANGED;
+ || reason == Bubbles.DISMISS_USER_CHANGED
+ || reason == Bubbles.DISMISS_USER_REMOVED;
int indexToRemove = indexForKey(key);
if (indexToRemove == -1) {
@@ -1073,6 +1086,35 @@ public class BubbleData {
return null;
}
+ /**
+ * Returns a list of bubbles that match the provided predicate. This checks all types of
+ * bubbles (i.e. pending, suppressed, active, and overflowed).
+ */
+ private List<Bubble> filterAllBubbles(Predicate<Bubble> predicate) {
+ ArrayList<Bubble> matchingBubbles = new ArrayList<>();
+ for (Bubble b : mPendingBubbles.values()) {
+ if (predicate.test(b)) {
+ matchingBubbles.add(b);
+ }
+ }
+ for (Bubble b : mSuppressedBubbles.values()) {
+ if (predicate.test(b)) {
+ matchingBubbles.add(b);
+ }
+ }
+ for (Bubble b : mBubbles) {
+ if (predicate.test(b)) {
+ matchingBubbles.add(b);
+ }
+ }
+ for (Bubble b : mOverflowBubbles) {
+ if (predicate.test(b)) {
+ matchingBubbles.add(b);
+ }
+ }
+ return matchingBubbles;
+ }
+
@VisibleForTesting(visibility = PRIVATE)
void setTimeSource(TimeSource timeSource) {
mTimeSource = timeSource;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 9d9e442affd3..97560f44fb06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -22,6 +22,7 @@ import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
+import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
import com.android.wm.shell.bubbles.storage.BubbleEntity
@@ -73,6 +74,22 @@ internal class BubbleDataRepository(
if (entities.isNotEmpty()) persistToDisk()
}
+ /**
+ * Removes all the bubbles associated with the provided user from memory. Then persists the
+ * snapshot to disk asynchronously.
+ */
+ fun removeBubblesForUser(@UserIdInt userId: Int, @UserIdInt parentId: Int) {
+ if (volatileRepository.removeBubblesForUser(userId, parentId)) persistToDisk()
+ }
+
+ /**
+ * Remove any bubbles that don't have a user id from the provided list of users.
+ */
+ fun sanitizeBubbles(users: List<UserInfo>) {
+ val userIds = users.map { u -> u.id }
+ if (volatileRepository.sanitizeBubbles(userIds)) persistToDisk()
+ }
+
private fun transform(bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
BubbleEntity(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index c7db8d8d1646..8a0db0a12711 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -57,7 +57,7 @@ public interface Bubbles {
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK})
+ DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {}
@@ -76,6 +76,7 @@ public interface Bubbles {
int DISMISS_PACKAGE_REMOVED = 13;
int DISMISS_NO_BUBBLE_UP = 14;
int DISMISS_RELOAD_FROM_DISK = 15;
+ int DISMISS_USER_REMOVED = 16;
/**
* @return {@code true} if there is a bubble associated with the provided key and if its
@@ -243,6 +244,13 @@ public interface Bubbles {
void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles);
/**
+ * Called when a user is removed.
+ *
+ * @param removedUserId the id of the removed user.
+ */
+ void onUserRemoved(int removedUserId);
+
+ /**
* Called when config changed.
*
* @param newConfig the new config.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt
index a5267d8be9fe..0d3ba9a78e35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.bubbles.storage
+import android.annotation.UserIdInt
import android.content.pm.LauncherApps
import android.os.UserHandle
import android.util.SparseArray
@@ -95,10 +96,63 @@ class BubbleVolatileRepository(private val launcherApps: LauncherApps) {
}
@Synchronized
- fun removeBubbles(userId: Int, bubbles: List<BubbleEntity>) =
+ fun removeBubbles(@UserIdInt userId: Int, bubbles: List<BubbleEntity>) =
uncache(bubbles.filter { b: BubbleEntity ->
getEntities(userId).removeIf { e: BubbleEntity -> b.key == e.key } })
+ /**
+ * Removes all the bubbles associated with the provided userId.
+ * @return whether bubbles were removed or not.
+ */
+ @Synchronized
+ fun removeBubblesForUser(@UserIdInt userId: Int, @UserIdInt parentUserId: Int): Boolean {
+ if (parentUserId != -1) {
+ return removeBubblesForUserWithParent(userId, parentUserId)
+ } else {
+ val entities = entitiesByUser.get(userId)
+ entitiesByUser.remove(userId)
+ return entities != null
+ }
+ }
+
+ /**
+ * Removes all the bubbles associated with the provided userId when that userId is part of
+ * a profile (e.g. managed account).
+ *
+ * @return whether bubbles were removed or not.
+ */
+ @Synchronized
+ private fun removeBubblesForUserWithParent(
+ @UserIdInt userId: Int,
+ @UserIdInt parentUserId: Int
+ ): Boolean {
+ return entitiesByUser.get(parentUserId).removeIf { b: BubbleEntity -> b.userId == userId }
+ }
+
+ /**
+ * Goes through all the persisted bubbles and removes them if the user is not in the active
+ * list of users.
+ *
+ * @return whether the list of bubbles changed or not (i.e. was a removal made).
+ */
+ @Synchronized
+ fun sanitizeBubbles(activeUsers: List<Int>): Boolean {
+ for (i in 0 until entitiesByUser.size()) {
+ // First check if the user is a parent / top-level user
+ val parentUserId = entitiesByUser.keyAt(i)
+ if (!activeUsers.contains(parentUserId)) {
+ return removeBubblesForUser(parentUserId, -1)
+ } else {
+ // Then check if each of the bubbles in the top-level user, still has a valid user
+ // as it could belong to a profile and have a different id from the parent.
+ return entitiesByUser.get(parentUserId).removeIf { b: BubbleEntity ->
+ !activeUsers.contains(b.userId)
+ }
+ }
+ }
+ return false
+ }
+
private fun cache(bubbles: List<BubbleEntity>) {
bubbles.groupBy { ShortcutKey(it.userId, it.packageName) }.forEach { (key, bubbles) ->
launcherApps.cacheShortcuts(key.pkg, bubbles.map { it.shortcutId },
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 3b83f1586d8c..6a2acf438302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -498,6 +498,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
dispatchVisibilityChanged(mDisplayId, isShowing);
}
}
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public InsetsSourceControl getImeSourceControl() {
+ return mImeSourceControl;
+ }
}
void removeImeSurface() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 0b8e631068fc..c94455d9151a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -107,6 +107,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private int mOrientation;
private int mRotation;
+ private final boolean mDimNonImeSide;
+
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
@@ -131,6 +133,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mRootBounds.set(configuration.windowConfiguration.getBounds());
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
resetDividerPosition();
+
+ mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide);
}
private int getDividerInsets(Resources resources, Display display) {
@@ -860,10 +864,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// Update target dim values
mLastDim1 = mDimValue1;
mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && mImeShown
- ? ADJUSTED_NONFOCUS_DIM : 0.0f;
+ && mDimNonImeSide ? ADJUSTED_NONFOCUS_DIM : 0.0f;
mLastDim2 = mDimValue2;
mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && mImeShown
- ? ADJUSTED_NONFOCUS_DIM : 0.0f;
+ && mDimNonImeSide ? ADJUSTED_NONFOCUS_DIM : 0.0f;
// Calculate target bounds offset for IME
mLastYOffset = mYOffsetForIme;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 1bc9e31b9e2e..b3799e2cf8d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -20,6 +20,7 @@ import android.animation.AnimationHandler;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
+import android.os.UserManager;
import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
@@ -43,6 +44,7 @@ import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
+import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -105,6 +107,7 @@ public class WMShellModule {
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
+ UserManager userManager,
LauncherApps launcherApps,
TaskStackListenerImpl taskStackListener,
UiEventLogger uiEventLogger,
@@ -114,13 +117,15 @@ public class WMShellModule {
DragAndDropController dragAndDropController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
return BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
- windowManagerShellWrapper, launcherApps, taskStackListener,
+ windowManagerShellWrapper, userManager, launcherApps, taskStackListener,
uiEventLogger, organizer, displayController, oneHandedOptional,
- dragAndDropController, mainExecutor, mainHandler, taskViewTransitions, syncQueue);
+ dragAndDropController, mainExecutor, mainHandler, bgExecutor,
+ taskViewTransitions, syncQueue);
}
//
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 42b101439664..49b6968f9417 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -26,8 +26,10 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
@@ -180,7 +182,8 @@ public class BackAnimationControllerTest {
// b/207481538, we check that the surface is not moved for now, we can re-enable this once
// we implement the animation
verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt());
- verify(mTransaction, never()).setPosition(animationTarget.leash, 100, 100);
+ verify(mTransaction, never()).setPosition(
+ animationTarget.leash, 100, 100);
verify(mTransaction, atLeastOnce()).apply();
}
@@ -251,6 +254,47 @@ public class BackAnimationControllerTest {
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
}
+ @Test
+ public void ignoresGesture_transitionInProgress() throws RemoteException {
+ mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
+ RemoteAnimationTarget animationTarget = createAnimationTarget();
+ createNavigationInfo(animationTarget, null, null,
+ BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
+
+ triggerBackGesture();
+ // Check that back invocation is dispatched.
+ verify(mIOnBackInvokedCallback).onBackInvoked();
+
+ reset(mIOnBackInvokedCallback);
+ // Verify that we prevent animation from restarting if another gestures happens before
+ // the previous transition is finished.
+ doMotionEvent(MotionEvent.ACTION_DOWN, 0);
+ verifyNoMoreInteractions(mIOnBackInvokedCallback);
+
+ // Verify that we start accepting gestures again once transition finishes.
+ mController.onBackToLauncherAnimationFinished();
+ doMotionEvent(MotionEvent.ACTION_DOWN, 0);
+ doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+ verify(mIOnBackInvokedCallback).onBackStarted();
+ }
+
+ @Test
+ public void acceptsGesture_transitionTimeout() throws RemoteException {
+ mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
+ RemoteAnimationTarget animationTarget = createAnimationTarget();
+ createNavigationInfo(animationTarget, null, null,
+ BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
+
+ triggerBackGesture();
+ reset(mIOnBackInvokedCallback);
+
+ // Simulate transition timeout.
+ mShellExecutor.flushAll();
+ doMotionEvent(MotionEvent.ACTION_DOWN, 0);
+ doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+ verify(mIOnBackInvokedCallback).onBackStarted();
+ }
+
private void doMotionEvent(int actionDown, int coordinate) {
mController.onMotionEvent(
MotionEvent.obtain(0, mEventTime, actionDown, coordinate, coordinate, 0),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index bde94d9d6c29..e6711aca19c1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -129,8 +129,8 @@ public class BubbleDataTest extends ShellTestCase {
mEntryA3 = createBubbleEntry(1, "a3", "package.a", null);
mEntryB1 = createBubbleEntry(1, "b1", "package.b", null);
mEntryB2 = createBubbleEntry(1, "b2", "package.b", null);
- mEntryB3 = createBubbleEntry(1, "b3", "package.b", null);
- mEntryC1 = createBubbleEntry(1, "c1", "package.c", null);
+ mEntryB3 = createBubbleEntry(11, "b3", "package.b", null);
+ mEntryC1 = createBubbleEntry(11, "c1", "package.c", null);
NotificationListenerService.Ranking ranking =
mock(NotificationListenerService.Ranking.class);
@@ -1058,6 +1058,37 @@ public class BubbleDataTest extends ShellTestCase {
assertBubbleListContains(mBubbleA2, mBubbleA1, mBubbleLocusId);
}
+ @Test
+ public void test_removeBubblesForUser() {
+ // A is user 1
+ sendUpdatedEntryAtTime(mEntryA1, 2000);
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ // B & C belong to user 11
+ sendUpdatedEntryAtTime(mEntryB3, 4000);
+ sendUpdatedEntryAtTime(mEntryC1, 5000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ verifyUpdateReceived();
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
+ assertBubbleListContains(mBubbleC1, mBubbleB3, mBubbleA2);
+
+ // Remove all the A bubbles
+ mBubbleData.removeBubblesForUser(1);
+ verifyUpdateReceived();
+
+ // Verify the update has the removals.
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.removedBubbles.get(0)).isEqualTo(
+ Pair.create(mBubbleA2, Bubbles.DISMISS_USER_REMOVED));
+ assertThat(update.removedBubbles.get(1)).isEqualTo(
+ Pair.create(mBubbleA1, Bubbles.DISMISS_USER_REMOVED));
+
+ // Verify no A bubbles in active or overflow.
+ assertBubbleListContains(mBubbleC1, mBubbleB3);
+ assertOverflowChangedTo(ImmutableList.of());
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index bfdf5208bbf0..77c70557fb70 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -23,14 +23,19 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import org.junit.Test
import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito
import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
+import org.mockito.Mockito.never
import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -41,17 +46,17 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
private val user11 = UserHandle.of(11)
// user, package, shortcut, notification key, height, res-height, title, taskId, locusId
- private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1",
- "0key-1", 120, 0, null, 1, null)
- private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob",
- "10key-2", 0, 16537428, "title", 2, null)
- private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2",
- "0key-3", 120, 0, null, INVALID_TASK_ID, null)
-
- private val bubble11 = BubbleEntity(11, "com.example.messenger",
- "shortcut-1", "01key-1", 120, 0, null, 3)
- private val bubble12 = BubbleEntity(11, "com.example.chat", "alice and bob",
- "11key-2", 0, 16537428, "title", INVALID_TASK_ID)
+ private val bubble1 = BubbleEntity(user0.identifier,
+ "com.example.messenger", "shortcut-1", "0key-1", 120, 0, null, 1, null)
+ private val bubble2 = BubbleEntity(user10_managed.identifier,
+ "com.example.chat", "alice and bob", "10key-2", 0, 16537428, "title", 2, null)
+ private val bubble3 = BubbleEntity(user0.identifier,
+ "com.example.messenger", "shortcut-2", "0key-3", 120, 0, null, INVALID_TASK_ID, null)
+
+ private val bubble11 = BubbleEntity(user11.identifier,
+ "com.example.messenger", "shortcut-1", "01key-1", 120, 0, null, 3)
+ private val bubble12 = BubbleEntity(user11.identifier,
+ "com.example.chat", "alice and bob", "11key-2", 0, 16537428, "title", INVALID_TASK_ID)
private val user0bubbles = listOf(bubble1, bubble2, bubble3)
private val user11bubbles = listOf(bubble11, bubble12)
@@ -151,6 +156,119 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
repository.addBubbles(user0.identifier, listOf(bubbleModified))
assertEquals(bubbleModified, repository.getEntities(user0.identifier).get(0))
}
+
+ @Test
+ fun testRemoveBubblesForUser() {
+ repository.addBubbles(user0.identifier, user0bubbles)
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+
+ val ret = repository.removeBubblesForUser(user0.identifier, -1)
+ assertThat(ret).isTrue() // bubbles were removed
+
+ assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
+ verify(launcherApps, never()).uncacheShortcuts(anyString(),
+ any(),
+ any(UserHandle::class.java), anyInt())
+ }
+
+ @Test
+ fun testRemoveBubblesForUser_parentUserRemoved() {
+ repository.addBubbles(user0.identifier, user0bubbles)
+ // bubble2 is the work profile bubble
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+
+ val ret = repository.removeBubblesForUser(user10_managed.identifier, user0.identifier)
+ assertThat(ret).isTrue() // bubbles were removed
+
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble3))
+ verify(launcherApps, never()).uncacheShortcuts(anyString(),
+ any(),
+ any(UserHandle::class.java), anyInt())
+ }
+
+ @Test
+ fun testRemoveBubblesForUser_withoutBubbles() {
+ repository.addBubbles(user0.identifier, user0bubbles)
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+
+ val ret = repository.removeBubblesForUser(user11.identifier, -1)
+ assertThat(ret).isFalse() // bubbles were NOT removed
+
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+ verify(launcherApps, never()).uncacheShortcuts(anyString(),
+ any(),
+ any(UserHandle::class.java), anyInt())
+ }
+
+ @Test
+ fun testSanitizeBubbles_noChanges() {
+ repository.addBubbles(user0.identifier, user0bubbles)
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+ repository.addBubbles(user11.identifier, user11bubbles)
+ assertThat(repository.getEntities(user11.identifier).toList())
+ .isEqualTo(listOf(bubble11, bubble12))
+
+ val ret = repository.sanitizeBubbles(listOf(user0.identifier,
+ user10_managed.identifier,
+ user11.identifier))
+ assertThat(ret).isFalse() // bubbles were NOT removed
+
+ verify(launcherApps, never()).uncacheShortcuts(anyString(),
+ any(),
+ any(UserHandle::class.java), anyInt())
+ }
+
+ @Test
+ fun testSanitizeBubbles_userRemoved() {
+ repository.addBubbles(user0.identifier, user0bubbles)
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+ repository.addBubbles(user11.identifier, user11bubbles)
+ assertThat(repository.getEntities(user11.identifier).toList())
+ .isEqualTo(listOf(bubble11, bubble12))
+
+ val ret = repository.sanitizeBubbles(listOf(user11.identifier))
+ assertThat(ret).isTrue() // bubbles were removed
+
+ assertThat(repository.getEntities(user0.identifier).toList()).isEmpty()
+ verify(launcherApps, never()).uncacheShortcuts(anyString(),
+ any(),
+ any(UserHandle::class.java), anyInt())
+
+ // User 11 bubbles should still be here
+ assertThat(repository.getEntities(user11.identifier).toList())
+ .isEqualTo(listOf(bubble11, bubble12))
+ }
+
+ @Test
+ fun testSanitizeBubbles_userParentRemoved() {
+ repository.addBubbles(user0.identifier, user0bubbles)
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble2, bubble3))
+
+ repository.addBubbles(user11.identifier, user11bubbles)
+ assertThat(repository.getEntities(user11.identifier).toList())
+ .isEqualTo(listOf(bubble11, bubble12))
+
+ val ret = repository.sanitizeBubbles(listOf(user0.identifier, user11.identifier))
+ assertThat(ret).isTrue() // bubbles were removed
+ // bubble2 is the work profile bubble and should be removed
+ assertThat(repository.getEntities(user0.identifier).toList())
+ .isEqualTo(listOf(bubble1, bubble3))
+ verify(launcherApps, never()).uncacheShortcuts(anyString(),
+ any(),
+ any(UserHandle::class.java), anyInt())
+
+ // User 11 bubbles should still be here
+ assertThat(repository.getEntities(user11.identifier).toList())
+ .isEqualTo(listOf(bubble11, bubble12))
+ }
}
private const val PKG_MESSENGER = "com.example.messenger"
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 86ae3995eeed..5a67eb9935dd 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -134,7 +134,7 @@ bool Properties::load() {
skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false);
SkAndroidFrameworkTraceUtil::setEnableTracing(
- base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, true));
+ base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false);
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
index fe74c62d4ec1..9626d8dac787 100644
--- a/libs/input/TEST_MAPPING
+++ b/libs/input/TEST_MAPPING
@@ -1,7 +1,7 @@
{
- "presubmit": [
- {
- "name": "libinputservice_test"
- }
- ]
+ "imports": [
+ {
+ "path": "frameworks/native/services/inputflinger"
+ }
+ ]
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index c8d2d1ee621f..5850a81b8295 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -1294,45 +1294,46 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg,
std::string defaultMsg = "Unknown Error";
/* translate OS errors to Java API CryptoException errorCodes (which are positive) */
+ jint jerr = 0;
switch (err) {
case ERROR_DRM_NO_LICENSE:
- err = gCryptoErrorCodes.cryptoErrorNoKey;
+ jerr = gCryptoErrorCodes.cryptoErrorNoKey;
defaultMsg = "Crypto key not available";
break;
case ERROR_DRM_LICENSE_EXPIRED:
- err = gCryptoErrorCodes.cryptoErrorKeyExpired;
+ jerr = gCryptoErrorCodes.cryptoErrorKeyExpired;
defaultMsg = "License expired";
break;
case ERROR_DRM_RESOURCE_BUSY:
- err = gCryptoErrorCodes.cryptoErrorResourceBusy;
+ jerr = gCryptoErrorCodes.cryptoErrorResourceBusy;
defaultMsg = "Resource busy or unavailable";
break;
case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
- err = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
+ jerr = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
defaultMsg = "Required output protections are not active";
break;
case ERROR_DRM_SESSION_NOT_OPENED:
- err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
+ jerr = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
defaultMsg = "Attempted to use a closed session";
break;
case ERROR_DRM_INSUFFICIENT_SECURITY:
- err = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+ jerr = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
defaultMsg = "Required security level is not met";
break;
case ERROR_DRM_CANNOT_HANDLE:
- err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
+ jerr = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
defaultMsg = "Operation not supported in this configuration";
break;
case ERROR_DRM_FRAME_TOO_LARGE:
- err = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+ jerr = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
defaultMsg = "Decrytped frame exceeds size of output buffer";
break;
case ERROR_DRM_SESSION_LOST_STATE:
- err = gCryptoErrorCodes.cryptoErrorLostState;
+ jerr = gCryptoErrorCodes.cryptoErrorLostState;
defaultMsg = "Session state was lost, open a new session and retry";
break;
default: /* Other negative DRM error codes go out best-effort. */
- err = MediaErrorToJavaError(err);
+ jerr = MediaErrorToJavaError(err);
defaultMsg = StrCryptoError(err);
break;
}
@@ -1344,7 +1345,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg,
jstring msgObj = env->NewStringUTF(msgStr.c_str());
jthrowable exception =
- (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
+ (jthrowable)env->NewObject(clazz.get(), constructID, jerr, msgObj);
env->Throw(exception);
}
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 3d6bf152f93f..a389bfc42725 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -20,7 +20,7 @@
<string name="app_label">Companion Device Manager</string>
<!-- Title of the device association confirmation dialog. -->
- <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
+ <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access your &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
<!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
@@ -31,10 +31,7 @@
<string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%2$s</xliff:g>&lt;/strong&gt;</string>
<!-- Description of the privileges the application will get if associated with the companion device of WATCH profile (type) [CHAR LIMIT=NONE] -->
- <string name="summary_watch" product="default"><xliff:g id="app_name" example="Wear">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.</string>
-
- <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile (type) [CHAR LIMIT=NONE] -->
- <string name="summary_watch" product="tablet"><xliff:g id="app_name" example="Wear">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.</string>
+ <string name="summary_watch">This app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string>
<!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 9e9ec04fbd93..a3aa0100870a 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -497,17 +497,19 @@ public class CompanionDeviceActivity extends FragmentActivity implements
}
final String deviceName = mSelectedDevice.getDisplayName();
- final Spanned title = getHtmlFromResources(
- this, R.string.confirmation_title, appLabel, deviceName);
+ final String profileName = getString(R.string.profile_name_watch);
+ final Spanned title;
final Spanned summary;
final Drawable profileIcon;
if (deviceProfile == null) {
+ title = getHtmlFromResources(this, R.string.confirmation_title, appLabel, deviceName);
summary = getHtmlFromResources(this, R.string.summary_generic);
profileIcon = getIcon(this, R.drawable.ic_device_other);
mSummary.setVisibility(View.GONE);
} else if (deviceProfile.equals(DEVICE_PROFILE_WATCH)) {
- summary = getHtmlFromResources(this, R.string.summary_watch, appLabel, deviceName);
+ title = getHtmlFromResources(this, R.string.confirmation_title, appLabel, profileName);
+ summary = getHtmlFromResources(this, R.string.summary_watch, deviceName, appLabel);
profileIcon = getIcon(this, R.drawable.ic_watch);
} else {
throw new RuntimeException("Unsupported profile " + deviceProfile);
@@ -535,7 +537,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mSummary.setVisibility(View.GONE);
} else if (deviceProfile.equals(DEVICE_PROFILE_WATCH)) {
profileName = getString(R.string.profile_name_watch);
- summary = getHtmlFromResources(this, R.string.summary_watch, appLabel);
+ summary = getHtmlFromResources(this, R.string.summary_watch, profileName, appLabel);
profileIcon = getIcon(this, R.drawable.ic_watch);
} else {
throw new RuntimeException("Unsupported profile " + deviceProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 6919cf237853..a9da2e0bfebe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -77,6 +77,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private final LocalBluetoothProfileManager mProfileManager;
private final Object mProfileLock = new Object();
BluetoothDevice mDevice;
+ private int mDeviceSide;
+ private int mDeviceMode;
private long mHiSyncId;
private int mGroupId;
// Need this since there is no method for getting RSSI
@@ -335,6 +337,22 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
connectDevice();
}
+ public int getDeviceSide() {
+ return mDeviceSide;
+ }
+
+ public void setDeviceSide(int side) {
+ mDeviceSide = side;
+ }
+
+ public int getDeviceMode() {
+ return mDeviceMode;
+ }
+
+ public void setDeviceMode(int mode) {
+ mDeviceMode = mode;
+ }
+
public long getHiSyncId() {
return mHiSyncId;
}
@@ -1000,7 +1018,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
== BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
EventLog.writeEvent(0x534e4554, "138529441", -1, "");
}
- mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
}
}
}
@@ -1111,7 +1128,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
stringRes = R.string.bluetooth_battery_level;
}
- // Set active string in following device connected situation.
+ // Set active string in following device connected situation, also show battery
+ // information if they have.
// 1. Hearing Aid device active.
// 2. Headset device active with in-calling state.
// 3. A2DP device active without in-calling state.
@@ -1130,6 +1148,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
stringRes = R.string.bluetooth_active_no_battery_level;
}
}
+
+ // Try to show left/right information if can not get it from battery for hearing
+ // aids specifically.
+ if (mIsActiveDeviceHearingAid
+ && stringRes == R.string.bluetooth_active_no_battery_level) {
+ final CachedBluetoothDevice subDevice = getSubDevice();
+ if (subDevice != null && subDevice.isConnected()) {
+ stringRes = R.string.bluetooth_hearing_aid_left_and_right_active;
+ } else {
+ if (mDeviceSide == HearingAidProfile.DeviceSide.SIDE_LEFT) {
+ stringRes = R.string.bluetooth_hearing_aid_left_active;
+ } else if (mDeviceSide == HearingAidProfile.DeviceSide.SIDE_RIGHT) {
+ stringRes = R.string.bluetooth_hearing_aid_right_active;
+ } else {
+ stringRes = R.string.bluetooth_active_no_battery_level;
+ }
+ }
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 6f2d4decf033..a491455da963 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -29,13 +29,48 @@ import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class HearingAidProfile implements LocalBluetoothProfile {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DeviceSide.SIDE_INVALID,
+ DeviceSide.SIDE_LEFT,
+ DeviceSide.SIDE_RIGHT
+ })
+
+ /** Side definition for hearing aids. See {@link BluetoothHearingAid}. */
+ public @interface DeviceSide {
+ int SIDE_INVALID = -1;
+ int SIDE_LEFT = 0;
+ int SIDE_RIGHT = 1;
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DeviceMode.MODE_INVALID,
+ DeviceMode.MODE_MONAURAL,
+ DeviceMode.MODE_BINAURAL
+ })
+
+ /** Mode definition for hearing aids. See {@link BluetoothHearingAid}. */
+ public @interface DeviceMode {
+ int MODE_INVALID = -1;
+ int MODE_MONAURAL = 0;
+ int MODE_BINAURAL = 1;
+ }
+
private static final String TAG = "HearingAidProfile";
private static boolean V = true;
@@ -212,6 +247,11 @@ public class HearingAidProfile implements LocalBluetoothProfile {
return isEnabled;
}
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote
+ */
public void setVolume(int volume) {
if (mService == null) {
return;
@@ -219,6 +259,12 @@ public class HearingAidProfile implements LocalBluetoothProfile {
mService.setVolume(volume);
}
+ /**
+ * Gets the HiSyncId (unique hearing aid device identifier) of the device.
+ *
+ * @param device Bluetooth device
+ * @return the HiSyncId of the device
+ */
public long getHiSyncId(BluetoothDevice device) {
if (mService == null || device == null) {
return BluetoothHearingAid.HI_SYNC_ID_INVALID;
@@ -226,6 +272,59 @@ public class HearingAidProfile implements LocalBluetoothProfile {
return mService.getHiSyncId(device);
}
+ /**
+ * Gets the side of the device.
+ *
+ * @param device Bluetooth device.
+ * @return side of the device. See {@link DeviceSide}.
+ */
+ @DeviceSide
+ public int getDeviceSide(@NonNull BluetoothDevice device) {
+ final int defaultValue = DeviceSide.SIDE_INVALID;
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to HearingAidService");
+ return defaultValue;
+ }
+
+ try {
+ Method method = mService.getClass().getDeclaredMethod("getDeviceSideInternal",
+ BluetoothDevice.class);
+ method.setAccessible(true);
+ return (int) method.invoke(mService, device);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ Log.e(TAG, "fail to get getDeviceSideInternal\n" + e.toString() + "\n"
+ + Log.getStackTraceString(new Throwable()));
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Gets the mode of the device.
+ *
+ * @param device Bluetooth device
+ * @return mode of the device. See {@link DeviceMode}.
+ */
+ @DeviceMode
+ public int getDeviceMode(@NonNull BluetoothDevice device) {
+ final int defaultValue = DeviceMode.MODE_INVALID;
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to HearingAidService");
+ return defaultValue;
+ }
+
+ try {
+ Method method = mService.getClass().getDeclaredMethod("getDeviceModeInternal",
+ BluetoothDevice.class);
+ method.setAccessible(true);
+ return (int) method.invoke(mService, device);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ Log.e(TAG, "fail to get getDeviceModeInternal\n" + e.toString() + "\n"
+ + Log.getStackTraceString(new Throwable()));
+
+ return defaultValue;
+ }
+ }
+
public String toString() {
return NAME;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0619986d21f6..58944f653869 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -340,6 +340,11 @@ public class LocalBluetoothProfileManager {
if (getHearingAidProfile() != null &&
mProfile instanceof HearingAidProfile &&
(newState == BluetoothProfile.STATE_CONNECTED)) {
+ final int side = getHearingAidProfile().getDeviceSide(cachedDevice.getDevice());
+ final int mode = getHearingAidProfile().getDeviceMode(cachedDevice.getDevice());
+ cachedDevice.setDeviceSide(side);
+ cachedDevice.setDeviceMode(mode);
+
// Check if the HiSyncID has being initialized
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 55d125e01388..be2a55ede942 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -78,6 +78,7 @@ public class CachedBluetoothDeviceTest {
@Mock
private BluetoothDevice mSubDevice;
private CachedBluetoothDevice mCachedDevice;
+ private CachedBluetoothDevice mSubCachedDevice;
private AudioManager mAudioManager;
private Context mContext;
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -95,7 +96,9 @@ public class CachedBluetoothDeviceTest {
when(mPanProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
+ mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice));
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
+ doAnswer((invocation) -> mBatteryLevel).when(mSubCachedDevice).getBatteryLevel();
}
@Test
@@ -351,8 +354,9 @@ public class CachedBluetoothDeviceTest {
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Set device as Active for Hearing Aid and test connection state summary
+ mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, left only");
// Set Hearing Aid profile to be disconnected and test connection state summary
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
@@ -390,17 +394,36 @@ public class CachedBluetoothDeviceTest {
}
@Test
- public void getConnectionSummary_testHearingAidInCall_returnActive() {
+ public void getConnectionSummary_testHearingAidRightEarInCall_returnActiveRightEar() {
// Arrange:
- // 1. Profile: {HEARING_AID, Connected, Active}
+ // 1. Profile: {HEARING_AID, Connected, Active, Right ear}
// 2. Audio Manager: In Call
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_RIGHT);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "Active" result without Battery Level.
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, right only");
+ }
+
+ @Test
+ public void getConnectionSummary_testHearingAidBothEarInCall_returnActiveBothEar() {
+ // Arrange:
+ // 1. Profile: {HEARING_AID, Connected, Active, Both ear}
+ // 2. Audio Manager: In Call
+ mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_RIGHT);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mSubCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT);
+ updateSubDeviceProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setSubDevice(mSubCachedDevice);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+ // Act & Assert:
+ // Get "Active" result without Battery Level.
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, left and right");
}
@Test
@@ -925,39 +948,41 @@ public class CachedBluetoothDeviceTest {
mCachedDevice.onProfileStateChanged(profile, status);
}
+ private void updateSubDeviceProfileStatus(LocalBluetoothProfile profile, int status) {
+ doReturn(status).when(profile).getConnectionStatus(mSubDevice);
+ mSubCachedDevice.onProfileStateChanged(profile, status);
+ }
+
@Test
public void getSubDevice_setSubDevice() {
- CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
- mSubDevice);
- mCachedDevice.setSubDevice(subCachedDevice);
+ mCachedDevice.setSubDevice(mSubCachedDevice);
- assertThat(mCachedDevice.getSubDevice()).isEqualTo(subCachedDevice);
+ assertThat(mCachedDevice.getSubDevice()).isEqualTo(mSubCachedDevice);
}
@Test
public void switchSubDeviceContent() {
- CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
- mSubDevice);
+
mCachedDevice.mRssi = RSSI_1;
mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
- subCachedDevice.mRssi = RSSI_2;
- subCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
- mCachedDevice.setSubDevice(subCachedDevice);
+ mSubCachedDevice.mRssi = RSSI_2;
+ mSubCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
+ mCachedDevice.setSubDevice(mSubCachedDevice);
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_1);
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
assertThat(mCachedDevice.mDevice).isEqualTo(mDevice);
- assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_2);
- assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
- assertThat(subCachedDevice.mDevice).isEqualTo(mSubDevice);
+ assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_2);
+ assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
+ assertThat(mSubCachedDevice.mDevice).isEqualTo(mSubDevice);
mCachedDevice.switchSubDeviceContent();
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
- assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_1);
- assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
- assertThat(subCachedDevice.mDevice).isEqualTo(mDevice);
+ assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
+ assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
+ assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
}
@Test
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index 3d2f570bde87..894bb5fc8577 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -181,7 +181,7 @@ public interface VolumeDialogController {
public interface Callbacks {
int VERSION = 1;
- void onShowRequested(int reason);
+ void onShowRequested(int reason, boolean keyguardLocked, int lockTaskModeState);
void onDismissRequested(int reason);
void onStateChanged(State state);
void onLayoutDirectionChanged(int layoutDirection);
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2c3d947ba9e2..7010a289df68 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -804,6 +804,14 @@
<!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
<string name="keyguard_face_successful_unlock_press">Unlocked by face. Press the unlock icon to open.</string>
+ <!-- Messages shown when users press outside of udfps region during -->
+ <string-array name="udfps_accessibility_touch_hints">
+ <item>Move left</item>
+ <item>Move down</item>
+ <item>Move right</item>
+ <item>Move up</item>
+ </string-array>
+
<!-- Message shown when face authentication fails and the pin pad is visible. [CHAR LIMIT=60] -->
<string name="keyguard_retry">Swipe up to try again</string>
@@ -2271,6 +2279,8 @@
<string name="media_output_dialog_unknown_launch_app_name">Unknown app</string>
<!-- Button text for stopping casting [CHAR LIMIT=60] -->
<string name="media_output_dialog_button_stop_casting">Stop casting</string>
+ <!-- Accessibility text describing purpose of media output dialog. [CHAR LIMIT=NONE] -->
+ <string name="media_output_dialog_accessibility_title">Available devices for audio output.</string>
<!-- Media Output Broadcast Dialog -->
<!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 5c9f5dba1f3f..2cc5ccdc3fa1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -32,6 +32,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
@@ -44,6 +45,7 @@ import androidx.annotation.Nullable;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
+import com.android.systemui.DejankUtils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
/**
@@ -194,9 +196,17 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
@Override
public void onAnimationEnd(Animator animation) {
- controller.finish(false);
- runOnFinishImeAnimationRunnable();
- finishRunnable.run();
+ // Run this in the next frame since it results in a slow binder call
+ // to InputMethodManager#hideSoftInput()
+ DejankUtils.postAfterTraversal(() -> {
+ Trace.beginSection("KeyguardPasswordView#onAnimationEnd");
+ // // TODO(b/230620476): Make hideSoftInput oneway
+ // controller.finish() eventually calls hideSoftInput
+ controller.finish(false);
+ runOnFinishImeAnimationRunnable();
+ finishRunnable.run();
+ Trace.endSection();
+ });
}
});
anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 5cfbdb0caf2d..742c65c2f854 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -192,4 +192,9 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
* Called on touches outside of the view if listenForTouchesOutsideView returns true
*/
open fun onTouchOutsideView() {}
+
+ /**
+ * Called when a view should announce an accessibility event.
+ */
+ open fun doAnnounceForAccessibility(str: String) {}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9febaa01eed7..88e467f6c2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -381,6 +381,27 @@ public class UdfpsController implements DozeReceiver {
&& mOverlayParams.getSensorBounds().contains((int) x, (int) y);
}
+ private Point getTouchInNativeCoordinates(@NonNull MotionEvent event, int idx) {
+ Point portraitTouch = new Point(
+ (int) event.getRawX(idx),
+ (int) event.getRawY(idx)
+ );
+ final int rot = mOverlayParams.getRotation();
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ RotationUtils.rotatePoint(portraitTouch,
+ RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
+ mOverlayParams.getLogicalDisplayWidth(),
+ mOverlayParams.getLogicalDisplayHeight()
+ );
+ }
+
+ // Scale the coordinates to native resolution.
+ final float scale = mOverlayParams.getScaleFactor();
+ portraitTouch.x = (int) (portraitTouch.x / scale);
+ portraitTouch.y = (int) (portraitTouch.y / scale);
+ return portraitTouch;
+ }
+
@VisibleForTesting
boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (mOverlay == null) {
@@ -434,6 +455,7 @@ public class UdfpsController implements DozeReceiver {
mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
mAttemptedToDismissKeyguard = true;
}
+
Trace.endSection();
break;
@@ -457,6 +479,8 @@ public class UdfpsController implements DozeReceiver {
mAttemptedToDismissKeyguard = true;
break;
}
+ // Map the touch to portrait mode if the device is in landscape mode.
+ final Point scaledTouch = getTouchInNativeCoordinates(event, idx);
if (actionMoveWithinSensorArea) {
if (mVelocityTracker == null) {
// touches could be injected, so the velocity tracker may not have
@@ -477,28 +501,13 @@ public class UdfpsController implements DozeReceiver {
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
if (!isIlluminationRequested && !mAcquiredReceived
&& !exceedsVelocityThreshold) {
- // Map the touch to portrait mode if the device is in landscape mode.
- Point portraitTouch = new Point(
- (int) event.getRawX(idx),
- (int) event.getRawY(idx)
- );
- final int rot = mOverlayParams.getRotation();
- if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- RotationUtils.rotatePoint(portraitTouch,
- RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
- mOverlayParams.getLogicalDisplayWidth(),
- mOverlayParams.getLogicalDisplayHeight()
- );
- }
- // Scale the coordinates to native resolution.
final float scale = mOverlayParams.getScaleFactor();
- int scaledX = (int) (portraitTouch.x / scale);
- int scaledY = (int) (portraitTouch.y / scale);
float scaledMinor = minor / scale;
float scaledMajor = major / scale;
- onFingerDown(requestId, scaledX, scaledY, scaledMinor, scaledMajor);
+ onFingerDown(requestId, scaledTouch.x, scaledTouch.y, scaledMinor,
+ scaledMajor);
Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = mSystemClock.elapsedRealtime();
mPowerManager.userActivity(mSystemClock.uptimeMillis(),
@@ -511,6 +520,24 @@ public class UdfpsController implements DozeReceiver {
} else {
Log.v(TAG, "onTouch | finger outside");
onFingerUp(requestId, udfpsView);
+ // Maybe announce for accessibility.
+ mFgExecutor.execute(() -> {
+ if (mOverlay == null) {
+ Log.e(TAG, "touch outside sensor area received"
+ + "but serverRequest is null");
+ return;
+ }
+ // Scale the coordinates to native resolution.
+ final float scale = mOverlayParams.getScaleFactor();
+ final float scaledSensorX =
+ mOverlayParams.getSensorBounds().centerX() / scale;
+ final float scaledSensorY =
+ mOverlayParams.getSensorBounds().centerY() / scale;
+
+ mOverlay.onTouchOutsideOfSensorArea(
+ scaledTouch.x, scaledTouch.y, scaledSensorX, scaledSensorY,
+ mOverlayParams.getRotation());
+ });
}
}
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index faa93a5a2ad3..37db2bd51238 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -130,6 +130,8 @@ class UdfpsControllerOverlay(
val animationViewController: UdfpsAnimationViewController<*>?
get() = overlayView?.animationViewController
+ private var touchExplorationEnabled = false
+
/** Show the overlay or return false and do nothing if it is already showing. */
@SuppressLint("ClickableViewAccessibility")
fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
@@ -154,14 +156,16 @@ class UdfpsControllerOverlay(
}
windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
-
+ touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
overlayTouchListener = TouchExplorationStateChangeListener {
if (accessibilityManager.isTouchExplorationEnabled) {
setOnHoverListener { v, event -> onTouch(v, event, true) }
setOnTouchListener(null)
+ touchExplorationEnabled = true
} else {
setOnHoverListener(null)
setOnTouchListener { v, event -> onTouch(v, event, true) }
+ touchExplorationEnabled = false
}
}
accessibilityManager.addTouchExplorationStateChangeListener(
@@ -179,7 +183,7 @@ class UdfpsControllerOverlay(
return false
}
- private fun inflateUdfpsAnimation(
+ fun inflateUdfpsAnimation(
view: UdfpsView,
controller: UdfpsController
): UdfpsAnimationViewController<*>? {
@@ -278,6 +282,88 @@ class UdfpsControllerOverlay(
enrollHelper?.onEnrollmentHelp()
}
+ /**
+ * This function computes the angle of touch relative to the sensor and maps
+ * the angle to a list of help messages which are announced if accessibility is enabled.
+ *
+ */
+ fun onTouchOutsideOfSensorArea(
+ touchX: Float,
+ touchY: Float,
+ sensorX: Float,
+ sensorY: Float,
+ rotation: Int
+ ) {
+
+ if (!touchExplorationEnabled) {
+ return
+ }
+ val touchHints =
+ context.resources.getStringArray(R.array.udfps_accessibility_touch_hints)
+ if (touchHints.size != 4) {
+ Log.e(TAG, "expected exactly 4 touch hints, got $touchHints.size?")
+ return
+ }
+ val theStr = onTouchOutsideOfSensorAreaImpl(touchX, touchY, sensorX, sensorY, rotation)
+ Log.v(TAG, "Announcing touch outside : " + theStr)
+ animationViewController?.doAnnounceForAccessibility(theStr)
+ }
+
+ /**
+ * This function computes the angle of touch relative to the sensor and maps
+ * the angle to a list of help messages which are announced if accessibility is enabled.
+ *
+ * There are 4 quadrants of the circle (90 degree arcs)
+ *
+ * [315, 360] && [0, 45) -> touchHints[0] = "Move Fingerprint to the left"
+ * [45, 135) -> touchHints[1] = "Move Fingerprint down"
+ * And so on.
+ */
+ fun onTouchOutsideOfSensorAreaImpl(
+ touchX: Float,
+ touchY: Float,
+ sensorX: Float,
+ sensorY: Float,
+ rotation: Int
+ ): String {
+ val touchHints =
+ context.resources.getStringArray(R.array.udfps_accessibility_touch_hints)
+
+ val xRelativeToSensor = touchX - sensorX
+ // Touch coordinates are with respect to the upper left corner, so reverse
+ // this calculation
+ val yRelativeToSensor = sensorY - touchY
+
+ var angleInRad =
+ Math.atan2(yRelativeToSensor.toDouble(), xRelativeToSensor.toDouble())
+ // If the radians are negative, that means we are counting clockwise.
+ // So we need to add 360 degrees
+ if (angleInRad < 0.0) {
+ angleInRad += 2.0 * Math.PI
+ }
+ // rad to deg conversion
+ val degrees = Math.toDegrees(angleInRad)
+
+ val degreesPerBucket = 360.0 / touchHints.size
+ val halfBucketDegrees = degreesPerBucket / 2.0
+ // The mapping should be as follows
+ // [315, 360] && [0, 45] -> 0
+ // [45, 135] -> 1
+ var index = (((degrees + halfBucketDegrees) % 360) / degreesPerBucket).toInt()
+ index %= touchHints.size
+
+ // A rotation of 90 degrees corresponds to increasing the index by 1.
+ if (rotation == Surface.ROTATION_90) {
+ index = (index + 1) % touchHints.size
+ }
+
+ if (rotation == Surface.ROTATION_270) {
+ index = (index + 3) % touchHints.size
+ }
+
+ return touchHints[index]
+ }
+
/** Cancel this request. */
fun cancel() {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 2ed60e555c58..0b7bddee41fa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -99,4 +99,10 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
public int getPaddingY() {
return mEnrollProgressBarRadius;
}
+
+ @Override
+ public void doAnnounceForAccessibility(String str) {
+ mView.announceForAccessibility(str);
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 25effec42f1a..db446c39767a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -19,13 +19,14 @@ package com.android.systemui.log
import android.os.Trace
import android.util.Log
import com.android.systemui.log.dagger.LogModule
+import com.android.systemui.util.collection.RingBuffer
import java.io.PrintWriter
import java.text.SimpleDateFormat
-import java.util.ArrayDeque
import java.util.Locale
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import kotlin.concurrent.thread
+import kotlin.math.max
/**
* A simple ring buffer of recyclable log messages
@@ -63,29 +64,22 @@ import kotlin.concurrent.thread
*
* Buffers are provided by [LogModule]. Instances should be created using a [LogBufferFactory].
*
- * @param name The name of this buffer
- * @param maxLogs The maximum number of messages to keep in memory at any one time, including the
- * unused pool. Must be >= [poolSize].
- * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to
- * sequential calls to [document] that aren't immediately followed by a matching call to [push].
+ * @param name The name of this buffer, printed when the buffer is dumped and in some other
+ * situations.
+ * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
+ * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
+ * the maximum, it behaves like a ring buffer.
*/
class LogBuffer @JvmOverloads constructor(
private val name: String,
- private val maxLogs: Int,
- private val poolSize: Int,
+ private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true
) {
- init {
- if (maxLogs < poolSize) {
- throw IllegalArgumentException("maxLogs must be greater than or equal to poolSize, " +
- "but maxLogs=$maxLogs < $poolSize=poolSize")
- }
- }
+ private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
- private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque()
- private val echoMessageQueue: BlockingQueue<LogMessageImpl>? =
- if (logcatEchoTracker.logInBackgroundThread) ArrayBlockingQueue(poolSize) else null
+ private val echoMessageQueue: BlockingQueue<LogMessage>? =
+ if (logcatEchoTracker.logInBackgroundThread) ArrayBlockingQueue(10) else null
init {
if (logcatEchoTracker.logInBackgroundThread && echoMessageQueue != null) {
@@ -104,6 +98,9 @@ class LogBuffer @JvmOverloads constructor(
var frozen = false
private set
+ private val mutable
+ get() = !frozen && maxSize > 0
+
/**
* Logs a message to the log buffer
*
@@ -138,34 +135,19 @@ class LogBuffer @JvmOverloads constructor(
initializer: LogMessage.() -> Unit,
noinline printer: LogMessage.() -> String
) {
- if (!frozen) {
- val message = obtain(tag, level, printer)
- initializer(message)
- push(message)
- }
- }
-
- /**
- * Same as [log], but doesn't push the message to the buffer. Useful if you need to supply a
- * "reason" for doing something (the thing you supply the reason to will presumably call [push]
- * on that message at some point).
- */
- inline fun document(
- tag: String,
- level: LogLevel,
- initializer: LogMessage.() -> Unit,
- noinline printer: LogMessage.() -> String
- ): LogMessage {
val message = obtain(tag, level, printer)
initializer(message)
- return message
+ commit(message)
}
/**
- * Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been
- * exhausted, creates a new instance.
+ * You should call [log] instead of this method.
*
- * In general, you should call [log] or [document] instead of this method.
+ * Obtains the next [LogMessage] from the ring buffer. If the buffer is not yet at max size,
+ * grows the buffer by one.
+ *
+ * After calling [obtain], the message will now be at the end of the buffer. The caller must
+ * store any relevant data on the message and then call [commit].
*/
@Synchronized
fun obtain(
@@ -173,28 +155,26 @@ class LogBuffer @JvmOverloads constructor(
level: LogLevel,
printer: (LogMessage) -> String
): LogMessageImpl {
- val message = when {
- frozen -> LogMessageImpl.create()
- buffer.size > maxLogs - poolSize -> buffer.removeFirst()
- else -> LogMessageImpl.create()
+ if (!mutable) {
+ return FROZEN_MESSAGE
}
+ val message = buffer.advance()
message.reset(tag, level, System.currentTimeMillis(), printer)
return message
}
/**
- * Pushes a message into buffer, possibly evicting an older message if the buffer is full.
+ * You should call [log] instead of this method.
+ *
+ * After acquiring a message via [obtain], call this method to signal to the buffer that you
+ * have finished filling in its data fields. The message will be echoed to logcat if
+ * necessary.
*/
@Synchronized
- fun push(message: LogMessage) {
- if (frozen) {
+ fun commit(message: LogMessage) {
+ if (!mutable) {
return
}
- if (buffer.size == maxLogs) {
- Log.e(TAG, "LogBuffer $name has exceeded its pool size")
- buffer.removeFirst()
- }
- buffer.add(message as LogMessageImpl)
// Log in the background thread only if echoMessageQueue exists and has capacity (checking
// capacity avoids the possibility of blocking this thread)
if (echoMessageQueue != null && echoMessageQueue.remainingCapacity() > 0) {
@@ -210,7 +190,7 @@ class LogBuffer @JvmOverloads constructor(
}
/** Sends message to echo after determining whether to use Logcat and/or systrace. */
- private fun echoToDesiredEndpoints(message: LogMessageImpl) {
+ private fun echoToDesiredEndpoints(message: LogMessage) {
val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) ||
logcatEchoTracker.isTagLoggable(message.tag, message.level)
echo(message, toLogcat = includeInLogcat, toSystrace = systrace)
@@ -219,19 +199,17 @@ class LogBuffer @JvmOverloads constructor(
/** Converts the entire buffer to a newline-delimited string */
@Synchronized
fun dump(pw: PrintWriter, tailLength: Int) {
- val start = if (tailLength <= 0) { 0 } else { buffer.size - tailLength }
+ val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
- for ((i, message) in buffer.withIndex()) {
- if (i >= start) {
- dumpMessage(message, pw)
- }
+ for (i in iterationStart until buffer.size) {
+ dumpMessage(buffer[i], pw)
}
}
/**
- * "Freezes" the contents of the buffer, making them immutable until [unfreeze] is called.
- * Calls to [log], [document], [obtain], and [push] will not affect the buffer and will return
- * dummy values if necessary.
+ * "Freezes" the contents of the buffer, making it immutable until [unfreeze] is called.
+ * Calls to [log], [obtain], and [commit] will not affect the buffer and will return dummy
+ * values if necessary.
*/
@Synchronized
fun freeze() {
@@ -293,3 +271,4 @@ class LogBuffer @JvmOverloads constructor(
private const val TAG = "LogBuffer"
private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+private val FROZEN_MESSAGE = LogMessageImpl.create() \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index cbfca25e0c60..5651399cb891 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -27,22 +27,21 @@ class LogBufferFactory @Inject constructor(
private val logcatEchoTracker: LogcatEchoTracker
) {
/* limit the size of maxPoolSize for low ram (Go) devices */
- private fun poolLimit(maxPoolSize_requested: Int): Int {
- if (ActivityManager.isLowRamDeviceStatic()) {
- return minOf(maxPoolSize_requested, 20) /* low ram max log size*/
+ private fun adjustMaxSize(requestedMaxSize: Int): Int {
+ return if (ActivityManager.isLowRamDeviceStatic()) {
+ minOf(requestedMaxSize, 20) /* low ram max log size*/
} else {
- return maxPoolSize_requested
+ requestedMaxSize
}
}
@JvmOverloads
fun create(
name: String,
- maxPoolSize: Int,
- flexSize: Int = 10,
+ maxSize: Int,
systrace: Boolean = true
): LogBuffer {
- val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker, systrace)
+ val buffer = LogBuffer(name, adjustMaxSize(maxSize), logcatEchoTracker, systrace)
dumpManager.registerBuffer(name, buffer)
return buffer
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
index 91734cc361b0..40b0cdc173d8 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
@@ -60,7 +60,7 @@ class LogcatEchoTrackerDebug private constructor(
Settings.Global.getUriFor(BUFFER_PATH),
true,
object : ContentObserver(Handler(mainLooper)) {
- override fun onChange(selfChange: Boolean, uri: Uri) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
cachedBufferLevels.clear()
}
@@ -70,7 +70,7 @@ class LogcatEchoTrackerDebug private constructor(
Settings.Global.getUriFor(TAG_PATH),
true,
object : ContentObserver(Handler(mainLooper)) {
- override fun onChange(selfChange: Boolean, uri: Uri) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
super.onChange(selfChange, uri)
cachedTagLevels.clear()
}
@@ -110,7 +110,7 @@ class LogcatEchoTrackerDebug private constructor(
}
private fun parseProp(propValue: String?): LogLevel {
- return when (propValue?.toLowerCase()) {
+ return when (propValue?.lowercase()) {
"verbose" -> LogLevel.VERBOSE
"v" -> LogLevel.VERBOSE
"debug" -> LogLevel.DEBUG
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 1e7a292dadba..eff025f771a5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -49,8 +49,7 @@ public class LogModule {
@SysUISingleton
@NotificationLog
public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifLog", 1000 /* maxPoolSize */,
- 10 /* flexSize */, false /* systrace */);
+ return factory.create("NotifLog", 1000 /* maxSize */, false /* systrace */);
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -74,8 +73,7 @@ public class LogModule {
@SysUISingleton
@NotificationSectionLog
public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifSectionLog", 1000 /* maxPoolSize */,
- 10 /* flexSize */, false /* systrace */);
+ return factory.create("NotifSectionLog", 1000 /* maxSize */, false /* systrace */);
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -91,8 +89,7 @@ public class LogModule {
@SysUISingleton
@QSLog
public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) {
- return factory.create("QSLog", 500 /* maxPoolSize */,
- 10 /* flexSize */, false /* systrace */);
+ return factory.create("QSLog", 500 /* maxSize */, false /* systrace */);
}
/** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
@@ -100,8 +97,8 @@ public class LogModule {
@SysUISingleton
@BroadcastDispatcherLog
public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) {
- return factory.create("BroadcastDispatcherLog", 500 /* maxPoolSize */,
- 10 /* flexSize */, false /* systrace */);
+ return factory.create("BroadcastDispatcherLog", 500 /* maxSize */,
+ false /* systrace */);
}
/** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
@@ -139,8 +136,8 @@ public class LogModule {
@SysUISingleton
@QSFragmentDisableLog
public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
- return factory.create("QSFragmentDisableFlagsLog", 10 /* maxPoolSize */,
- 10 /* flexSize */, false /* systrace */);
+ return factory.create("QSFragmentDisableFlagsLog", 10 /* maxSize */,
+ false /* systrace */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 8a104c42068e..0f8687183e94 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -32,6 +32,7 @@ import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.animation.requiresRemeasuring
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.traceSection
import java.io.PrintWriter
import java.util.TreeMap
import javax.inject.Inject
@@ -425,7 +426,7 @@ class MediaCarouselController @Inject constructor(
oldKey: String?,
data: MediaData,
isSsReactivated: Boolean
- ): Boolean {
+ ): Boolean = traceSection("MediaCarouselController#addOrUpdatePlayer") {
MediaPlayerData.moveIfExists(oldKey, key)
val existingPlayer = MediaPlayerData.getMediaPlayer(key)
val curVisibleMediaKey = MediaPlayerData.playerKeys()
@@ -471,7 +472,7 @@ class MediaCarouselController @Inject constructor(
key: String,
data: SmartspaceMediaData,
shouldPrioritize: Boolean
- ) {
+ ) = traceSection("MediaCarouselController#addSmartspaceMediaRecommendations") {
if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
if (MediaPlayerData.getMediaPlayer(key) != null) {
Log.w(TAG, "Skip adding smartspace target in carousel")
@@ -698,7 +699,7 @@ class MediaCarouselController @Inject constructor(
animate: Boolean,
duration: Long = 200,
startDelay: Long = 0
- ) {
+ ) = traceSection("MediaCarouselController#onDesiredLocationChanged") {
desiredHostState?.let {
if (this.desiredLocation != desiredLocation) {
// Only log an event when location changes
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 6ef25046d328..972e93b886b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -28,7 +28,6 @@ import android.app.WallpaperColors;
import android.app.smartspace.SmartspaceAction;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
@@ -45,6 +44,7 @@ import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Process;
+import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -397,6 +397,7 @@ public class MediaControlPanel {
if (mMediaViewHolder == null) {
return;
}
+ Trace.beginSection("MediaControlPanel#bindPlayer<" + key + ">");
mKey = key;
mMediaData = data;
MediaSession.Token token = data.getToken();
@@ -455,7 +456,7 @@ public class MediaControlPanel {
bindActionButtons(data);
boolean isSongUpdated = bindSongMetadata(data);
- bindArtworkAndColors(data, isSongUpdated);
+ bindArtworkAndColors(data, key, isSongUpdated);
// TODO: We don't need to refresh this state constantly, only if the state actually changed
// to something which might impact the measurement
@@ -463,6 +464,7 @@ public class MediaControlPanel {
if (!mMetadataAnimationHandler.isRunning()) {
mMediaViewController.refreshState();
}
+ Trace.endSection();
}
private void bindOutputSwitcherChip(MediaData data) {
@@ -607,7 +609,11 @@ public class MediaControlPanel {
mRecommendationViewHolder.getRecommendations().setContentDescription(contentDescription);
}
- private void bindArtworkAndColors(MediaData data, boolean updateBackground) {
+ private void bindArtworkAndColors(MediaData data, String key, boolean updateBackground) {
+ final int traceCookie = data.hashCode();
+ final String traceName = "MediaControlPanel#bindArtworkAndColors<" + key + ">";
+ Trace.beginAsyncSection(traceName, traceCookie);
+
final int reqId = mArtworkNextBindRequestId++;
if (updateBackground) {
mIsArtworkBound = false;
@@ -648,7 +654,10 @@ public class MediaControlPanel {
final ColorScheme colorScheme = mutableColorScheme;
mMainExecutor.execute(() -> {
// Cancel the request if a later one arrived first
- if (reqId < mArtworkBoundId) return;
+ if (reqId < mArtworkBoundId) {
+ Trace.endAsyncSection(traceName, traceCookie);
+ return;
+ }
mArtworkBoundId = reqId;
// Bind the album view to the artwork or a transition drawable
@@ -698,6 +707,7 @@ public class MediaControlPanel {
appIconView.setImageResource(R.drawable.ic_music_note);
}
}
+ Trace.endAsyncSection(traceName, traceCookie);
});
});
}
@@ -990,6 +1000,9 @@ public class MediaControlPanel {
return;
}
+ Trace.beginSection(
+ "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+
mRecommendationData = data;
mSmartspaceId = SmallHash.hash(data.getTargetId());
mPackageName = data.getPackageName();
@@ -1003,12 +1016,14 @@ public class MediaControlPanel {
mUid = applicationInfo.uid;
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Fail to get media recommendation's app info", e);
+ Trace.endSection();
return;
}
CharSequence appName = data.getAppName(mContext);
if (appName == null) {
Log.w(TAG, "Fail to get media recommendation's app name");
+ Trace.endSection();
return;
}
@@ -1123,6 +1138,7 @@ public class MediaControlPanel {
if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) {
mMediaViewController.refreshState();
}
+ Trace.endSection();
}
private void fetchAndUpdateRecommendationColors(Drawable appIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 0a4455658b6b..8bf2c6e92105 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -65,6 +65,7 @@ import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.traceSection
import java.io.IOException
import java.io.PrintWriter
import java.util.concurrent.Executor
@@ -1031,7 +1032,11 @@ class MediaDataManager(
)
}
- fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
+ fun onMediaDataLoaded(
+ key: String,
+ oldKey: String?,
+ data: MediaData
+ ) = traceSection("MediaDataManager#onMediaDataLoaded") {
Assert.isMainThread()
if (mediaEntries.containsKey(key)) {
// Otherwise this was removed already
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 30771c728e86..ed5c1933af25 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.animation.UniqueObjectHostView
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -582,7 +583,7 @@ class MediaHierarchyManager @Inject constructor(
private fun updateDesiredLocation(
forceNoAnimation: Boolean = false,
forceStateUpdate: Boolean = false
- ) {
+ ) = traceSection("MediaHierarchyManager#updateDesiredLocation") {
val desiredLocation = calculateLocation()
if (desiredLocation != this.desiredLocation || forceStateUpdate) {
if (this.desiredLocation >= 0 && desiredLocation != this.desiredLocation) {
@@ -616,7 +617,10 @@ class MediaHierarchyManager @Inject constructor(
}
}
- private fun performTransitionToNewLocation(isNewView: Boolean, animate: Boolean) {
+ private fun performTransitionToNewLocation(
+ isNewView: Boolean,
+ animate: Boolean
+ ) = traceSection("MediaHierarchyManager#performTransitionToNewLocation") {
if (previousLocation < 0 || isNewView) {
cancelAnimationAndApplyDesiredState()
return
@@ -899,7 +903,7 @@ class MediaHierarchyManager @Inject constructor(
alpha: Float,
immediately: Boolean = false,
clipBounds: Rect = EMPTY_RECT
- ) {
+ ) = traceSection("MediaHierarchyManager#applyState") {
currentBounds.set(bounds)
currentClipping = clipBounds
carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
@@ -922,7 +926,9 @@ class MediaHierarchyManager @Inject constructor(
}
}
- private fun updateHostAttachment() {
+ private fun updateHostAttachment() = traceSection(
+ "MediaHierarchyManager#updateHostAttachment"
+ ) {
var newLocation = resolveLocationForFading()
var canUseOverlay = !isCurrentlyFading()
if (isCrossFadeAnimatorRunning) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
index ba7c1679b174..aea2934c46fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHostStatesManager.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.animation.MeasurementOutput
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -46,7 +47,10 @@ class MediaHostStatesManager @Inject constructor() {
* Notify that a media state for a given location has changed. Should only be called from
* Media hosts themselves.
*/
- fun updateHostState(@MediaLocation location: Int, hostState: MediaHostState) {
+ fun updateHostState(
+ @MediaLocation location: Int,
+ hostState: MediaHostState
+ ) = traceSection("MediaHostStatesManager#updateHostState") {
val currentState = mediaHostStates.get(location)
if (!hostState.equals(currentState)) {
val newState = hostState.copy()
@@ -71,7 +75,7 @@ class MediaHostStatesManager @Inject constructor() {
fun updateCarouselDimensions(
@MediaLocation location: Int,
hostState: MediaHostState
- ): MeasurementOutput {
+ ): MeasurementOutput = traceSection("MediaHostStatesManager#updateCarouselDimensions") {
val result = MeasurementOutput(0, 0)
for (controller in controllers) {
val measurement = controller.getMeasurementsForState(hostState)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 7eccb3b91bb5..ae62355e6768 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -26,6 +26,7 @@ import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionLayoutController
import com.android.systemui.util.animation.TransitionViewState
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -371,7 +372,10 @@ class MediaViewController @Inject constructor(
* Attach a view to this controller. This may perform measurements if it's not available yet
* and should therefore be done carefully.
*/
- fun attach(transitionLayout: TransitionLayout, type: TYPE) {
+ fun attach(
+ transitionLayout: TransitionLayout,
+ type: TYPE
+ ) = traceSection("MediaViewController#attach") {
updateMediaViewControllerType(type)
logger.logMediaLocation("attach", currentStartLocation, currentEndLocation)
this.transitionLayout = transitionLayout
@@ -392,7 +396,9 @@ class MediaViewController @Inject constructor(
* and all widgets know their location. Calling this method may create a measurement if we
* don't have a cached value available already.
*/
- fun getMeasurementsForState(hostState: MediaHostState): MeasurementOutput? {
+ fun getMeasurementsForState(
+ hostState: MediaHostState
+ ): MeasurementOutput? = traceSection("MediaViewController#getMeasurementsForState") {
val viewState = obtainViewState(hostState) ?: return null
measurement.measuredWidth = viewState.width
measurement.measuredHeight = viewState.height
@@ -408,7 +414,7 @@ class MediaViewController @Inject constructor(
@MediaLocation endLocation: Int,
transitionProgress: Float,
applyImmediately: Boolean
- ) {
+ ) = traceSection("MediaViewController#setCurrentState") {
currentEndLocation = endLocation
currentStartLocation = startLocation
currentTransitionProgress = transitionProgress
@@ -540,7 +546,7 @@ class MediaViewController @Inject constructor(
/**
* Clear all existing measurements and refresh the state to match the view.
*/
- fun refreshState() {
+ fun refreshState() = traceSection("MediaViewController#refreshState") {
// Let's clear all of our measurements and recreate them!
viewStates.clear()
if (firstRefresh) {
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 1a727f8c3323..fa6db01275b0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -227,9 +227,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
lp.setFitInsetsIgnoringVisibility(true);
window.setAttributes(lp);
window.setContentView(mDialogView);
- // Sets window to a blank string to avoid talkback announce app label first when pop up,
- // which doesn't make sense.
- window.setTitle(EMPTY_TITLE);
+ window.setTitle(mContext.getString(R.string.media_output_dialog_accessibility_title));
mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 5fd9671bda01..512230ee54a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -98,6 +98,7 @@ public interface NotificationLockscreenUserManager {
interface UserChangedListener {
default void onUserChanged(int userId) {}
default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
+ default void onUserRemoved(int userId) {}
}
/** Used to hide notifications on the lockscreen */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 334cfe5f4c41..0394724dd597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -152,6 +152,15 @@ public class NotificationLockscreenUserManagerImpl implements
listener.onUserChanged(mCurrentUserId);
}
break;
+ case Intent.ACTION_USER_REMOVED:
+ int removedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (removedUserId != -1) {
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserRemoved(removedUserId);
+ }
+ }
+ updateCurrentProfilesCache();
+ break;
case Intent.ACTION_USER_ADDED:
case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
@@ -303,6 +312,7 @@ public class NotificationLockscreenUserManagerImpl implements
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt
index 06235071734e..d44a56942065 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt
@@ -14,9 +14,9 @@
package com.android.systemui.statusbar.phone
import android.view.MotionEvent
-import com.android.internal.util.RingBuffer
import com.android.systemui.dump.DumpsysTableLogger
import com.android.systemui.dump.Row
+import com.android.systemui.util.collection.RingBuffer
import java.text.SimpleDateFormat
import java.util.Locale
@@ -67,16 +67,9 @@ class NPVCDownEventState private constructor(
* Do not use [append] to add new elements. Instead use [insert], as it will recycle if
* necessary.
*/
- class Buffer(
- capacity: Int
- ) : RingBuffer<NPVCDownEventState>(NPVCDownEventState::class.java, capacity) {
- override fun append(t: NPVCDownEventState?) {
- throw UnsupportedOperationException("Not supported, use insert instead")
- }
+ class Buffer(capacity: Int) {
- override fun createNewItem(): NPVCDownEventState {
- return NPVCDownEventState()
- }
+ private val buffer = RingBuffer(capacity) { NPVCDownEventState() }
/**
* Insert a new element in the buffer.
@@ -94,7 +87,7 @@ class NPVCDownEventState private constructor(
touchSlopExceededBeforeDown: Boolean,
lastEventSynthesized: Boolean
) {
- nextSlot.apply {
+ buffer.advance().apply {
this.timeStamp = timeStamp
this.x = x
this.y = y
@@ -115,7 +108,7 @@ class NPVCDownEventState private constructor(
* @see NPVCDownEventState.asStringList
*/
fun toList(): List<Row> {
- return toArray().map { it.asStringList }
+ return buffer.asSequence().map { it.asStringList }.toList()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 314ab2221b19..075df1a28b11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -322,7 +322,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
private final RecordingController mRecordingController;
private final PanelEventsEmitter mPanelEventsEmitter;
- private boolean mShouldUseSplitNotificationShade;
+ private boolean mSplitShadeEnabled;
// The bottom padding reserved for elements of the keyguard measuring notifications
private float mKeyguardNotificationBottomPadding;
/**
@@ -813,7 +813,7 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
mFragmentService = fragmentService;
mSettingsChangeObserver = new SettingsChangeObserver(handler);
- mShouldUseSplitNotificationShade =
+ mSplitShadeEnabled =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
mView.setWillNotDraw(!DEBUG);
mLargeScreenShadeHeaderController = largeScreenShadeHeaderController;
@@ -1028,7 +1028,7 @@ public class NotificationPanelViewController extends PanelViewController {
});
mView.setAccessibilityDelegate(mAccessibilityDelegate);
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
updateResources();
}
@@ -1125,20 +1125,18 @@ public class NotificationPanelViewController extends PanelViewController {
mSplitShadeNotificationsScrimMarginBottom =
mResources.getDimensionPixelSize(
R.dimen.split_shade_notifications_scrim_margin_bottom);
-
mShelfAndLockIconOverlap =
mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap);
- final boolean newShouldUseSplitNotificationShade =
+ final boolean newSplitShadeEnabled =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
- final boolean splitNotificationShadeChanged =
- mShouldUseSplitNotificationShade != newShouldUseSplitNotificationShade;
+ final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
+ mSplitShadeEnabled = newSplitShadeEnabled;
- mShouldUseSplitNotificationShade = newShouldUseSplitNotificationShade;
boolean useLargeScreenShadeHeader =
LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources());
if (mQs != null) {
- mQs.setInSplitShade(mShouldUseSplitNotificationShade);
+ mQs.setInSplitShade(mSplitShadeEnabled);
}
mLargeScreenShadeHeaderHeight =
mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
@@ -1154,7 +1152,12 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardMediaController.refreshMediaPosition();
- if (splitNotificationShadeChanged) {
+ if (splitShadeChanged) {
+ // when we switch from split shade to regular shade we want to enforce setting qs to
+ // the default state: expanded for split shade and collapsed otherwise
+ if (!isOnKeyguard() && mPanelExpanded) {
+ setQsExpanded(mSplitShadeEnabled);
+ }
updateClockAppearance();
updateQsState();
mNotificationStackScrollLayoutController.updateFooter();
@@ -1383,7 +1386,7 @@ public class NotificationPanelViewController extends PanelViewController {
updateClockAppearance();
}
if (!onKeyguard) {
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
// Quick settings are not on the top of the notifications
// when in split shade mode (they are on the left side),
// so we should not add a padding for them
@@ -1411,10 +1414,9 @@ public class NotificationPanelViewController extends PanelViewController {
.getVisibleNotificationCount() != 0
|| mMediaDataManager.hasActiveMediaOrRecommendation();
boolean splitShadeWithActiveMedia =
- mShouldUseSplitNotificationShade
- && mMediaDataManager.hasActiveMediaOrRecommendation();
+ mSplitShadeEnabled && mMediaDataManager.hasActiveMediaOrRecommendation();
boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
- if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
+ if ((hasVisibleNotifications && !mSplitShadeEnabled)
|| (splitShadeWithActiveMedia && !mDozing)) {
mKeyguardStatusViewController.displayClock(SMALL, shouldAnimateClockChange);
} else {
@@ -1451,7 +1453,7 @@ public class NotificationPanelViewController extends PanelViewController {
bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(),
mDisplayTopInset,
- mShouldUseSplitNotificationShade,
+ mSplitShadeEnabled,
udfpsAodTopLocation,
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
@@ -1481,8 +1483,7 @@ public class NotificationPanelViewController extends PanelViewController {
boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0
|| mMediaDataManager.hasActiveMediaOrRecommendation();
- boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications
- || mDozing;
+ boolean shouldBeCentered = !mSplitShadeEnabled || !hasVisibleNotifications || mDozing;
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -1491,7 +1492,7 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
if (animate) {
ChangeBounds transition = new ChangeBounds();
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
// Excluding media from the transition on split-shade, as it doesn't transition
// horizontally properly.
transition.excludeTarget(R.id.status_view_media_container, true);
@@ -1648,7 +1649,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void setShowShelfOnly(boolean shelfOnly) {
mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
- shelfOnly && !mShouldUseSplitNotificationShade);
+ shelfOnly && !mSplitShadeEnabled);
}
public void closeQs() {
@@ -1689,7 +1690,7 @@ public class NotificationPanelViewController extends PanelViewController {
mQsExpandImmediate = true;
setShowShelfOnly(true);
}
- if (mShouldUseSplitNotificationShade && isOnKeyguard()) {
+ if (mSplitShadeEnabled && isOnKeyguard()) {
// It's a special case as this method is likely to not be initiated by finger movement
// but rather called from adb shell or accessibility service.
// We're using LockscreenShadeTransitionController because on lockscreen that's the
@@ -1919,7 +1920,7 @@ public class NotificationPanelViewController extends PanelViewController {
int flingType;
if (expandsQs && !isCancelMotionEvent) {
flingType = FLING_EXPAND;
- } else if (mShouldUseSplitNotificationShade) {
+ } else if (mSplitShadeEnabled) {
flingType = FLING_HIDE;
} else {
flingType = FLING_COLLAPSE;
@@ -1985,11 +1986,11 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean handleQsTouch(MotionEvent event) {
- if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(event.getX())) {
+ if (mSplitShadeEnabled && touchXOutsideOfQs(event.getX())) {
return false;
}
final int action = event.getActionMasked();
- boolean collapsedQs = !mQsExpanded && !mShouldUseSplitNotificationShade;
+ boolean collapsedQs = !mQsExpanded && !mSplitShadeEnabled;
boolean expandedShadeCollapsedQs = getExpandedFraction() == 1f && mBarState != KEYGUARD
&& collapsedQs && isQsExpansionEnabled();
if (action == MotionEvent.ACTION_DOWN && expandedShadeCollapsedQs) {
@@ -2007,7 +2008,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
- if (!mConflictingQsExpansionGesture && !mShouldUseSplitNotificationShade) {
+ if (!mConflictingQsExpansionGesture && !mSplitShadeEnabled) {
return true;
}
}
@@ -2298,7 +2299,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void updateQsState() {
- boolean qsFullScreen = mQsExpanded && !mShouldUseSplitNotificationShade;
+ boolean qsFullScreen = mQsExpanded && !mSplitShadeEnabled;
mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
mNotificationStackScrollLayoutController.setScrollingEnabled(
mBarState != KEYGUARD && (!qsFullScreen || mQsExpansionFromOverscroll));
@@ -2346,7 +2347,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQsExpansion() {
if (mQs == null) return;
final float squishiness;
- if ((mQsExpandImmediate || mQsExpanded) && !mShouldUseSplitNotificationShade) {
+ if ((mQsExpandImmediate || mQsExpanded) && !mSplitShadeEnabled) {
squishiness = 1;
} else if (mLockscreenShadeTransitionController.getQSDragProgress() > 0) {
squishiness = mLockscreenShadeTransitionController.getQSDragProgress();
@@ -2355,7 +2356,7 @@ public class NotificationPanelViewController extends PanelViewController {
.getNotificationSquishinessFraction();
}
final float qsExpansionFraction = computeQsExpansionFraction();
- final float adjustedExpansionFraction = mShouldUseSplitNotificationShade
+ final float adjustedExpansionFraction = mSplitShadeEnabled
? 1f : computeQsExpansionFraction();
mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
squishiness);
@@ -2364,7 +2365,7 @@ public class NotificationPanelViewController extends PanelViewController {
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
setQSClippingBounds();
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
// In split shade we want to pretend that QS are always collapsed so their behaviour and
// interactions don't influence notifications as they do in portrait. But we want to set
// 0 explicitly in case we're rotating from non-split shade with QS expansion of 1.
@@ -2404,7 +2405,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQSExpansionEnabledAmbient() {
final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsHeaderHeight;
- mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
+ mQsExpansionEnabledAmbient = mSplitShadeEnabled
|| (mAmbientState.getScrollY() <= scrollRangeToTop);
setQsExpansionEnabled();
}
@@ -2428,7 +2429,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int calculateTopQsClippingBound(int qsPanelBottomY) {
int top;
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
top = Math.min(qsPanelBottomY, mLargeScreenShadeHeaderHeight);
} else {
if (mTransitioningToFullShadeProgress > 0.0f) {
@@ -2462,7 +2463,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private int calculateBottomQsClippingBound(int top) {
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
return top + mNotificationStackScrollLayoutController.getHeight()
+ mSplitShadeNotificationsScrimMarginBottom;
} else {
@@ -2559,7 +2560,7 @@ public class NotificationPanelViewController extends PanelViewController {
// qsTranslation should only be positive during pulse expansion because it's
// already translating in from the top
qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f);
- } else if (!mShouldUseSplitNotificationShade) {
+ } else if (!mSplitShadeEnabled) {
qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT;
}
}
@@ -2573,11 +2574,11 @@ public class NotificationPanelViewController extends PanelViewController {
mQsClipTop,
mQsClipBottom,
radius, qsVisible
- && !mShouldUseSplitNotificationShade);
+ && !mSplitShadeEnabled);
}
mKeyguardStatusViewController.setClipBounds(
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
- if (!qsVisible && mShouldUseSplitNotificationShade) {
+ if (!qsVisible && mSplitShadeEnabled) {
// On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
// be visible, otherwise you can see the bounds once swiping up to see bouncer
mScrimController.setNotificationsBounds(0, 0, 0, 0);
@@ -2585,12 +2586,11 @@ public class NotificationPanelViewController extends PanelViewController {
// Increase the height of the notifications scrim when not in split shade
// (e.g. portrait tablet) so the rounded corners are not visible at the bottom,
// in this case they are rendered off-screen
- final int notificationsScrimBottom =
- mShouldUseSplitNotificationShade ? bottom : bottom + radius;
+ final int notificationsScrimBottom = mSplitShadeEnabled ? bottom : bottom + radius;
mScrimController.setNotificationsBounds(left, top, right, notificationsScrimBottom);
}
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
mKeyguardStatusBarViewController.setNoTopClipping();
} else {
mKeyguardStatusBarViewController.updateTopClipping(top);
@@ -2604,7 +2604,7 @@ public class NotificationPanelViewController extends PanelViewController {
int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
- int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
+ int bottomRadius = mSplitShadeEnabled ? radius : 0;
mNotificationStackScrollLayoutController.setRoundedClippingBounds(
nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
}
@@ -2646,7 +2646,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private float calculateNotificationsTopPadding() {
- if (mShouldUseSplitNotificationShade && !mKeyguardShowing) {
+ if (mSplitShadeEnabled && !mKeyguardShowing) {
return 0;
}
if (mKeyguardShowing && (mQsExpandImmediate
@@ -2919,7 +2919,7 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
if (!isQsExpansionEnabled() || mCollapsedOnDown
|| (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())
- || mShouldUseSplitNotificationShade) {
+ || mSplitShadeEnabled) {
return false;
}
View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
@@ -2950,7 +2950,7 @@ public class NotificationPanelViewController extends PanelViewController {
return true;
}
- return !mShouldUseSplitNotificationShade && (isInSettings() || mIsPanelCollapseOnQQS);
+ return !mSplitShadeEnabled && (isInSettings() || mIsPanelCollapseOnQQS);
}
@Override
@@ -3134,7 +3134,7 @@ public class NotificationPanelViewController extends PanelViewController {
float appearAmount = mNotificationStackScrollLayoutController
.calculateAppearFraction(mExpandedHeight);
float startHeight = -mQsExpansionHeight;
- if (!mShouldUseSplitNotificationShade && mBarState == StatusBarState.SHADE) {
+ if (!mSplitShadeEnabled && mBarState == StatusBarState.SHADE) {
// Small parallax as we pull down and clip QS
startHeight = -mQsExpansionHeight * QS_PARALLAX_AMOUNT;
}
@@ -3682,7 +3682,7 @@ public class NotificationPanelViewController extends PanelViewController {
mQs.setCollapseExpandAction(mCollapseExpandAction);
mQs.setHeaderClickable(isQsExpansionEnabled());
mQs.setOverscrolling(mStackScrollerOverscrolling);
- mQs.setInSplitShade(mShouldUseSplitNotificationShade);
+ mQs.setInSplitShade(mSplitShadeEnabled);
// recompute internal state when qspanel height changes
mQs.getView().addOnLayoutChangeListener(
@@ -4353,7 +4353,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
// When in split shade, overscroll shouldn't carry through to QS
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
return;
}
cancelQsAnimation();
@@ -4371,7 +4371,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void flingTopOverscroll(float velocity, boolean open) {
// in split shade mode we want to expand/collapse QS only when touch happens within QS
- if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(mInitialTouchX)) {
+ if (mSplitShadeEnabled && touchXOutsideOfQs(mInitialTouchX)) {
return;
}
mLastOverscroll = 0f;
@@ -4711,7 +4711,7 @@ public class NotificationPanelViewController extends PanelViewController {
// the top of QS
if (!mQsExpanded) {
// TODO(b/185683835) Nicer clipping when using new spacial model
- if (mShouldUseSplitNotificationShade) {
+ if (mSplitShadeEnabled) {
mQs.animateHeaderSlidingOut();
}
}
@@ -4908,7 +4908,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQSMinHeight() {
float previousMin = mQsMinExpansionHeight;
- if (mKeyguardShowing || mShouldUseSplitNotificationShade) {
+ if (mKeyguardShowing || mSplitShadeEnabled) {
mQsMinExpansionHeight = 0;
} else {
mQsMinExpansionHeight = mQs.getQsMinExpansionHeight();
@@ -5042,7 +5042,7 @@ public class NotificationPanelViewController extends PanelViewController {
// we need to ignore it on keyguard as this is a false alarm - transition from unlocked
// to locked will trigger this event and we're not actually in the process of opening
// the shade, lockscreen is just always expanded
- if (mShouldUseSplitNotificationShade && !isOnKeyguard()) {
+ if (mSplitShadeEnabled && !isOnKeyguard()) {
mQsExpandImmediate = true;
}
mCentralSurfaces.makeExpandedVisible(false);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c6a8445d2443..094490ba7adc 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -48,6 +48,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -475,10 +476,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
mThemeStyle = fetchThemeStyleFromSetting();
mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
- if (colorSchemeIsApplied()) {
- Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme);
- return;
- }
mNeedsOverlayCreation = true;
if (DEBUG) {
Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
@@ -538,19 +535,28 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
/**
* Checks if the color scheme in mColorScheme matches the current system palettes.
+ * @param managedProfiles List of managed profiles for this user.
*/
- private boolean colorSchemeIsApplied() {
- return mResources.getColor(
- android.R.color.system_accent1_500, mContext.getTheme())
- == mColorScheme.getAccent1().get(6)
- && mResources.getColor(android.R.color.system_accent2_500, mContext.getTheme())
- == mColorScheme.getAccent2().get(6)
- && mResources.getColor(android.R.color.system_accent3_500, mContext.getTheme())
- == mColorScheme.getAccent3().get(6)
- && mResources.getColor(android.R.color.system_neutral1_500, mContext.getTheme())
- == mColorScheme.getNeutral1().get(6)
- && mResources.getColor(android.R.color.system_neutral2_500, mContext.getTheme())
- == mColorScheme.getNeutral2().get(6);
+ private boolean colorSchemeIsApplied(Set<UserHandle> managedProfiles) {
+ final ArraySet<UserHandle> allProfiles = new ArraySet<>(managedProfiles);
+ allProfiles.add(UserHandle.SYSTEM);
+ for (UserHandle userHandle : allProfiles) {
+ Resources res = userHandle.isSystem()
+ ? mResources : mContext.createContextAsUser(userHandle, 0).getResources();
+ if (!(res.getColor(android.R.color.system_accent1_500, mContext.getTheme())
+ == mColorScheme.getAccent1().get(6)
+ && res.getColor(android.R.color.system_accent2_500, mContext.getTheme())
+ == mColorScheme.getAccent2().get(6)
+ && res.getColor(android.R.color.system_accent3_500, mContext.getTheme())
+ == mColorScheme.getAccent3().get(6)
+ && res.getColor(android.R.color.system_neutral1_500, mContext.getTheme())
+ == mColorScheme.getNeutral1().get(6)
+ && res.getColor(android.R.color.system_neutral2_500, mContext.getTheme())
+ == mColorScheme.getNeutral2().get(6))) {
+ return false;
+ }
+ }
+ return true;
}
private void updateThemeOverlays() {
@@ -623,6 +629,12 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
managedProfiles.add(userInfo.getUserHandle());
}
}
+
+ if (colorSchemeIsApplied(managedProfiles)) {
+ Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme);
+ return;
+ }
+
if (DEBUG) {
Log.d(TAG, "Applying overlays: " + categoryToPackage.keySet().stream()
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
diff --git a/packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt b/packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt
new file mode 100644
index 000000000000..97dc842ec699
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.collection
+
+import kotlin.math.max
+
+/**
+ * A simple ring buffer implementation
+ *
+ * Use [advance] to get the least recent item in the buffer (and then presumably fill it with
+ * appropriate data). This will cause it to become the most recent item.
+ *
+ * As the buffer is used, it will grow, allocating new instances of T using [factory] until it
+ * reaches [maxSize]. After this point, no new instances will be created. Instead, the "oldest"
+ * instances will be recycled from the back of the buffer and placed at the front.
+ *
+ * @param maxSize The maximum size the buffer can grow to before it begins functioning as a ring.
+ * @param factory A function that creates a fresh instance of T. Used by the buffer while it's
+ * growing to [maxSize].
+ */
+class RingBuffer<T>(
+ private val maxSize: Int,
+ private val factory: () -> T
+) : Iterable<T> {
+
+ private val buffer = MutableList<T?>(maxSize) { null }
+
+ /**
+ * An abstract representation that points to the "end" of the buffer. Increments every time
+ * [advance] is called and never wraps. Use [indexOf] to calculate the associated index into
+ * the backing array. Always points to the "next" available slot in the buffer. Before the
+ * buffer has completely filled, the value pointed to will be null. Afterward, it will be the
+ * value at the "beginning" of the buffer.
+ *
+ * This value is unlikely to overflow. Assuming [advance] is called at rate of 100 calls/ms,
+ * omega will overflow after a little under three million years of continuous operation.
+ */
+ private var omega: Long = 0
+
+ /**
+ * The number of items currently stored in the buffer. Calls to [advance] will cause this value
+ * to increase by one until it reaches [maxSize].
+ */
+ val size: Int
+ get() = if (omega < maxSize) omega.toInt() else maxSize
+
+ /**
+ * Advances the buffer's position by one and returns the value that is now present at the "end"
+ * of the buffer. If the buffer is not yet full, uses [factory] to create a new item.
+ * Otherwise, reuses the value that was previously at the "beginning" of the buffer.
+ *
+ * IMPORTANT: The value is returned as-is, without being reset. It will retain any data that
+ * was previously stored on it.
+ */
+ fun advance(): T {
+ val index = indexOf(omega)
+ omega += 1
+ val entry = buffer[index] ?: factory().also {
+ buffer[index] = it
+ }
+ return entry
+ }
+
+ /**
+ * Returns the value stored at [index], which can range from 0 (the "start", or oldest element
+ * of the buffer) to [size] - 1 (the "end", or newest element of the buffer).
+ */
+ operator fun get(index: Int): T {
+ if (index < 0 || index >= size) {
+ throw IndexOutOfBoundsException("Index $index is out of bounds")
+ }
+
+ // If omega is larger than the maxSize, then the buffer is full, and omega is equivalent
+ // to the "start" of the buffer. If omega is smaller than the maxSize, then the buffer is
+ // not yet full and our start should be 0. However, in modspace, maxSize and 0 are
+ // equivalent, so we can get away with using it as the start value instead.
+ val start = max(omega, maxSize.toLong())
+
+ return buffer[indexOf(start + index)]!!
+ }
+
+ inline fun forEach(action: (T) -> Unit) {
+ for (i in 0 until size) {
+ action(get(i))
+ }
+ }
+
+ override fun iterator(): Iterator<T> {
+ return object : Iterator<T> {
+ private var position: Int = 0
+
+ override fun next(): T {
+ if (position >= size) {
+ throw NoSuchElementException()
+ }
+ return get(position).also { position += 1 }
+ }
+
+ override fun hasNext(): Boolean {
+ return position < size
+ }
+ }
+ }
+
+ private fun indexOf(position: Long): Int {
+ return (position % maxSize).toInt()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 190e655ae403..f71d98827e4b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -152,7 +152,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
private void applyConfiguration() {
mController.setVolumePolicy(mVolumePolicy);
- mController.showDndTile(true);
+ mController.showDndTile();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 59aa4d6fcb8b..bf7c4598b5da 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -18,6 +18,8 @@ package com.android.systemui.volume;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -91,10 +93,8 @@ import javax.inject.Inject;
public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
-
private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
private static final int DYNAMIC_STREAM_START_INDEX = 100;
- private static final int VIBRATE_HINT_DURATION = 50;
private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -122,14 +122,16 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private final PackageManager mPackageManager;
private final MediaRouter2Manager mRouter2Manager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private AudioManager mAudio;
- private IAudioService mAudioService;
+ private final AudioManager mAudio;
+ private final IAudioService mAudioService;
private final NotificationManager mNoMan;
private final SettingObserver mObserver;
private final Receiver mReceiver = new Receiver();
private final RingerModeObservers mRingerModeObservers;
private final MediaSessions mMediaSessions;
private final CaptioningManager mCaptioningManager;
+ private final KeyguardManager mKeyguardManager;
+ private final ActivityManager mActivityManager;
protected C mCallbacks = new C();
private final State mState = new State();
protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
@@ -141,9 +143,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private long mLastToggledRingerOn;
private boolean mDeviceInteractive = true;
- private boolean mDestroyed;
private VolumePolicy mVolumePolicy;
- private boolean mShowDndTile = true;
@GuardedBy("this")
private UserActivityListener mUserActivityListener;
@@ -176,7 +176,10 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
AccessibilityManager accessibilityManager,
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle,
- CaptioningManager captioningManager) {
+ CaptioningManager captioningManager,
+ KeyguardManager keyguardManager,
+ ActivityManager activityManager
+ ) {
mContext = context.getApplicationContext();
mPackageManager = packageManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -202,6 +205,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mHasVibrator = mVibrator.hasVibrator();
mAudioService = iAudioService;
mCaptioningManager = captioningManager;
+ mKeyguardManager = keyguardManager;
+ mActivityManager = activityManager;
+
boolean accessibilityVolumeStreamActive = accessibilityManager
.isAccessibilityVolumeStreamActive();
@@ -247,7 +253,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
public void register() {
setVolumeController();
setVolumePolicy(mVolumePolicy);
- showDndTile(mShowDndTile);
+ showDndTile();
try {
mMediaSessions.init();
} catch (SecurityException e) {
@@ -272,10 +278,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
public void dump(PrintWriter pw, String[] args) {
pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
- pw.print(" mDestroyed: "); pw.println(mDestroyed);
pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
pw.print(" mState: "); pw.println(mState.toString(4));
- pw.print(" mShowDndTile: "); pw.println(mShowDndTile);
pw.print(" mHasVibrator: "); pw.println(mHasVibrator);
synchronized (mMediaSessionsCallbacksW.mRemoteStreams) {
pw.print(" mRemoteStreams: ");
@@ -293,7 +297,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
public void setUserActivityListener(UserActivityListener listener) {
- if (mDestroyed) return;
synchronized (this) {
mUserActivityListener = listener;
}
@@ -304,7 +307,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
public void getState() {
- if (mDestroyed) return;
mWorker.sendEmptyMessage(W.GET_STATE);
}
@@ -317,48 +319,39 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
public void getCaptionsComponentState(boolean fromTooltip) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.GET_CAPTIONS_COMPONENT_STATE, fromTooltip).sendToTarget();
}
public void notifyVisible(boolean visible) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
}
public void userActivity() {
- if (mDestroyed) return;
mWorker.removeMessages(W.USER_ACTIVITY);
mWorker.sendEmptyMessage(W.USER_ACTIVITY);
}
public void setRingerMode(int value, boolean external) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
}
public void setZenMode(int value) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
}
public void setExitCondition(Condition condition) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
}
public void setStreamMute(int stream, boolean mute) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
}
public void setStreamVolume(int stream, int level) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
}
public void setActiveStream(int stream) {
- if (mDestroyed) return;
mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
}
@@ -392,7 +385,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
private void onNotifyVisibleW(boolean visible) {
- if (mDestroyed) return;
mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
if (!visible) {
if (updateActiveStreamW(-1)) {
@@ -465,7 +457,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mCallbacks.onStateChanged(mState);
}
if (showUI) {
- mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+ onShowRequestedW(Events.SHOW_REASON_VOLUME_CHANGED);
}
if (showVibrateHint) {
mCallbacks.onShowVibrateHint();
@@ -645,6 +637,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
return true;
}
+ private void onShowRequestedW(int reason) {
+ mCallbacks.onShowRequested(reason, mKeyguardManager.isKeyguardLocked(),
+ mActivityManager.getLockTaskModeState());
+ }
+
private void onSetRingerModeW(int mode, boolean external) {
if (external) {
mAudio.setRingerMode(mode);
@@ -687,9 +684,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mCallbacks.onDismissRequested(reason);
}
- public void showDndTile(boolean visible) {
+ public void showDndTile() {
if (D.BUG) Log.d(TAG, "showDndTile");
- DndTile.setVisible(mContext, visible);
+ DndTile.setVisible(mContext, true);
}
private final class VC extends IVolumeController.Stub {
@@ -699,7 +696,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
public void displaySafeVolumeWarning(int flags) throws RemoteException {
if (D.BUG) Log.d(TAG, "displaySafeVolumeWarning "
+ Util.audioManagerFlagsToString(flags));
- if (mDestroyed) return;
mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
}
@@ -707,7 +703,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
public void volumeChanged(int streamType, int flags) throws RemoteException {
if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
+ " " + Util.audioManagerFlagsToString(flags));
- if (mDestroyed) return;
mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
}
@@ -719,14 +714,12 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Override
public void setLayoutDirection(int layoutDirection) throws RemoteException {
if (D.BUG) Log.d(TAG, "setLayoutDirection");
- if (mDestroyed) return;
mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
}
@Override
public void dismiss() throws RemoteException {
if (D.BUG) Log.d(TAG, "dismiss requested");
- if (mDestroyed) return;
mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
.sendToTarget();
mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
@@ -735,7 +728,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Override
public void setA11yMode(int mode) {
if (D.BUG) Log.d(TAG, "setA11yMode to " + mode);
- if (mDestroyed) return;
switch (mode) {
case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
// "legacy" mode
@@ -798,7 +790,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
}
- class C implements Callbacks {
+ static class C implements Callbacks {
private final Map<Callbacks, Handler> mCallbackMap = new ConcurrentHashMap<>();
public void add(Callbacks callback, Handler handler) {
@@ -811,12 +803,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
@Override
- public void onShowRequested(final int reason) {
+ public void onShowRequested(
+ final int reason,
+ final boolean keyguardLocked,
+ final int lockTaskModeState) {
for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
entry.getValue().post(new Runnable() {
@Override
public void run() {
- entry.getKey().onShowRequested(reason);
+ entry.getKey().onShowRequested(reason, keyguardLocked, lockTaskModeState);
}
});
}
@@ -923,7 +918,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Override
public void onAccessibilityModeChanged(Boolean showA11yStream) {
- boolean show = showA11yStream == null ? false : showA11yStream;
+ boolean show = showA11yStream != null && showA11yStream;
for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
entry.getValue().post(new Runnable() {
@Override
@@ -937,7 +932,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Override
public void onCaptionComponentStateChanged(
Boolean isComponentEnabled, Boolean fromTooltip) {
- boolean componentEnabled = isComponentEnabled == null ? false : isComponentEnabled;
+ boolean componentEnabled = isComponentEnabled != null && isComponentEnabled;
for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
entry.getValue().post(
() -> entry.getKey().onCaptionComponentStateChanged(
@@ -1183,7 +1178,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mCallbacks.onStateChanged(mState);
}
if (showUI) {
- mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
+ onShowRequestedW(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index bfdcbd6deb48..e3cb2fa5a0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -32,6 +32,8 @@ import static android.view.View.LAYOUT_DIRECTION_RTL;
import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL;
+import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
import android.animation.Animator;
@@ -68,6 +70,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.VibrationEffect;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -101,9 +104,11 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.RotationPolicy;
import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
@@ -149,6 +154,13 @@ public class VolumeDialogImpl implements VolumeDialog,
private static final int DRAWER_ANIMATION_DURATION_SHORT = 175;
private static final int DRAWER_ANIMATION_DURATION = 250;
+ /** Shows volume dialog show animation. */
+ private static final String TYPE_SHOW = "show";
+ /** Dismiss volume dialog animation. */
+ private static final String TYPE_DISMISS = "dismiss";
+ /** Volume dialog slider animation. */
+ private static final String TYPE_UPDATE = "update";
+
private final int mDialogShowAnimationDurationMs;
private final int mDialogHideAnimationDurationMs;
private int mDialogWidth;
@@ -248,7 +260,7 @@ public class VolumeDialogImpl implements VolumeDialog,
private State mState;
private SafetyWarningDialog mSafetyWarning;
private boolean mHovering = false;
- private boolean mShowActiveStreamOnly;
+ private final boolean mShowActiveStreamOnly;
private boolean mConfigChanged = false;
private boolean mIsAnimatingDismiss = false;
private boolean mHasSeenODICaptionsTooltip;
@@ -258,6 +270,7 @@ public class VolumeDialogImpl implements VolumeDialog,
private final boolean mUseBackgroundBlur;
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
private BackgroundBlurDrawable mDialogRowsViewBackground;
+ private final InteractionJankMonitor mInteractionJankMonitor;
public VolumeDialogImpl(
Context context,
@@ -266,7 +279,8 @@ public class VolumeDialogImpl implements VolumeDialog,
DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
MediaOutputDialogFactory mediaOutputDialogFactory,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ InteractionJankMonitor interactionJankMonitor) {
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mController = volumeDialogController;
@@ -290,6 +304,7 @@ public class VolumeDialogImpl implements VolumeDialog,
mContext.getResources().getInteger(R.integer.config_dialogHideAnimationDurationMs);
mUseBackgroundBlur =
mContext.getResources().getBoolean(R.bool.config_volumeDialogUseBackgroundBlur);
+ mInteractionJankMonitor = interactionJankMonitor;
if (mUseBackgroundBlur) {
final int dialogRowsViewColorAboveBlur = mContext.getColor(
@@ -312,7 +327,7 @@ public class VolumeDialogImpl implements VolumeDialog,
}
public void init(int windowType, Callback callback) {
- initDialog();
+ initDialog(mActivityManager.getLockTaskModeState());
mAccessibility.init();
@@ -379,7 +394,7 @@ public class VolumeDialogImpl implements VolumeDialog,
Region.Op.UNION);
}
- private void initDialog() {
+ private void initDialog(int lockTaskModeState) {
mDialog = new CustomDialog(mContext);
initDimens();
@@ -422,6 +437,7 @@ public class VolumeDialogImpl implements VolumeDialog,
.alpha(1)
.translationX(0)
.setDuration(mDialogShowAnimationDurationMs)
+ .setListener(getJankListener(getDialogView(), TYPE_SHOW, DIALOG_TIMEOUT_MILLIS))
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
@@ -573,7 +589,7 @@ public class VolumeDialogImpl implements VolumeDialog,
updateRowsH(getActiveRow());
initRingerH();
- initSettingsH();
+ initSettingsH(lockTaskModeState);
initODICaptionsH();
}
@@ -692,7 +708,7 @@ public class VolumeDialogImpl implements VolumeDialog,
final int m = seekBar.getMax();
final int n = m / 100 - 1;
final int level = progress == 0 ? 0
- : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
+ : progress == m ? (m / 100) : (1 + (int) ((progress / (float) m) * n));
return level;
}
@@ -1018,12 +1034,11 @@ public class VolumeDialogImpl implements VolumeDialog,
mIsRingerDrawerOpen = false;
}
- public void initSettingsH() {
+ private void initSettingsH(int lockTaskModeState) {
if (mSettingsView != null) {
mSettingsView.setVisibility(
mDeviceProvisionedController.isCurrentUserSetup() &&
- mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE ?
- VISIBLE : GONE);
+ lockTaskModeState == LOCK_TASK_MODE_NONE ? VISIBLE : GONE);
}
if (mSettingsIcon != null) {
mSettingsIcon.setOnClickListener(v -> {
@@ -1251,27 +1266,54 @@ public class VolumeDialogImpl implements VolumeDialog,
mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
}
- private void showH(int reason) {
+ private Animator.AnimatorListener getJankListener(View v, String type, long timeout) {
+ return new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {
+ mInteractionJankMonitor.begin(Builder.withView(CUJ_VOLUME_CONTROL, v).setTag(type)
+ .setTimeout(timeout));
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ mInteractionJankMonitor.end(CUJ_VOLUME_CONTROL);
+ }
+
+ @Override
+ public void onAnimationCancel(@NonNull Animator animation) {
+ mInteractionJankMonitor.cancel(CUJ_VOLUME_CONTROL);
+ }
+
+ @Override
+ public void onAnimationRepeat(@NonNull Animator animation) {
+ // no-op
+ }
+ };
+ }
+
+ private void showH(int reason, boolean keyguardLocked, int lockTaskModeState) {
+ Trace.beginSection("VolumeDialogImpl#showH");
if (D.BUG) Log.d(TAG, "showH r=" + Events.SHOW_REASONS[reason]);
mHandler.removeMessages(H.SHOW);
mHandler.removeMessages(H.DISMISS);
rescheduleTimeoutH();
if (mConfigChanged) {
- initDialog(); // resets mShowing to false
+ initDialog(lockTaskModeState); // resets mShowing to false
mConfigurableTexts.update();
mConfigChanged = false;
}
- initSettingsH();
+ initSettingsH(lockTaskModeState);
mShowing = true;
mIsAnimatingDismiss = false;
mDialog.show();
- Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+ Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked);
mController.notifyVisible(true);
mController.getCaptionsComponentState(false);
checkODICaptionsTooltip(false);
updateBackgroundForDrawerClosedAmount();
+ Trace.endSection();
}
protected void rescheduleTimeoutH() {
@@ -1305,6 +1347,7 @@ public class VolumeDialogImpl implements VolumeDialog,
}
protected void dismissH(int reason) {
+ Trace.beginSection("VolumeDialogImpl#dismissH");
if (D.BUG) {
Log.d(TAG, "mDialog.dismiss() reason: " + Events.DISMISS_REASONS[reason]
+ " from: " + Debug.getCaller());
@@ -1335,7 +1378,8 @@ public class VolumeDialogImpl implements VolumeDialog,
hideRingerDrawer();
}, 50));
if (!shouldSlideInVolumeTray()) animator.translationX(mDialogView.getWidth() / 2.0f);
- animator.start();
+ animator.setListener(getJankListener(getDialogView(), TYPE_DISMISS,
+ mDialogHideAnimationDurationMs)).start();
checkODICaptionsTooltip(true);
mController.notifyVisible(false);
synchronized (mSafetyWarningLock) {
@@ -1344,6 +1388,7 @@ public class VolumeDialogImpl implements VolumeDialog,
mSafetyWarning.dismiss();
}
}
+ Trace.endSection();
}
private boolean showActiveStreamOnly() {
@@ -1383,6 +1428,7 @@ public class VolumeDialogImpl implements VolumeDialog,
}
private void updateRowsH(final VolumeRow activeRow) {
+ Trace.beginSection("VolumeDialogImpl#updateRowsH");
if (D.BUG) Log.d(TAG, "updateRowsH");
if (!mShowing) {
trimObsoleteH();
@@ -1446,6 +1492,7 @@ public class VolumeDialogImpl implements VolumeDialog,
}
updateBackgroundForDrawerClosedAmount();
+ Trace.endSection();
}
protected void updateRingerH() {
@@ -1730,7 +1777,9 @@ public class VolumeDialogImpl implements VolumeDialog,
final boolean enableSlider = !zenMuted;
final int vlevel = row.ss.muted && (!isRingStream && !zenMuted) ? 0
: row.ss.level;
+ Trace.beginSection("VolumeDialogImpl#updateVolumeRowSliderH");
updateVolumeRowSliderH(row, enableSlider, vlevel);
+ Trace.endSection();
if (row.number != null) row.number.setText(Integer.toString(vlevel));
}
@@ -1760,14 +1809,6 @@ public class VolumeDialogImpl implements VolumeDialog,
mContext, com.android.internal.R.attr.textColorOnAccent);
row.sliderProgressSolid.setTintList(colorTint);
- if (row.sliderBgIcon != null) {
- row.sliderBgIcon.setTintList(colorTint);
- }
-
- if (row.sliderBgSolid != null) {
- row.sliderBgSolid.setTintList(bgTint);
- }
-
if (row.sliderProgressIcon != null) {
row.sliderProgressIcon.setTintList(bgTint);
}
@@ -1824,6 +1865,8 @@ public class VolumeDialogImpl implements VolumeDialog,
}
row.animTargetProgress = newProgress;
row.anim.setDuration(UPDATE_ANIMATION_DURATION);
+ row.anim.addListener(
+ getJankListener(row.view, TYPE_UPDATE, UPDATE_ANIMATION_DURATION));
row.anim.start();
} else {
// update slider directly to clamped value
@@ -1994,8 +2037,8 @@ public class VolumeDialogImpl implements VolumeDialog,
private final VolumeDialogController.Callbacks mControllerCallbackH
= new VolumeDialogController.Callbacks() {
@Override
- public void onShowRequested(int reason) {
- showH(reason);
+ public void onShowRequested(int reason, boolean keyguardLocked, int lockTaskModeState) {
+ showH(reason, keyguardLocked, lockTaskModeState);
}
@Override
@@ -2078,7 +2121,8 @@ public class VolumeDialogImpl implements VolumeDialog,
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case SHOW: showH(msg.arg1); break;
+ case SHOW: showH(msg.arg1, VolumeDialogImpl.this.mKeyguard.isKeyguardLocked(),
+ VolumeDialogImpl.this.mActivityManager.getLockTaskModeState()); break;
case DISMISS: dismissH(msg.arg1); break;
case RECHECK: recheckH((VolumeRow) msg.obj); break;
case RECHECK_ALL: recheckH(null); break;
@@ -2100,7 +2144,7 @@ public class VolumeDialogImpl implements VolumeDialog,
* within the bounds of the volume dialog, will fall through to the window below.
*/
@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
+ public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
rescheduleTimeoutH();
return super.dispatchTouchEvent(ev);
}
@@ -2124,7 +2168,7 @@ public class VolumeDialogImpl implements VolumeDialog,
* volume dialog.
*/
@Override
- public boolean onTouchEvent(MotionEvent event) {
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
if (mShowing) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
@@ -2213,8 +2257,6 @@ public class VolumeDialogImpl implements VolumeDialog,
private View view;
private TextView header;
private ImageButton icon;
- private Drawable sliderBgSolid;
- private AlphaTintDrawableWrapper sliderBgIcon;
private Drawable sliderProgressSolid;
private AlphaTintDrawableWrapper sliderProgressIcon;
private SeekBar slider;
@@ -2228,7 +2270,6 @@ public class VolumeDialogImpl implements VolumeDialog,
private int iconMuteRes;
private boolean important;
private boolean defaultStream;
- private ColorStateList cachedTint;
private int iconState; // from Events
private ObjectAnimator anim; // slider progress animation for non-touch-related updates
private int animTargetProgress;
@@ -2243,9 +2284,6 @@ public class VolumeDialogImpl implements VolumeDialog,
if (sliderProgressIcon != null) {
sliderProgressIcon.setDrawable(view.getResources().getDrawable(iconRes, theme));
}
- if (sliderBgIcon != null) {
- sliderBgIcon.setDrawable(view.getResources().getDrawable(iconRes, theme));
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 79aa643fc6c2..f3855bddfe48 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.volume.dagger;
import android.content.Context;
import android.media.AudioManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -51,7 +52,8 @@ public interface VolumeModule {
DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
MediaOutputDialogFactory mediaOutputDialogFactory,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ InteractionJankMonitor interactionJankMonitor) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -59,7 +61,8 @@ public interface VolumeModule {
deviceProvisionedController,
configurationController,
mediaOutputDialogFactory,
- activityStarter);
+ activityStarter,
+ interactionJankMonitor);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 542a53701529..83fc28189db5 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -271,6 +271,11 @@ public class BubblesManager implements Dumpable {
mBubbles.onCurrentProfilesChanged(currentProfiles);
}
+ @Override
+ public void onUserRemoved(int userId) {
+ mBubbles.onUserRemoved(userId);
+ }
+
});
mSysuiProxy = new Bubbles.SysuiProxy() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 6eba2154e46c..431739b6187a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -367,6 +367,73 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue()
assertThat(controllerOverlay.matchesRequestId(REQUEST_ID + 1)).isFalse()
}
+
+ @Test
+ fun testTouchOutsideAreaNoRotation() = withReason(REASON_ENROLL_ENROLLING) {
+ val touchHints =
+ context.resources.getStringArray(R.array.udfps_accessibility_touch_hints)
+ val rotation = Surface.ROTATION_0
+ // touch at 0 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, 0.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[0])
+ // touch at 90 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, -1.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[1])
+ // touch at 180 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(-1.0f /* x */, 0.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[2])
+ // touch at 270 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, 1.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[3])
+ }
+
+ fun testTouchOutsideAreaNoRotation90Degrees() = withReason(REASON_ENROLL_ENROLLING) {
+ val touchHints =
+ context.resources.getStringArray(R.array.udfps_accessibility_touch_hints)
+ val rotation = Surface.ROTATION_90
+ // touch at 0 degrees -> 90 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, 0.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[1])
+ // touch at 90 degrees -> 180 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, -1.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[2])
+ // touch at 180 degrees -> 270 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(-1.0f /* x */, 0.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[3])
+ // touch at 270 degrees -> 0 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, 1.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[0])
+ }
+
+ fun testTouchOutsideAreaNoRotation270Degrees() = withReason(REASON_ENROLL_ENROLLING) {
+ val touchHints =
+ context.resources.getStringArray(R.array.udfps_accessibility_touch_hints)
+ val rotation = Surface.ROTATION_270
+ // touch at 0 degrees -> 270 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, 0.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[3])
+ // touch at 90 degrees -> 0 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, -1.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[0])
+ // touch at 180 degrees -> 90 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(-1.0f /* x */, 0.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[1])
+ // touch at 270 degrees -> 180 degrees
+ assertThat(controllerOverlay.onTouchOutsideOfSensorAreaImpl(0.0f /* x */, 1.0f /* y */,
+ 0.0f /* sensorX */, 0.0f /* sensorY */, rotation))
+ .isEqualTo(touchHints[2])
+ }
}
private class EnrollListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
index 0720bdb06556..bd029a727ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
@@ -24,7 +24,7 @@ import com.android.systemui.log.LogcatEchoTracker
* Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
*/
fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
- LogBuffer(name, 50, 50, LogcatEchoTrackerAlways())
+ LogBuffer(name, 50, LogcatEchoTrackerAlways())
/**
* A [LogcatEchoTracker] that always allows echoing to the logcat.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
index 6971c63ed6d4..8cb530c355bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
@@ -30,7 +30,7 @@ class LSShadeTransitionLoggerTest : SysuiTestCase() {
@Before
fun setup() {
logger = LSShadeTransitionLogger(
- LogBuffer("Test", 10, 10, mock()),
+ LogBuffer("Test", 10, mock()),
gestureLogger,
displayMetrics)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 356d002e9da9..7ebf750e219c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -1090,6 +1090,21 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
+ public void testQsExpansionChangedToDefaultWhenRotatingFromOrToSplitShade() {
+ // to make sure shade is in expanded state
+ mNotificationPanelViewController.startWaitingForOpenPanelGesture();
+ assertThat(mNotificationPanelViewController.isQsExpanded()).isFalse();
+
+ // switch to split shade from portrait (default state)
+ enableSplitShade(/* enabled= */ true);
+ assertThat(mNotificationPanelViewController.isQsExpanded()).isTrue();
+
+ // switch to portrait from split shade
+ enableSplitShade(/* enabled= */ false);
+ assertThat(mNotificationPanelViewController.isQsExpanded()).isFalse();
+ }
+
+ @Test
public void interceptTouchEvent_withinQs_shadeExpanded_startsQsTracking() {
mNotificationPanelViewController.mQs = mQs;
when(mQsFrame.getX()).thenReturn(0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 98397fb01b6b..6abc687f0ebb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -379,7 +379,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mCommandQueue,
mCarrierConfigTracker,
new CollapsedStatusBarFragmentLogger(
- new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
+ new LogBuffer("TEST", 1, mock(LogcatEchoTracker.class)),
new DisableFlagsLogger()
),
mOperatorNameViewControllerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 68818f610fb7..b7f38f1433ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -733,6 +733,18 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
USER_SYSTEM);
+ reset(mResources);
+ when(mResources.getColor(eq(android.R.color.system_accent1_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent1().get(6));
+ when(mResources.getColor(eq(android.R.color.system_accent2_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent2().get(6));
+ when(mResources.getColor(eq(android.R.color.system_accent3_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent3().get(6));
+ when(mResources.getColor(eq(android.R.color.system_neutral1_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getNeutral1().get(6));
+ when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getNeutral2().get(6));
+
// Defers event because we already have initial colors.
verify(mThemeOverlayApplier, never())
.applyCurrentUserOverlays(any(), any(), anyInt(), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt
new file mode 100644
index 000000000000..5e09b81da4e8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.collection
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RingBufferTest : SysuiTestCase() {
+
+ private val buffer = RingBuffer(5) { TestElement() }
+
+ private val history = mutableListOf<TestElement>()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testBarelyFillBuffer() {
+ fillBuffer(5)
+
+ assertEquals(0, buffer[0].id)
+ assertEquals(1, buffer[1].id)
+ assertEquals(2, buffer[2].id)
+ assertEquals(3, buffer[3].id)
+ assertEquals(4, buffer[4].id)
+ }
+
+ @Test
+ fun testPartiallyFillBuffer() {
+ fillBuffer(3)
+
+ assertEquals(3, buffer.size)
+
+ assertEquals(0, buffer[0].id)
+ assertEquals(1, buffer[1].id)
+ assertEquals(2, buffer[2].id)
+
+ assertThrows(IndexOutOfBoundsException::class.java) { buffer[3] }
+ assertThrows(IndexOutOfBoundsException::class.java) { buffer[4] }
+ }
+
+ @Test
+ fun testSpinBuffer() {
+ fillBuffer(277)
+
+ assertEquals(272, buffer[0].id)
+ assertEquals(273, buffer[1].id)
+ assertEquals(274, buffer[2].id)
+ assertEquals(275, buffer[3].id)
+ assertEquals(276, buffer[4].id)
+ assertThrows(IndexOutOfBoundsException::class.java) { buffer[5] }
+
+ assertEquals(5, buffer.size)
+ }
+
+ @Test
+ fun testElementsAreRecycled() {
+ fillBuffer(23)
+
+ assertSame(history[4], buffer[1])
+ assertSame(history[9], buffer[1])
+ assertSame(history[14], buffer[1])
+ assertSame(history[19], buffer[1])
+ }
+
+ @Test
+ fun testIterator() {
+ fillBuffer(13)
+
+ val iterator = buffer.iterator()
+
+ for (i in 0 until 5) {
+ assertEquals(history[8 + i], iterator.next())
+ }
+ assertFalse(iterator.hasNext())
+ assertThrows(NoSuchElementException::class.java) { iterator.next() }
+ }
+
+ @Test
+ fun testForEach() {
+ fillBuffer(13)
+ var i = 8
+
+ buffer.forEach {
+ assertEquals(history[i], it)
+ i++
+ }
+ assertEquals(13, i)
+ }
+
+ private fun fillBuffer(count: Int) {
+ for (i in 0 until count) {
+ val elem = buffer.advance()
+ elem.id = history.size
+ history.add(elem)
+ }
+ }
+}
+
+private class TestElement(var id: Int = 0) {
+ override fun toString(): String {
+ return "{TestElement $id}"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index ec619bb5952a..aaf2188a2612 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.volume;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -23,6 +25,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -91,6 +95,10 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
private WakefulnessLifecycle mWakefullnessLifcycle;
@Mock
private CaptioningManager mCaptioningManager;
+ @Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
+ private ActivityManager mActivityManager;
@Before
@@ -112,7 +120,8 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
- mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mCallback);
+ mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mKeyguardManager,
+ mActivityManager, mCallback);
mVolumeController.setEnableDialogs(true, true);
}
@@ -129,7 +138,8 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
- verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+ verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
+ LOCK_TASK_MODE_NONE);
}
@Test
@@ -138,7 +148,8 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_AWAKE);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
- verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+ verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
+ LOCK_TASK_MODE_NONE);
}
@Test
@@ -151,7 +162,8 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
when(mWakefullnessLifcycle.getWakefulness()).thenReturn(
WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
- verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+ verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED, false,
+ LOCK_TASK_MODE_NONE);
}
@Test
@@ -188,10 +200,13 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
PackageManager packageManager,
WakefulnessLifecycle wakefulnessLifecycle,
CaptioningManager captioningManager,
+ KeyguardManager keyguardManager,
+ ActivityManager activityManager,
C callback) {
super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager,
notificationManager, optionalVibrator, iAudioService, accessibilityManager,
- packageManager, wakefulnessLifecycle, captioningManager);
+ packageManager, wakefulnessLifecycle, captioningManager, keyguardManager,
+ activityManager);
mCallbacks = callback;
ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 949345662e79..312db2d7066a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -38,6 +38,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -85,6 +86,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
MediaOutputDialogFactory mMediaOutputDialogFactory;
@Mock
ActivityStarter mActivityStarter;
+ @Mock
+ InteractionJankMonitor mInteractionJankMonitor;
@Before
public void setup() throws Exception {
@@ -99,7 +102,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mDeviceProvisionedController,
mConfigurationController,
mMediaOutputDialogFactory,
- mActivityStarter);
+ mActivityStarter,
+ mInteractionJankMonitor);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 238a4d37a872..7d4e27fa2580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -66,6 +66,7 @@ import android.hardware.face.FaceManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
@@ -365,6 +366,7 @@ public class BubblesTest extends SysuiTestCase {
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
+ mock(UserManager.class),
mLauncherApps,
mBubbleLogger,
mTaskStackListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index dff89e0a5558..a6327b9daed5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -55,6 +55,7 @@ import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
@@ -330,6 +331,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
+ mock(UserManager.class),
mLauncherApps,
mBubbleLogger,
mTaskStackListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 9646edf6756a..17e5778f7aab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -19,6 +19,7 @@ package com.android.systemui.wmshell;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
+import android.os.UserManager;
import android.view.WindowManager;
import com.android.internal.statusbar.IStatusBarService;
@@ -53,6 +54,7 @@ public class TestableBubbleController extends BubbleController {
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
+ UserManager userManager,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
TaskStackListenerImpl taskStackListener,
@@ -66,10 +68,10 @@ public class TestableBubbleController extends BubbleController {
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
- statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
- oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler,
- taskViewTransitions, syncQueue);
+ statusBarService, windowManager, windowManagerShellWrapper, userManager,
+ launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, positioner,
+ displayController, oneHandedOptional, dragAndDropController, shellMainExecutor,
+ shellMainHandler, new SyncExecutor(), taskViewTransitions, syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 7a5fa628f645..570e4e6fdc3d 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -182,7 +182,7 @@ class AssociationRequestsProcessor {
// 2b.1. Populate the request with required info.
request.setPackageName(packageName);
request.setUserId(userId);
- request.setSkipPrompt(mayAssociateWithoutPrompt(request, packageName, userId));
+ request.setSkipPrompt(mayAssociateWithoutPrompt(packageName, userId));
// 2b.2. Prepare extras and create an Intent.
final Bundle extras = new Bundle();
@@ -321,18 +321,7 @@ class AssociationRequestsProcessor {
}
};
- private boolean mayAssociateWithoutPrompt(@NonNull AssociationRequest request,
- @NonNull String packageName, @UserIdInt int userId) {
- final String deviceProfile = request.getDeviceProfile();
- if (deviceProfile != null) {
- final boolean isRoleHolder = Binder.withCleanCallingIdentity(
- () -> isRoleHolder(mContext, userId, packageName, deviceProfile));
- if (isRoleHolder) {
- // Don't need to collect user's consent since app already holds the role.
- return true;
- }
- }
-
+ private boolean mayAssociateWithoutPrompt(@NonNull String packageName, @UserIdInt int userId) {
// Below we check if the requesting package is allowlisted (usually by the OEM) for creating
// CDM associations without user confirmation (prompt).
// For this we'll check to config arrays:
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 8622ef0940d5..0bfe2825bdd5 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -78,6 +78,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
@@ -121,8 +122,10 @@ public class CompanionDeviceManagerService extends SystemService {
private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
+ private static final String SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW =
+ "debug.cdm.cdmservice.removal_time_window";
- private static final long ASSOCIATION_CLEAN_UP_TIME_WINDOW = DAYS.toMillis(3 * 30); // 3 months
+ private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
private PersistentDataStore mPersistentStore;
private final PersistUserStateHandler mUserPersistenceHandler;
@@ -211,8 +214,8 @@ public class CompanionDeviceManagerService extends SystemService {
mPackageMonitor.register(context, FgThread.get().getLooper(), UserHandle.ALL, true);
mDevicePresenceMonitor.init(context);
} else if (phase == PHASE_BOOT_COMPLETED) {
- // Run the Association CleanUp job service daily.
- AssociationCleanUpService.schedule(getContext());
+ // Run the Inactive Association Removal job service daily.
+ InactiveAssociationsRemovalService.schedule(getContext());
}
}
@@ -410,17 +413,20 @@ public class CompanionDeviceManagerService extends SystemService {
mCompanionAppController.onPackagesChanged(userId);
}
- // Revoke associations if the selfManaged companion device does not connect for 3
- // months for specific profile.
- private void associationCleanUp(String profile) {
+ // Revoke associations if the selfManaged companion device does not connect for 3 months.
+ void removeInactiveSelfManagedAssociations() {
+ final long currentTime = System.currentTimeMillis();
+ long removalWindow = SystemProperties.getLong(SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW, -1);
+ if (removalWindow <= 0) {
+ // 0 or negative values indicate that the sysprop was never set or should be ignored.
+ removalWindow = ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT;
+ }
+
for (AssociationInfo ai : mAssociationStore.getAssociations()) {
- if (ai.isSelfManaged()
- && profile.equals(ai.getDeviceProfile())
- && System.currentTimeMillis() - ai.getLastTimeConnectedMs()
- >= ASSOCIATION_CLEAN_UP_TIME_WINDOW) {
- Slog.i(TAG, "Removing the association for associationId: "
- + ai.getId()
- + " due to the device does not connect for 3 months.");
+ if (!ai.isSelfManaged()) continue;
+ final boolean isInactive = currentTime - ai.getLastTimeConnectedMs() >= removalWindow;
+ if (isInactive) {
+ Slog.i(TAG, "Removing inactive self-managed association: " + ai.getId());
disassociateInternal(ai.getId());
}
}
@@ -1078,10 +1084,10 @@ public class CompanionDeviceManagerService extends SystemService {
return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
}
- private class LocalService extends CompanionDeviceManagerServiceInternal {
+ private class LocalService implements CompanionDeviceManagerServiceInternal {
@Override
- public void associationCleanUp(String profile) {
- CompanionDeviceManagerService.this.associationCleanUp(profile);
+ public void removeInactiveSelfManagedAssociations() {
+ CompanionDeviceManagerService.this.removeInactiveSelfManagedAssociations();
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
index 326fefe1f8ea..36492407d463 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
@@ -18,12 +18,10 @@ package com.android.server.companion;
/**
* Companion Device Manager Local System Service Interface.
- *
- * @hide Only for use within the system server.
*/
-public abstract class CompanionDeviceManagerServiceInternal {
+interface CompanionDeviceManagerServiceInternal {
/**
- * @see CompanionDeviceManagerService#associationCleanUp
+ * @see CompanionDeviceManagerService#removeInactiveSelfManagedAssociations
*/
- public abstract void associationCleanUp(String profile);
+ void removeInactiveSelfManagedAssociations();
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 6a19a42723c5..434d283b3f8a 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.companion;
import android.companion.AssociationInfo;
+import android.os.Binder;
import android.os.ShellCommand;
import android.util.Log;
import android.util.Slog;
@@ -96,6 +97,16 @@ class CompanionDeviceShellCommand extends ShellCommand {
mDevicePresenceMonitor.simulateDeviceDisappeared(associationId);
break;
+ case "remove-inactive-associations": {
+ // This command should trigger the same "clean-up" job as performed by the
+ // InactiveAssociationsRemovalService JobService. However, since the
+ // InactiveAssociationsRemovalService run as system, we want to run this
+ // as system (not as shell/root) as well.
+ Binder.withCleanCallingIdentity(
+ mService::removeInactiveSelfManagedAssociations);
+ }
+ break;
+
default:
return handleDefaultCommands(cmd);
}
@@ -142,6 +153,12 @@ class CompanionDeviceShellCommand extends ShellCommand {
pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than");
pw.println(" 60 seconds ago.");
pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+ pw.println(" remove-inactive-associations");
+ pw.println(" Remove self-managed associations that have not been active ");
+ pw.println(" for a long time (90 days or as configured via ");
+ pw.println(" \"debug.cdm.cdmservice.cleanup_time_window\" system property). ");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
}
private int getNextIntArgRequired() {
diff --git a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java b/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java
index 55246e14d592..3482863032f9 100644
--- a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
+++ b/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,10 +24,8 @@ import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
-import android.companion.AssociationRequest;
import android.content.ComponentName;
import android.content.Context;
-import android.os.AsyncTask;
import android.util.Slog;
import com.android.server.LocalServices;
@@ -37,26 +35,24 @@ import com.android.server.LocalServices;
* The job will be executed only if the device is charging and in idle mode due to the application
* will be killed if association/role are revoked.
*/
-public class AssociationCleanUpService extends JobService {
- private static final int JOB_ID = AssociationCleanUpService.class.hashCode();
+public class InactiveAssociationsRemovalService extends JobService {
+ private static final int JOB_ID = InactiveAssociationsRemovalService.class.hashCode();
private static final long ONE_DAY_INTERVAL = DAYS.toMillis(1);
@Override
public boolean onStartJob(final JobParameters params) {
- Slog.i(TAG, "Execute the Association CleanUp job");
- // Special policy for APP_STREAMING role that need to revoke associations if the device
- // does not connect for 3 months.
- AsyncTask.execute(() -> {
- LocalServices.getService(CompanionDeviceManagerServiceInternal.class)
- .associationCleanUp(AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
- jobFinished(params, false);
- });
+ Slog.i(TAG, "Execute the Association Removal job");
+ // Special policy for selfManaged that need to revoke associations if the device
+ // does not connect for 90 days.
+ LocalServices.getService(CompanionDeviceManagerServiceInternal.class)
+ .removeInactiveSelfManagedAssociations();
+ jobFinished(params, false);
return true;
}
@Override
public boolean onStopJob(final JobParameters params) {
- Slog.i(TAG, "Association cleanup job stopped; id=" + params.getJobId()
+ Slog.i(TAG, "Association removal job stopped; id=" + params.getJobId()
+ ", reason="
+ JobParameters.getInternalReasonCodeDescription(
params.getInternalStopReasonCode()));
@@ -64,10 +60,10 @@ public class AssociationCleanUpService extends JobService {
}
static void schedule(Context context) {
- Slog.i(TAG, "Scheduling the Association Cleanup job");
+ Slog.i(TAG, "Scheduling the Association Removal job");
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
final JobInfo job = new JobInfo.Builder(JOB_ID,
- new ComponentName(context, AssociationCleanUpService.class))
+ new ComponentName(context, InactiveAssociationsRemovalService.class))
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.setPeriodic(ONE_DAY_INTERVAL)
@@ -75,3 +71,4 @@ public class AssociationCleanUpService extends JobService {
jobScheduler.schedule(job);
}
}
+
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6e8bc826153..163df101620d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1530,6 +1530,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// Encapsulates the global setting "hidden_api_blacklist_exemptions"
final HiddenApiSettings mHiddenApiBlacklist;
+ final SdkSandboxSettings mSdkSandboxSettings;
+
private final PlatformCompat mPlatformCompat;
PackageManagerInternal mPackageManagerInt;
@@ -2235,6 +2237,53 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * Handles settings related to the enforcement of SDK sandbox restrictions.
+ */
+ static class SdkSandboxSettings implements DeviceConfig.OnPropertiesChangedListener {
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mEnforceBroadcastReceiverRestrictions;
+
+ /**
+ * Property to enforce broadcast receiver restrictions for SDK sandbox processes. If the
+ * value of this property is {@code true}, the restrictions will be enforced.
+ */
+ public static final String ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS =
+ "enforce_broadcast_receiver_restrictions";
+
+ SdkSandboxSettings(Context context) {
+ mContext = context;
+ }
+
+ void registerObserver() {
+ synchronized (mLock) {
+ mEnforceBroadcastReceiverRestrictions = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SDK_SANDBOX,
+ ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS, false);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SDK_SANDBOX,
+ mContext.getMainExecutor(), this);
+ }
+ }
+
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ synchronized (mLock) {
+ mEnforceBroadcastReceiverRestrictions = properties.getBoolean(
+ ENFORCE_BROADCAST_RECEIVER_RESTRICTIONS, false);
+ }
+ }
+
+ boolean isBroadcastReceiverRestrictionsEnforced() {
+ synchronized (mLock) {
+ return mEnforceBroadcastReceiverRestrictions;
+ }
+ }
+ }
+
AppOpsManager getAppOpsManager() {
if (mAppOpsManager == null) {
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
@@ -2287,6 +2336,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
+ mSdkSandboxSettings = null;
mFactoryTest = FACTORY_TEST_OFF;
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mInternal = new LocalService();
@@ -2406,6 +2456,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
+ mSdkSandboxSettings = new SdkSandboxSettings(mContext);
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
@@ -4358,6 +4409,14 @@ public class ActivityManagerService extends IActivityManager.Stub
+ " but does not exist in that user");
return;
}
+
+ // Policy: certain classes of app are not subject to user-invoked stop
+ if (getPackageManagerInternal().isPackageStateProtected(packageName, userId)) {
+ Slog.w(TAG, "Asked to stop " + packageName + "/u" + userId
+ + " but it is protected");
+ return;
+ }
+
Slog.i(TAG, "Stopping app for user: " + packageName + "/" + userId);
// A specific subset of the work done in forceStopPackageLocked(), because we are
@@ -7911,6 +7970,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final boolean alwaysFinishActivities =
Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
mHiddenApiBlacklist.registerObserver();
+ mSdkSandboxSettings.registerObserver();
mPlatformCompat.registerContentObserver();
mAppProfiler.retrieveSettings();
@@ -12940,7 +13000,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Allow Sandbox process to register only unexported receivers.
if ((flags & Context.RECEIVER_NOT_EXPORTED) != 0) {
enforceNotIsolatedCaller("registerReceiver");
- } else {
+ } else if (mSdkSandboxSettings.isBroadcastReceiverRestrictionsEnforced()) {
enforceNotIsolatedOrSdkSandboxCaller("registerReceiver");
}
ArrayList<Intent> stickyIntents = null;
@@ -13546,13 +13606,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (brOptions.getIdForResponseEvent() > 0) {
- // STOPSHIP (206518114): Temporarily check for PACKAGE_USAGE_STATS permission as
- // well until the clients switch to using the new permission.
- if (checkPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
- callingPid, callingUid) != PERMISSION_GRANTED) {
- enforceUsageStatsPermission(callerPackage, callingUid, callingPid,
- "recordResponseEventWhileInBackground()");
- }
+ enforcePermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
+ callingPid, callingUid, "recordResponseEventWhileInBackground");
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 397a4420700e..2c2579f9e504 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3561,6 +3561,9 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Enable/disable rate limit on FGS notification deferral policy.");
pw.println(" force-stop [--user <USER_ID> | all | current] <PACKAGE>");
pw.println(" Completely stop the given application package.");
+ pw.println(" stop-app [--user <USER_ID> | all | current] <PACKAGE>");
+ pw.println(" Stop an app and all of its services. Unlike `force-stop` this does");
+ pw.println(" not cancel the app's scheduled alarms and jobs.");
pw.println(" crash [--user <USER_ID>] <PACKAGE|PID>");
pw.println(" Induce a VM crash in the specified package or process");
pw.println(" kill [--user <USER_ID> | all | current] <PACKAGE>");
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index b9da144713cd..b56654fd7b9a 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3258,7 +3258,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED;
}
synchronized (this) {
- if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass)) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, true)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -3483,7 +3483,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
- if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass)) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, false)) {
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
@@ -3997,7 +3997,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
final UidState uidState = ops.uidState;
- isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass);
+ isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
+ false);
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
@@ -4834,7 +4835,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private boolean isOpRestrictedLocked(int uid, int code, String packageName,
- String attributionTag, @Nullable RestrictionBypass appBypass) {
+ String attributionTag, @Nullable RestrictionBypass appBypass, boolean isCheckOp) {
int restrictionSetCount = mOpGlobalRestrictions.size();
for (int i = 0; i < restrictionSetCount; i++) {
@@ -4851,7 +4852,8 @@ public class AppOpsService extends IAppOpsService.Stub {
// For each client, check that the given op is not restricted, or that the given
// package is exempt from the restriction.
ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
- if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle)) {
+ if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle,
+ isCheckOp)) {
RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
if (opBypass != null) {
// If we are the system, bypass user restrictions for certain codes
@@ -7224,7 +7226,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public boolean hasRestriction(int restriction, String packageName, String attributionTag,
- int userId) {
+ int userId, boolean isCheckOp) {
if (perUserRestrictions == null) {
return false;
}
@@ -7243,6 +7245,9 @@ public class AppOpsService extends IAppOpsService.Stub {
return true;
}
+ if (isCheckOp) {
+ return !perUserExclusions.includes(packageName);
+ }
return !perUserExclusions.contains(packageName, attributionTag);
}
@@ -7409,7 +7414,8 @@ public class AppOpsService extends IAppOpsService.Stub {
int numRestrictions = mOpUserRestrictions.size();
for (int i = 0; i < numRestrictions; i++) {
if (mOpUserRestrictions.valueAt(i)
- .hasRestriction(code, pkg, attributionTag, user.getIdentifier())) {
+ .hasRestriction(code, pkg, attributionTag, user.getIdentifier(),
+ false)) {
number++;
}
}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 0e2582c23b86..0d755213da07 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -39,6 +39,7 @@ import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -544,4 +545,37 @@ public class Utils {
throw new IllegalArgumentException("Unknown strength: " + strength);
}
}
+
+ /**
+ * Checks if a client package is running in the background.
+ *
+ * @param clientPackage The name of the package to be checked.
+ * @return Whether the client package is running in background
+ */
+ public static boolean isBackground(String clientPackage) {
+ Slog.v(TAG, "Checking if the authenticating is in background,"
+ + " clientPackage:" + clientPackage);
+ final List<ActivityManager.RunningTaskInfo> tasks =
+ ActivityTaskManager.getInstance().getTasks(Integer.MAX_VALUE);
+
+ if (tasks == null || tasks.isEmpty()) {
+ Slog.d(TAG, "No running tasks reported");
+ return true;
+ }
+
+ for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
+ final ComponentName topActivity = taskInfo.topActivity;
+ if (topActivity != null) {
+ final String topPackage = topActivity.getPackageName();
+ if (topPackage.contentEquals(clientPackage) && taskInfo.isVisible()) {
+ return false;
+ } else {
+ Slog.i(TAG, "Running task, top: " + topPackage
+ + ", isVisible: " + taskInfo.isVisible());
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 1002229518e4..4eb6d38d9227 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -19,10 +19,8 @@ package com.android.server.biometrics.sensors;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -42,7 +40,6 @@ import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import java.util.ArrayList;
-import java.util.List;
import java.util.function.Supplier;
/**
@@ -202,25 +199,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
if (!mAllowBackgroundAuthentication && authenticated
&& !Utils.isKeyguard(getContext(), getOwnerString())
&& !Utils.isSystem(getContext(), getOwnerString())) {
- final List<ActivityManager.RunningTaskInfo> tasks =
- mActivityTaskManager.getTasks(1);
- if (tasks == null || tasks.isEmpty()) {
- Slog.e(TAG, "No running tasks reported");
- isBackgroundAuth = true;
- } else {
- final ComponentName topActivity = tasks.get(0).topActivity;
- if (topActivity == null) {
- Slog.e(TAG, "Unable to get top activity");
- isBackgroundAuth = true;
- } else {
- final String topPackage = topActivity.getPackageName();
- if (!topPackage.contentEquals(getOwnerString())) {
- Slog.e(TAG, "Background authentication detected, top: " + topPackage
- + ", client: " + getOwnerString());
- isBackgroundAuth = true;
- }
- }
- }
+ isBackgroundAuth = Utils.isBackground(getOwnerString());
}
// Fail authentication if we can't confirm the client activity is on top.
@@ -465,7 +444,6 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
@Override
public void cancel() {
super.cancel();
-
if (mTaskStackListener != null) {
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 4e03ee9a618c..e0900b9df200 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -104,22 +104,17 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
Slog.e(getTag(), "Task stack changed for client: " + client);
continue;
}
- if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ if (Utils.isKeyguard(mContext, client.getOwnerString())
+ || Utils.isSystem(mContext, client.getOwnerString())) {
continue; // Keyguard is always allowed
}
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage =
- runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(client.getOwnerString())
- && !client.isAlreadyDone()) {
- Slog.e(getTag(), "Stopping background authentication, top: "
- + topPackage + " currentClient: " + client);
- mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
- client.getToken(), client.getRequestId());
- }
+ if (Utils.isBackground(client.getOwnerString())
+ && !client.isAlreadyDone()) {
+ Slog.e(getTag(), "Stopping background authentication,"
+ + " currentClient: " + client);
+ mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
+ client.getToken(), client.getRequestId());
}
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index a600f08efc24..f16af78364a2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -122,18 +122,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
continue; // Keyguard is always allowed
}
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage =
- runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(client.getOwnerString())
- && !client.isAlreadyDone()) {
- Slog.e(getTag(), "Stopping background authentication, top: "
- + topPackage + " currentClient: " + client);
- mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
- client.getToken(), client.getRequestId());
- }
+ if (Utils.isBackground(client.getOwnerString())
+ && !client.isAlreadyDone()) {
+ Slog.e(getTag(), "Stopping background authentication,"
+ + " currentClient: " + client);
+ mSensors.valueAt(i).getScheduler().cancelAuthenticationOrDetection(
+ client.getToken(), client.getRequestId());
}
}
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 78a30e820c80..2a3f34ae3cd4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -142,17 +142,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
return; // Keyguard is always allowed
}
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(client.getOwnerString())
- && !client.isAlreadyDone()) {
- Slog.e(TAG, "Stopping background authentication, top: "
- + topPackage + " currentClient: " + client);
- mScheduler.cancelAuthenticationOrDetection(
- client.getToken(), client.getRequestId());
- }
+ if (Utils.isBackground(client.getOwnerString())
+ && !client.isAlreadyDone()) {
+ Slog.e(TAG, "Stopping background authentication,"
+ + " currentClient: " + client);
+ mScheduler.cancelAuthenticationOrDetection(
+ client.getToken(), client.getRequestId());
}
});
}
diff --git a/services/core/java/com/android/server/input/TEST_MAPPING b/services/core/java/com/android/server/input/TEST_MAPPING
new file mode 100644
index 000000000000..9626d8dac787
--- /dev/null
+++ b/services/core/java/com/android/server/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/native/services/inputflinger"
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index beca71486899..1a6155b43f6b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -121,17 +121,19 @@ public abstract class ApexManager {
public final File apexDirectory;
public final File preInstalledApexPath;
public final File apexFile;
+ public final boolean activeApexChanged;
private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) {
- this(null, apexDirectory, preInstalledApexPath, apexFile);
+ this(null, apexDirectory, preInstalledApexPath, apexFile, false);
}
private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
- File preInstalledApexPath, File apexFile) {
+ File preInstalledApexPath, File apexFile, boolean activeApexChanged) {
this.apexModuleName = apexModuleName;
this.apexDirectory = apexDirectory;
this.preInstalledApexPath = preInstalledApexPath;
this.apexFile = apexFile;
+ this.activeApexChanged = activeApexChanged;
}
private ActiveApexInfo(ApexInfo apexInfo) {
@@ -140,7 +142,8 @@ public abstract class ApexManager {
new File(Environment.getApexDirectory() + File.separator
+ apexInfo.moduleName),
new File(apexInfo.preinstalledModulePath),
- new File(apexInfo.modulePath));
+ new File(apexInfo.modulePath),
+ apexInfo.activeApexChanged);
}
}
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index e7bcb0aa13c8..78e7b0a17764 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -26,6 +26,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.SigningDetails;
import android.os.Binder;
+import android.os.Handler;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
@@ -53,7 +54,7 @@ import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
-import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* AppsFilter is the entity responsible for filtering visibility between apps based on declarations
@@ -68,6 +69,11 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
protected static final boolean DEBUG_LOGGING = false;
public static final boolean DEBUG_TRACING = false;
+ // Allow some time for cache rebuilds.
+ protected static final int CACHE_REBUILD_DELAY_MIN_MS = 10000;
+ // With each new rebuild the delay doubles until it reaches max delay.
+ protected static final int CACHE_REBUILD_DELAY_MAX_MS = 10000;
+
/**
* This contains a list of app UIDs that are implicitly queryable because another app explicitly
* interacted with it. For example, if application A starts a service in application B,
@@ -122,10 +128,10 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
protected SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot;
/**
- * Executor for running reasonably short background tasks such as building the initial
+ * Handler for running reasonably short background tasks such as building the initial
* visibility cache.
*/
- protected Executor mBackgroundExecutor;
+ protected Handler mBackgroundHandler;
/**
* Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of
@@ -133,7 +139,7 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
* computationally expensive recomputing.
* Full recompute is done lazily at the point when we use mQueriesViaComponent to filter apps.
*/
- protected boolean mQueriesViaComponentRequireRecompute = false;
+ protected AtomicBoolean mQueriesViaComponentRequireRecompute = new AtomicBoolean(false);
/**
* A set of App IDs that are always queryable by any package, regardless of their manifest
@@ -183,6 +189,10 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
protected volatile boolean mCacheReady = false;
+ protected static final boolean CACHE_VALID = true;
+ protected static final boolean CACHE_INVALID = false;
+ protected AtomicBoolean mCacheValid = new AtomicBoolean(CACHE_INVALID);
+
protected boolean isForceQueryable(int callingAppId) {
return mForceQueryable.contains(callingAppId);
}
@@ -506,7 +516,7 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent");
}
- if (!mQueriesViaComponentRequireRecompute) {
+ if (!mQueriesViaComponentRequireRecompute.get()) {
if (isQueryableViaComponent(callingAppId, targetAppId)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "queries component");
@@ -702,17 +712,34 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
}
}
pw.println(" system apps queryable: " + mSystemAppsQueryable);
- dumpQueryables(pw, filteringAppId, users, expandPackages);
+ dumpForceQueryable(pw, filteringAppId, expandPackages);
+ dumpQueriesViaPackage(pw, filteringAppId, expandPackages);
+ dumpQueriesViaComponent(pw, filteringAppId, expandPackages);
+ dumpQueriesViaImplicitlyQueryable(pw, filteringAppId, users, expandPackages);
+ dumpQueriesViaUsesLibrary(pw, filteringAppId, expandPackages);
}
- protected void dumpQueryables(PrintWriter pw, @Nullable Integer filteringAppId, int[] users,
+ protected void dumpForceQueryable(PrintWriter pw, @Nullable Integer filteringAppId,
ToString<Integer> expandPackages) {
+ pw.println(" queries via forceQueryable:");
dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(),
"forceQueryable", " ", expandPackages);
+ }
+
+ protected void dumpQueriesViaPackage(PrintWriter pw, @Nullable Integer filteringAppId,
+ ToString<Integer> expandPackages) {
pw.println(" queries via package name:");
dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages);
+ }
+
+ protected void dumpQueriesViaComponent(PrintWriter pw, @Nullable Integer filteringAppId,
+ ToString<Integer> expandPackages) {
pw.println(" queries via component:");
dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages);
+ }
+
+ protected void dumpQueriesViaImplicitlyQueryable(PrintWriter pw,
+ @Nullable Integer filteringAppId, int[] users, ToString<Integer> expandPackages) {
pw.println(" queryable via interaction:");
for (int user : users) {
pw.append(" User ").append(Integer.toString(user)).println(":");
@@ -723,6 +750,10 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
mRetainedImplicitlyQueryable, " ", expandPackages);
}
+ }
+
+ protected void dumpQueriesViaUsesLibrary(PrintWriter pw, @Nullable Integer filteringAppId,
+ ToString<Integer> expandPackages) {
pw.println(" queryable via uses-library:");
dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ",
expandPackages);
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index c817e24d3b82..9fddc76b78c3 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -35,6 +35,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
+import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -70,7 +71,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.Executor;
/**
* Implementation of the methods that update the internal structures of AppsFilter. Because of the
@@ -83,7 +83,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
* A cached snapshot.
*/
@NonNull
- private SnapshotCache<AppsFilterSnapshot> mSnapshot;
+ private final SnapshotCache<AppsFilterSnapshot> mSnapshot;
/**
* Watchable machinery
@@ -142,18 +142,24 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
dispatchChange(this);
}
+ private void invalidateCache(String reason) {
+ if (mCacheValid.compareAndSet(CACHE_VALID, CACHE_INVALID)) {
+ Slog.i(TAG, "Invalidating cache: " + reason);
+ }
+ }
+
@VisibleForTesting(visibility = PRIVATE)
AppsFilterImpl(FeatureConfig featureConfig,
String[] forceQueryableList,
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider,
- Executor backgroundExecutor) {
+ Handler backgroundHandler) {
mFeatureConfig = featureConfig;
mForceQueryableByDevicePackageNames = forceQueryableList;
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
- mBackgroundExecutor = backgroundExecutor;
+ mBackgroundHandler = backgroundHandler;
mShouldFilterCache = new WatchedSparseBooleanMatrix();
mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>(
mShouldFilterCache, mShouldFilterCache, "AppsFilter.mShouldFilterCache");
@@ -371,7 +377,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
AppsFilterImpl appsFilter = new AppsFilterImpl(featureConfig,
forcedQueryablePackageNames, forceSystemAppsQueryable, null,
- injector.getBackgroundExecutor());
+ injector.getBackgroundHandler());
featureConfig.setAppsFilter(appsFilter);
return appsFilter;
}
@@ -394,7 +400,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
return false;
}
final boolean changed;
- synchronized (mLock) {
+ synchronized (mImplicitlyQueryableLock) {
changed = retainOnUpdate
? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid)
: mImplicitlyQueryable.add(recipientUid, visibleUid);
@@ -404,13 +410,13 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
+ recipientUid + " -> " + visibleUid);
}
- // TODO(b/231528435): invalidate cache instead of locking.
- if (true/*mCacheReady*/) {
+ if (mCacheReady) {
synchronized (mCacheLock) {
- // update the cache in a one-off manner since we've got all the information we
- // need.
+ // Update the cache in a one-off manner since we've got all the information we need.
mShouldFilterCache.put(recipientUid, visibleUid, false);
}
+ } else if (changed) {
+ invalidateCache("grantImplicitAccess: " + recipientUid + " -> " + visibleUid);
}
onChanged();
return changed;
@@ -444,8 +450,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
final UserInfo[] users = snapshot.getUserInfos();
final ArraySet<String> additionalChangedPackages =
addPackageInternal(newPkgSetting, settings);
- // TODO(b/231528435): invalidate cache instead of locking.
- if (true/*mCacheReady*/) {
+ if (mCacheReady) {
synchronized (mCacheLock) {
updateShouldFilterCacheForPackage(snapshot, null, newPkgSetting,
settings, users, USER_ALL, settings.size());
@@ -464,7 +469,9 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
}
}
- } // else, rebuild entire cache when system is ready
+ } else {
+ invalidateCache("addPackage: " + newPkgSetting.getPackageName());
+ }
} finally {
onChanged();
if (DEBUG_TRACING) {
@@ -487,7 +494,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
// packages for signature matches
for (PackageStateInternal setting : existingSettings.values()) {
if (isSystemSigned(mSystemSigningDetails, setting)) {
- synchronized (mLock) {
+ synchronized (mForceQueryableLock) {
mForceQueryable.add(setting.getAppId());
}
}
@@ -499,13 +506,18 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
return null;
}
- synchronized (mLock) {
- if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) {
- mQueriesViaComponentRequireRecompute = true;
- }
+ final boolean protectedBroadcastsChanged;
+ synchronized (mProtectedBroadcastsLock) {
+ protectedBroadcastsChanged =
+ mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
+ }
+ if (protectedBroadcastsChanged) {
+ mQueriesViaComponentRequireRecompute.set(true);
+ }
- final boolean newIsForceQueryable =
- mForceQueryable.contains(newPkgSetting.getAppId())
+ final boolean newIsForceQueryable;
+ synchronized (mForceQueryableLock) {
+ newIsForceQueryable = mForceQueryable.contains(newPkgSetting.getAppId())
/* shared user that is already force queryable */
|| newPkgSetting.isForceQueryableOverride() /* adb override */
|| (newPkgSetting.isSystem() && (mSystemAppsQueryable
@@ -517,58 +529,77 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
&& isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
mForceQueryable.add(newPkgSetting.getAppId());
}
+ }
- for (int i = existingSettings.size() - 1; i >= 0; i--) {
- final PackageStateInternal existingSetting = existingSettings.valueAt(i);
- if (existingSetting.getAppId() == newPkgSetting.getAppId()
- || existingSetting.getPkg()
- == null) {
- continue;
- }
- final AndroidPackage existingPkg = existingSetting.getPkg();
- // let's evaluate the ability of already added packages to see this new package
- if (!newIsForceQueryable) {
- if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg,
- newPkg, mProtectedBroadcasts)) {
+ for (int i = existingSettings.size() - 1; i >= 0; i--) {
+ final PackageStateInternal existingSetting = existingSettings.valueAt(i);
+ if (existingSetting.getAppId() == newPkgSetting.getAppId()
+ || existingSetting.getPkg()
+ == null) {
+ continue;
+ }
+ final AndroidPackage existingPkg = existingSetting.getPkg();
+ // let's evaluate the ability of already added packages to see this new package
+ if (!newIsForceQueryable) {
+ if (!mQueriesViaComponentRequireRecompute.get()
+ && canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) {
+ synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.add(existingSetting.getAppId(),
newPkgSetting.getAppId());
}
- if (canQueryViaPackage(existingPkg, newPkg)
- || canQueryAsInstaller(existingSetting, newPkg)) {
+ }
+ if (canQueryViaPackage(existingPkg, newPkg)
+ || canQueryAsInstaller(existingSetting, newPkg)) {
+ synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.add(existingSetting.getAppId(),
newPkgSetting.getAppId());
}
- if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+ }
+ if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+ synchronized (mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary.add(existingSetting.getAppId(),
newPkgSetting.getAppId());
}
}
- // now we'll evaluate our new package's ability to see existing packages
- if (!mForceQueryable.contains(existingSetting.getAppId())) {
- if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg,
- existingPkg, mProtectedBroadcasts)) {
+ }
+ final boolean existingIsForceQueryable;
+ synchronized (mForceQueryableLock) {
+ existingIsForceQueryable = mForceQueryable.contains(existingSetting.getAppId());
+ }
+ // now we'll evaluate our new package's ability to see existing packages
+ if (!existingIsForceQueryable) {
+ if (!mQueriesViaComponentRequireRecompute.get()
+ && canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) {
+ synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.add(newPkgSetting.getAppId(),
existingSetting.getAppId());
}
- if (canQueryViaPackage(newPkg, existingPkg)
- || canQueryAsInstaller(newPkgSetting, existingPkg)) {
+ }
+ if (canQueryViaPackage(newPkg, existingPkg)
+ || canQueryAsInstaller(newPkgSetting, existingPkg)) {
+ synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.add(newPkgSetting.getAppId(),
existingSetting.getAppId());
}
- if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+ }
+ if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+ synchronized (mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(),
existingSetting.getAppId());
}
}
- // if either package instruments the other, mark both as visible to one another
- if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null
- && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg())
- || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) {
+ }
+ // if either package instruments the other, mark both as visible to one another
+ if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null
+ && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg())
+ || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) {
+ synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId());
mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId());
}
}
}
+
int existingSize = existingSettings.size();
ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
for (int index = 0; index < existingSize; index++) {
@@ -587,9 +618,6 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
private void removeAppIdFromVisibilityCache(int appId) {
- if (!mCacheReady) {
- return;
- }
synchronized (mCacheLock) {
for (int i = 0; i < mShouldFilterCache.size(); i++) {
if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) {
@@ -643,7 +671,17 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal) {
- mBackgroundExecutor.execute(() -> {
+ updateEntireShouldFilterCacheAsync(pmInternal, CACHE_REBUILD_DELAY_MIN_MS);
+ }
+
+ private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal,
+ long delayMs) {
+ mBackgroundHandler.postDelayed(() -> {
+ if (!mCacheValid.compareAndSet(CACHE_INVALID, CACHE_VALID)) {
+ // Cache is already valid.
+ return;
+ }
+
final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>();
final UserInfo[][] usersRef = new UserInfo[1][];
final PackageDataSnapshot snapshot = pmInternal.snapshot();
@@ -663,8 +701,15 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
updateEntireShouldFilterCacheInner(snapshot, settings, usersRef[0], USER_ALL);
onChanged();
+ if (!mCacheValid.compareAndSet(CACHE_VALID, CACHE_VALID)) {
+ Slog.i(TAG, "Cache invalidated while building, retrying.");
+ updateEntireShouldFilterCacheAsync(pmInternal,
+ Math.min(delayMs * 2, CACHE_REBUILD_DELAY_MAX_MS));
+ return;
+ }
+
mCacheReady = true;
- });
+ }, delayMs);
}
public void onUserCreated(PackageDataSnapshot snapshot, int newUserId) {
@@ -775,7 +820,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
private void collectProtectedBroadcasts(
ArrayMap<String, ? extends PackageStateInternal> existingSettings,
@Nullable String excludePackage) {
- synchronized (mLock) {
+ synchronized (mProtectedBroadcastsLock) {
mProtectedBroadcasts.clear();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageStateInternal setting = existingSettings.valueAt(i);
@@ -809,30 +854,37 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
*/
private void recomputeComponentVisibility(
ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
- synchronized (mLock) {
+ synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.clear();
- for (int i = existingSettings.size() - 1; i >= 0; i--) {
- PackageStateInternal setting = existingSettings.valueAt(i);
- if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
+ }
+ for (int i = existingSettings.size() - 1; i >= 0; i--) {
+ PackageStateInternal setting = existingSettings.valueAt(i);
+ if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
+ continue;
+ }
+ for (int j = existingSettings.size() - 1; j >= 0; j--) {
+ if (i == j) {
continue;
}
- for (int j = existingSettings.size() - 1; j >= 0; j--) {
- if (i == j) {
- continue;
- }
- final PackageStateInternal otherSetting = existingSettings.valueAt(j);
- if (otherSetting.getPkg() == null || mForceQueryable.contains(
- otherSetting.getAppId())) {
- continue;
- }
- if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(),
- mProtectedBroadcasts)) {
+ final PackageStateInternal otherSetting = existingSettings.valueAt(j);
+ if (otherSetting.getPkg() == null || mForceQueryable.contains(
+ otherSetting.getAppId())) {
+ continue;
+ }
+ final boolean canQueryViaComponents;
+ synchronized (mProtectedBroadcastsLock) {
+ canQueryViaComponents = canQueryViaComponents(setting.getPkg(),
+ otherSetting.getPkg(), mProtectedBroadcasts);
+ }
+ if (canQueryViaComponents) {
+ synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId());
}
}
}
}
- mQueriesViaComponentRequireRecompute = false;
+
+ mQueriesViaComponentRequireRecompute.set(false);
onChanged();
}
@@ -860,7 +912,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
final UserInfo[] users = snapshot.getUserInfos();
final Collection<SharedUserSetting> sharedUserSettings = snapshot.getAllSharedUsers();
final int userCount = users.length;
- synchronized (mLock) {
+ synchronized (mImplicitlyQueryableLock) {
for (int u = 0; u < userCount; u++) {
final int userId = users[u].id;
final int removingUid = UserHandle.getUid(userId, setting.getAppId());
@@ -880,39 +932,53 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
mRetainedImplicitlyQueryable.keyAt(i), removingUid);
}
}
+ }
- if (!mQueriesViaComponentRequireRecompute) {
+ if (!mQueriesViaComponentRequireRecompute.get()) {
+ synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.remove(setting.getAppId());
for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
- mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i),
- setting.getAppId());
+ mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.getAppId());
}
}
+ }
+
+ synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.remove(setting.getAppId());
for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i),
setting.getAppId());
}
+ }
+
+ synchronized (mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary.remove(setting.getAppId());
for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i),
setting.getAppId());
}
+ }
+ synchronized (mForceQueryableLock) {
mForceQueryable.remove(setting.getAppId());
+ }
+ boolean protectedBroadcastsChanged = false;
+ synchronized (mProtectedBroadcastsLock) {
if (setting.getPkg() != null
&& !setting.getPkg().getProtectedBroadcasts().isEmpty()) {
final String removingPackageName = setting.getPkg().getPackageName();
- final ArrayList<String> protectedBroadcasts = new ArrayList<>();
- protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage());
+ final ArrayList<String> protectedBroadcasts = new ArrayList<>(
+ mProtectedBroadcasts.untrackedStorage());
collectProtectedBroadcasts(settings, removingPackageName);
- if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) {
- mQueriesViaComponentRequireRecompute = true;
- }
+ protectedBroadcastsChanged = !mProtectedBroadcasts.containsAll(protectedBroadcasts);
}
}
+ if (protectedBroadcastsChanged) {
+ mQueriesViaComponentRequireRecompute.set(true);
+ }
+
additionalChangedPackages = mOverlayReferenceMapper.removePkg(setting.getPackageName());
mFeatureConfig.updatePackageState(setting, true /*removed*/);
@@ -932,27 +998,26 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
}
- removeAppIdFromVisibilityCache(setting.getAppId());
- // TODO(b/231528435): invalidate cache instead of locking.
- if (/*mCacheReady && */setting.hasSharedUser()) {
- final ArraySet<? extends PackageStateInternal> sharedUserPackages =
- getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings);
- for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
- PackageStateInternal siblingSetting =
- sharedUserPackages.valueAt(i);
- if (siblingSetting == setting) {
- continue;
- }
- synchronized (mCacheLock) {
- updateShouldFilterCacheForPackage(snapshot,
- setting.getPackageName(), siblingSetting, settings,
- users, USER_ALL, settings.size());
+ if (mCacheReady) {
+ removeAppIdFromVisibilityCache(setting.getAppId());
+
+ if (setting.hasSharedUser()) {
+ final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+ getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings);
+ for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
+ PackageStateInternal siblingSetting =
+ sharedUserPackages.valueAt(i);
+ if (siblingSetting == setting) {
+ continue;
+ }
+ synchronized (mCacheLock) {
+ updateShouldFilterCacheForPackage(snapshot,
+ setting.getPackageName(), siblingSetting, settings,
+ users, USER_ALL, settings.size());
+ }
}
}
- }
- // TODO(b/231528435): invalidate cache instead of locking.
- if (true/*mCacheReady*/) {
if (additionalChangedPackages != null) {
for (int index = 0; index < additionalChangedPackages.size(); index++) {
String changedPackage = additionalChangedPackages.valueAt(index);
@@ -969,6 +1034,8 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
}
}
+ } else {
+ invalidateCache("removePackage: " + setting.getPackageName());
}
onChanged();
}
diff --git a/services/core/java/com/android/server/pm/AppsFilterLocked.java b/services/core/java/com/android/server/pm/AppsFilterLocked.java
index b85bd8fbaa87..30eb09e61d3f 100644
--- a/services/core/java/com/android/server/pm/AppsFilterLocked.java
+++ b/services/core/java/com/android/server/pm/AppsFilterLocked.java
@@ -26,9 +26,18 @@ import java.io.PrintWriter;
*/
abstract class AppsFilterLocked extends AppsFilterBase {
/**
- * Guards the accesses for the list/set class members
+ * The following locks guard the accesses for the list/set class members
*/
- protected final Object mLock = new Object();
+ protected final Object mForceQueryableLock = new Object();
+ protected final Object mQueriesViaPackageLock = new Object();
+ protected final Object mQueriesViaComponentLock = new Object();
+ /**
+ * This lock covers both {@link #mImplicitlyQueryable} and {@link #mRetainedImplicitlyQueryable}
+ */
+ protected final Object mImplicitlyQueryableLock = new Object();
+ protected final Object mQueryableViaUsesLibraryLock = new Object();
+ protected final Object mProtectedBroadcastsLock = new Object();
+
/**
* Guards the access for {@link AppsFilterBase#mShouldFilterCache};
*/
@@ -36,42 +45,42 @@ abstract class AppsFilterLocked extends AppsFilterBase {
@Override
protected boolean isForceQueryable(int appId) {
- synchronized (mLock) {
+ synchronized (mForceQueryableLock) {
return super.isForceQueryable(appId);
}
}
@Override
protected boolean isQueryableViaPackage(int callingAppId, int targetAppId) {
- synchronized (mLock) {
+ synchronized (mQueriesViaPackageLock) {
return super.isQueryableViaPackage(callingAppId, targetAppId);
}
}
@Override
protected boolean isQueryableViaComponent(int callingAppId, int targetAppId) {
- synchronized (mLock) {
+ synchronized (mQueriesViaComponentLock) {
return super.isQueryableViaComponent(callingAppId, targetAppId);
}
}
@Override
protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) {
- synchronized (mLock) {
+ synchronized (mImplicitlyQueryableLock) {
return super.isImplicitlyQueryable(callingAppId, targetAppId);
}
}
@Override
protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) {
- synchronized (mLock) {
+ synchronized (mImplicitlyQueryableLock) {
return super.isRetainedImplicitlyQueryable(callingAppId, targetAppId);
}
}
@Override
protected boolean isQueryableViaUsesLibrary(int callingAppId, int targetAppId) {
- synchronized (mLock) {
+ synchronized (mQueryableViaUsesLibraryLock) {
return super.isQueryableViaUsesLibrary(callingAppId, targetAppId);
}
}
@@ -84,10 +93,42 @@ abstract class AppsFilterLocked extends AppsFilterBase {
}
@Override
- protected void dumpQueryables(PrintWriter pw, @Nullable Integer filteringAppId, int[] users,
+ protected void dumpForceQueryable(PrintWriter pw, @Nullable Integer filteringAppId,
+ ToString<Integer> expandPackages) {
+ synchronized (mForceQueryableLock) {
+ super.dumpForceQueryable(pw, filteringAppId, expandPackages);
+ }
+ }
+
+ @Override
+ protected void dumpQueriesViaPackage(PrintWriter pw, @Nullable Integer filteringAppId,
+ ToString<Integer> expandPackages) {
+ synchronized (mQueriesViaPackageLock) {
+ super.dumpQueriesViaPackage(pw, filteringAppId, expandPackages);
+ }
+ }
+
+ @Override
+ protected void dumpQueriesViaComponent(PrintWriter pw, @Nullable Integer filteringAppId,
+ ToString<Integer> expandPackages) {
+ synchronized (mQueriesViaComponentLock) {
+ super.dumpQueriesViaComponent(pw, filteringAppId, expandPackages);
+ }
+ }
+
+ @Override
+ protected void dumpQueriesViaImplicitlyQueryable(PrintWriter pw,
+ @Nullable Integer filteringAppId, int[] users, ToString<Integer> expandPackages) {
+ synchronized (mImplicitlyQueryableLock) {
+ super.dumpQueriesViaImplicitlyQueryable(pw, filteringAppId, users, expandPackages);
+ }
+ }
+
+ @Override
+ protected void dumpQueriesViaUsesLibrary(PrintWriter pw, @Nullable Integer filteringAppId,
ToString<Integer> expandPackages) {
- synchronized (mLock) {
- dumpQueryables(pw, filteringAppId, users, expandPackages);
+ synchronized (mQueryableViaUsesLibraryLock) {
+ super.dumpQueriesViaUsesLibrary(pw, filteringAppId, expandPackages);
}
}
}
diff --git a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java
index 39b63415e42e..6ae6efae1230 100644
--- a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java
@@ -26,22 +26,32 @@ import java.util.Arrays;
*/
public final class AppsFilterSnapshotImpl extends AppsFilterBase {
AppsFilterSnapshotImpl(AppsFilterImpl orig) {
- synchronized (orig.mLock) {
+ synchronized (orig.mImplicitlyQueryableLock) {
mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot();
- mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>();
mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot();
- mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>();
+ }
+ mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>();
+ mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>();
+ synchronized (orig.mQueriesViaPackageLock) {
mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot();
- mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>();
+ }
+ mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>();
+ synchronized (orig.mQueriesViaComponentLock) {
mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot();
- mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>();
+ }
+ mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>();
+ synchronized (orig.mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot();
- mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>();
+ }
+ mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>();
+ synchronized (orig.mForceQueryableLock) {
mForceQueryable = orig.mForceQueryableSnapshot.snapshot();
- mForceQueryableSnapshot = new SnapshotCache.Sealed<>();
+ }
+ mForceQueryableSnapshot = new SnapshotCache.Sealed<>();
+ synchronized (orig.mProtectedBroadcastsLock) {
mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot();
- mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>();
}
+ mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>();
mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
mForceQueryableByDevicePackageNames =
Arrays.copyOf(orig.mForceQueryableByDevicePackageNames,
@@ -62,6 +72,6 @@ public final class AppsFilterSnapshotImpl extends AppsFilterBase {
}
mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>();
- mBackgroundExecutor = null;
+ mBackgroundHandler = null;
}
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index f3c41af05da3..30de9ba638cc 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -5443,22 +5443,18 @@ public class ComputerEngine implements Computer {
false /*checkShell*/, "may package query");
final PackageStateInternal sourceSetting = getPackageStateInternal(sourcePackageName);
final PackageStateInternal targetSetting = getPackageStateInternal(targetPackageName);
- if (sourceSetting == null || targetSetting == null) {
+ boolean throwException = sourceSetting == null || targetSetting == null;
+ if (!throwException) {
+ final boolean filterSource =
+ shouldFilterApplication(sourceSetting, callingUid, userId);
+ final boolean filterTarget =
+ shouldFilterApplication(targetSetting, callingUid, userId);
+ // The caller must have visibility of the both packages
+ throwException = filterSource || filterTarget;
+ }
+ if (throwException) {
throw new ParcelableException(new PackageManager.NameNotFoundException("Package(s) "
- + (sourceSetting == null ? sourcePackageName + " " : "")
- + (targetSetting == null ? targetPackageName + " " : "")
- + "not found."));
- }
- final boolean filterSource =
- shouldFilterApplication(sourceSetting, callingUid, userId);
- final boolean filterTarget =
- shouldFilterApplication(targetSetting, callingUid, userId);
- // The caller must have visibility of the both packages
- if (filterSource || filterTarget) {
- throw new ParcelableException(new PackageManager.NameNotFoundException("Package(s) "
- + (filterSource ? sourcePackageName + " " : "")
- + (filterTarget ? targetPackageName + " " : "")
- + "not found."));
+ + sourcePackageName + " and/or " + targetPackageName + " not found."));
}
final int sourcePackageUid = UserHandle.getUid(userId, sourceSetting.getAppId());
return !shouldFilterApplication(targetSetting, sourcePackageUid, userId);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 29b122d56ac5..aabe8a163df7 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static com.android.server.pm.ApexManager.ActiveApexInfo;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -46,6 +47,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -62,12 +64,16 @@ import com.android.server.pm.pkg.PackageStateInternal;
import dalvik.system.DexFile;
import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -315,10 +321,11 @@ final class DexOptHelper {
// The default is "true".
if (!"false".equals(DeviceConfig.getProperty("runtime", "dexopt_system_ui_on_boot"))) {
- // System UI is important to user experience, so we check it on every boot. It may need
- // to be re-compiled after a mainline update or an OTA.
- // TODO(b/227310505): Only do this after a mainline update or an OTA.
- checkAndDexOptSystemUi();
+ // System UI is important to user experience, so we check it after a mainline update or
+ // an OTA. It may need to be re-compiled in these cases.
+ if (hasBcpApexesChanged() || mPm.isDeviceUpgrading()) {
+ checkAndDexOptSystemUi();
+ }
}
// We need to re-extract after an OTA.
@@ -745,4 +752,42 @@ final class DexOptHelper {
/*package*/ void controlDexOptBlocking(boolean block) {
mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
}
+
+ /**
+ * Returns the module names of the APEXes that contribute to bootclasspath.
+ */
+ private static List<String> getBcpApexes() {
+ String bcp = System.getenv("BOOTCLASSPATH");
+ if (TextUtils.isEmpty(bcp)) {
+ Log.e(TAG, "Unable to get BOOTCLASSPATH");
+ return List.of();
+ }
+
+ ArrayList<String> bcpApexes = new ArrayList<>();
+ for (String pathStr : bcp.split(":")) {
+ Path path = Paths.get(pathStr);
+ // Check if the path is in the format of `/apex/<apex-module-name>/...` and extract the
+ // apex module name from the path.
+ if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) {
+ bcpApexes.add(path.getName(1).toString());
+ }
+ }
+
+ return bcpApexes;
+ }
+
+ /**
+ * Returns true of any of the APEXes that contribute to bootclasspath has changed during this
+ * boot.
+ */
+ private static boolean hasBcpApexesChanged() {
+ Set<String> bcpApexes = new HashSet<>(getBcpApexes());
+ ApexManager apexManager = ApexManager.getInstance();
+ for (ActiveApexInfo apexInfo : apexManager.getActiveApexInfos()) {
+ if (bcpApexes.contains(apexInfo.apexModuleName) && apexInfo.activeApexChanged) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/pm/FileInstallArgs.java b/services/core/java/com/android/server/pm/FileInstallArgs.java
index 85c3cc91ecf0..e3ceccd1abb8 100644
--- a/services/core/java/com/android/server/pm/FileInstallArgs.java
+++ b/services/core/java/com/android/server/pm/FileInstallArgs.java
@@ -172,9 +172,22 @@ class FileInstallArgs extends InstallArgs {
return false;
}
- if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
- Slog.w(TAG, "Failed to restorecon");
- return false;
+ if (onIncremental) {
+ Slog.i(TAG, PackageManagerServiceUtils.SELINUX_BUG
+ + ": Skipping restorecon for Incremental install of " + beforeCodeFile);
+ } else {
+ try {
+ if (!SELinux.restoreconRecursive(afterCodeFile)) {
+ Slog.w(TAG, "Failed to restorecon");
+ return false;
+ }
+ PackageManagerServiceUtils.verifySelinuxLabels(afterCodeFile.getAbsolutePath());
+ } catch (Exception e) {
+ Slog.e(TAG,
+ PackageManagerServiceUtils.SELINUX_BUG + ": Exception from restorecon on "
+ + beforeCodeFile, e);
+ throw e;
+ }
}
// Reflect the rename internally
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 57a1fe04b690..f909feed8045 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -648,6 +648,10 @@ final class InstallPackageHelper {
Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.mPkg);
}
+ if (res.mPkg != null) {
+ PackageManagerServiceUtils.verifySelinuxLabels(res.mPkg.getPath());
+ }
+
// A restore should be requested at this point if (a) the install
// succeeded, (b) the operation is not an update.
final boolean update = res.mRemovedInfo != null
@@ -3566,6 +3570,7 @@ final class InstallPackageHelper {
@ParsingPackageUtils.ParseFlags int parseFlags,
@PackageManagerService.ScanFlags int scanFlags,
@Nullable UserHandle user) throws PackageManagerException {
+ PackageManagerServiceUtils.verifySelinuxLabels(parsedPackage.getPath());
final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
parsedPackage, parseFlags, scanFlags, user);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 4d11b13510e9..703be169f14c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
@@ -60,6 +61,7 @@ import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
+import android.os.SELinux;
import android.os.SystemProperties;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -564,13 +566,8 @@ public class PackageManagerServiceUtils {
// the older ones. We check to see if either the new package is signed by an older cert
// with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
// with being sharedUser with the existing signing cert.
- boolean match =
- parsedSignatures.checkCapability(
- sharedUserSetting.getSigningDetails(),
- SigningDetails.CertCapabilities.SHARED_USER_ID)
- || sharedUserSetting.getSigningDetails().checkCapability(
- parsedSignatures,
- SigningDetails.CertCapabilities.SHARED_USER_ID);
+ boolean match = canJoinSharedUserId(parsedSignatures,
+ sharedUserSetting.getSigningDetails());
// Special case: if the sharedUserId capability check failed it could be due to this
// being the only package in the sharedUserId so far and the lineage being updated to
// deny the sharedUserId capability of the previous key in the lineage.
@@ -645,6 +642,28 @@ public class PackageManagerServiceUtils {
}
/**
+ * Returns whether the package with {@code packageSigningDetails} can join the sharedUserId
+ * with {@code sharedUserSigningDetails}.
+ * <p>
+ * A sharedUserId maintains a shared {@link SigningDetails} containing the full lineage and
+ * capabilities for each package in the sharedUserId. A package can join the sharedUserId if
+ * its current signer is the same as the shared signer, or if the current signer of either
+ * is in the signing lineage of the other with the {@link
+ * SigningDetails.CertCapabilities#SHARED_USER_ID} capability granted to that previous signer
+ * in the lineage.
+ *
+ * @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the
+ * sharedUserId
+ * @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId
+ * @return true if the package seeking to join the sharedUserId meets the requirements
+ */
+ public static boolean canJoinSharedUserId(@NonNull SigningDetails packageSigningDetails,
+ @NonNull SigningDetails sharedUserSigningDetails) {
+ return packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID)
+ || sharedUserSigningDetails.checkCapability(packageSigningDetails, SHARED_USER_ID);
+ }
+
+ /**
* Extract native libraries to a target path
*/
public static int extractNativeBinaries(File dstCodePath, String packageName) {
@@ -1388,4 +1407,28 @@ public class PackageManagerServiceUtils {
}
}
}
+
+ // TODO(b/231951809): remove this workaround after figuring out why apk_tmp_file labels stay
+ // on the installed apps instead of the correct apk_data_file ones
+
+ public static final String SELINUX_BUG = "b/231951809";
+
+ /**
+ * A workaround for b/231951809:
+ * Verifies the SELinux labels of the passed path, and tries to correct them if detects them
+ * wrong or missing.
+ */
+ public static void verifySelinuxLabels(String path) {
+ final String expectedCon = SELinux.fileSelabelLookup(path);
+ final String actualCon = SELinux.getFileContext(path);
+ Slog.i(TAG, SELINUX_BUG + ": checking selinux labels for " + path + " expected / actual: "
+ + expectedCon + " / " + actualCon);
+ if (expectedCon == null || !expectedCon.equals(actualCon)) {
+ Slog.w(TAG, SELINUX_BUG + ": labels don't match, reapplying for " + path);
+ if (!SELinux.restoreconRecursive(new File(path))) {
+ Slog.w(TAG, SELINUX_BUG + ": Failed to reapply restorecon");
+ }
+ // well, if it didn't work now after not working at first, not much else can be done
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5fc916f888f3..d6a133e43789 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -22,11 +22,9 @@ import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRI
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
-import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
-import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -212,12 +210,10 @@ final class ReconcilePackageUtils {
// the signatures on the first package scanned for the shared user (i.e. if the
// signaturesChanged state hasn't been initialized yet in SharedUserSetting).
if (sharedUserSetting != null) {
- final Signature[] sharedUserSignatures = sharedUserSetting
- .signatures.mSigningDetails.getSignatures();
if (sharedUserSetting.signaturesChanged != null
- && compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH) {
+ && !PackageManagerServiceUtils.canJoinSharedUserId(
+ parsedPackage.getSigningDetails(),
+ sharedUserSetting.getSigningDetails())) {
if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
// Mismatched signatures is an error and silently skipping system
// packages will likely break the device in unforeseen ways.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bcdf4291ed41..fcdab88a4c9b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4207,7 +4207,9 @@ public class UserManagerService extends IUserManager.Stub {
writeUserListLP();
}
updateUserIds();
- mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
+ Binder.withCleanCallingIdentity(() -> {
+ mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
+ });
dispatchUserAdded(preCreatedUser, token);
return preCreatedUser;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4075cddc302c..3c13abf4403c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -72,8 +72,8 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelDuration;
import android.os.PowerManager;
+import android.os.PowerManager.GoToSleepReason;
import android.os.PowerManager.ServiceType;
-import android.os.PowerManager.WakeData;
import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
@@ -352,7 +352,7 @@ public final class PowerManagerService extends SystemService
// Last reason the device went to sleep.
private @WakeReason int mLastGlobalWakeReason;
- private int mLastGlobalSleepReason;
+ private @GoToSleepReason int mLastGlobalSleepReason;
// Timestamp of last time power boost interaction was sent.
private long mLastInteractivePowerHintTime;
@@ -6350,20 +6350,26 @@ public final class PowerManagerService extends SystemService
}
}
+ @GoToSleepReason
private int getLastSleepReasonInternal() {
synchronized (mLock) {
return mLastGlobalSleepReason;
}
}
- @VisibleForTesting
private PowerManager.WakeData getLastWakeupInternal() {
synchronized (mLock) {
- return new WakeData(mLastGlobalWakeTime, mLastGlobalWakeReason,
+ return new PowerManager.WakeData(mLastGlobalWakeTime, mLastGlobalWakeReason,
mLastGlobalWakeTime - mLastGlobalSleepTime);
}
}
+ private PowerManager.SleepData getLastGoToSleepInternal() {
+ synchronized (mLock) {
+ return new PowerManager.SleepData(mLastGlobalSleepTime, mLastGlobalSleepReason);
+ }
+ }
+
/**
* If the user presses power while the proximity sensor is enabled and keeping
* the screen off, then turn the screen back on by telling display manager to
@@ -6528,11 +6534,16 @@ public final class PowerManagerService extends SystemService
}
@Override
- public WakeData getLastWakeup() {
+ public PowerManager.WakeData getLastWakeup() {
return getLastWakeupInternal();
}
@Override
+ public PowerManager.SleepData getLastGoToSleep() {
+ return getLastGoToSleepInternal();
+ }
+
+ @Override
public boolean interceptPowerKeyDown(KeyEvent event) {
return interceptPowerKeyDownInternal(event);
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 3e364314e10f..78b1c20ac4b2 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -71,8 +71,8 @@ final class Vibration {
IGNORED_SUPERSEDED,
}
- /** Start time in CLOCK_BOOTTIME base. */
- public final long startTime;
+ /** Start time using {@link SystemClock#uptimeMillis()}, for calculations. */
+ public final long startUptimeMillis;
public final VibrationAttributes attrs;
public final long id;
public final int uid;
@@ -94,11 +94,14 @@ final class Vibration {
/**
* Start/end times in unix epoch time. Only to be used for debugging purposes and to correlate
- * with other system events, any duration calculations should be done use {@link #startTime} so
- * as not to be affected by discontinuities created by RTC adjustments.
+ * with other system events, any duration calculations should be done use
+ * {@link #startUptimeMillis} so as not to be affected by discontinuities created by RTC
+ * adjustments.
*/
private final long mStartTimeDebug;
private long mEndTimeDebug;
+ /** End time using {@link SystemClock#uptimeMillis()}, for calculations. */
+ private long mEndUptimeMillis;
private Status mStatus;
/** A {@link CountDownLatch} to enable waiting for completion. */
@@ -109,7 +112,7 @@ final class Vibration {
this.token = token;
this.mEffect = effect;
this.id = id;
- this.startTime = SystemClock.elapsedRealtime();
+ this.startUptimeMillis = SystemClock.uptimeMillis();
this.attrs = attrs;
this.uid = uid;
this.opPkg = opPkg;
@@ -131,6 +134,7 @@ final class Vibration {
return;
}
mStatus = status;
+ mEndUptimeMillis = SystemClock.uptimeMillis();
mEndTimeDebug = System.currentTimeMillis();
mCompletionLatch.countDown();
}
@@ -225,15 +229,17 @@ final class Vibration {
/** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */
public Vibration.DebugInfo getDebugInfo() {
+ long durationMs = hasEnded() ? mEndUptimeMillis - startUptimeMillis : -1;
return new Vibration.DebugInfo(
- mStartTimeDebug, mEndTimeDebug, mEffect, mOriginalEffect, /* scale= */ 0, attrs,
- uid, opPkg, reason, mStatus);
+ mStartTimeDebug, mEndTimeDebug, durationMs, mEffect, mOriginalEffect,
+ /* scale= */ 0, attrs, uid, opPkg, reason, mStatus);
}
/** Debug information about vibrations. */
static final class DebugInfo {
private final long mStartTimeDebug;
private final long mEndTimeDebug;
+ private final long mDurationMs;
private final CombinedVibration mEffect;
private final CombinedVibration mOriginalEffect;
private final float mScale;
@@ -243,11 +249,12 @@ final class Vibration {
private final String mReason;
private final Status mStatus;
- DebugInfo(long startTimeDebug, long endTimeDebug, CombinedVibration effect,
- CombinedVibration originalEffect, float scale, VibrationAttributes attrs,
- int uid, String opPkg, String reason, Status status) {
+ DebugInfo(long startTimeDebug, long endTimeDebug, long durationMs,
+ CombinedVibration effect, CombinedVibration originalEffect, float scale,
+ VibrationAttributes attrs, int uid, String opPkg, String reason, Status status) {
mStartTimeDebug = startTimeDebug;
mEndTimeDebug = endTimeDebug;
+ mDurationMs = durationMs;
mEffect = effect;
mOriginalEffect = originalEffect;
mScale = scale;
@@ -266,6 +273,8 @@ final class Vibration {
.append(", endTime: ")
.append(mEndTimeDebug == 0 ? null
: DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
+ .append(", durationMs: ")
+ .append(mDurationMs)
.append(", status: ")
.append(mStatus.name().toLowerCase())
.append(", effect: ")
@@ -290,6 +299,7 @@ final class Vibration {
final long token = proto.start(fieldId);
proto.write(VibrationProto.START_TIME, mStartTimeDebug);
proto.write(VibrationProto.END_TIME, mEndTimeDebug);
+ proto.write(VibrationProto.DURATION_MS, mDurationMs);
proto.write(VibrationProto.STATUS, mStatus.ordinal());
final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index ac635a0746c5..f9ffd92a7a1c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -52,6 +52,7 @@ import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
import android.os.vibrator.VibrationConfig;
import android.provider.Settings;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
@@ -121,6 +122,19 @@ final class VibrationSettings {
USAGE_PHYSICAL_EMULATION,
USAGE_HARDWARE_FEEDBACK));
+ /**
+ * Set of reasons for {@link PowerManager} going to sleep events that allows vibrations to
+ * continue running.
+ *
+ * <p>Some examples are timeout and inattentive, which indicates automatic screen off events.
+ * When a vibration is playing during one of these screen off events then it will not be
+ * cancelled by the service.
+ */
+ private static final Set<Integer> POWER_MANAGER_SLEEP_REASON_ALLOWLIST = new HashSet<>(
+ Arrays.asList(
+ PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT));
+
private static final IntentFilter USER_SWITCHED_INTENT_FILTER =
new IntentFilter(Intent.ACTION_USER_SWITCHED);
private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
@@ -135,7 +149,8 @@ final class VibrationSettings {
private final Object mLock = new Object();
private final Context mContext;
private final String mSystemUiPackage;
- private final SettingsContentObserver mSettingObserver;
+ @VisibleForTesting
+ final SettingsContentObserver mSettingObserver;
@VisibleForTesting
final UidObserver mUidObserver;
@VisibleForTesting
@@ -150,6 +165,9 @@ final class VibrationSettings {
@GuardedBy("mLock")
@Nullable
private AudioManager mAudioManager;
+ @GuardedBy("mLock")
+ @Nullable
+ private PowerManagerInternal mPowerManagerInternal;
@GuardedBy("mLock")
private boolean mVibrateInputDevices;
@@ -199,10 +217,16 @@ final class VibrationSettings {
}
public void onSystemReady() {
+ PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ int ringerMode = am.getRingerModeInternal();
+
synchronized (mLock) {
- mAudioManager = mContext.getSystemService(AudioManager.class);
- mRingerMode = mAudioManager.getRingerModeInternal();
+ mPowerManagerInternal = pm;
+ mAudioManager = am;
+ mRingerMode = ringerMode;
}
+
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
@@ -211,7 +235,6 @@ final class VibrationSettings {
// ignored; both services live in system_server
}
- PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
pm.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
@@ -381,7 +404,27 @@ final class VibrationSettings {
* @return true if the vibration should be cancelled when the screen goes off, false otherwise.
*/
public boolean shouldCancelVibrationOnScreenOff(int uid, String opPkg,
- @VibrationAttributes.Usage int usage) {
+ @VibrationAttributes.Usage int usage, long vibrationStartUptimeMillis) {
+ PowerManagerInternal pm;
+ synchronized (mLock) {
+ pm = mPowerManagerInternal;
+ }
+ if (pm != null) {
+ // The SleepData from PowerManager may refer to a more recent sleep than the broadcast
+ // that triggered this method call. That's ok because only automatic sleeps would be
+ // ignored here and not cancel a vibration, and those are usually triggered by timeout
+ // or inactivity, so it's unlikely that it will override a more active goToSleep reason.
+ PowerManager.SleepData sleepData = pm.getLastGoToSleep();
+ if ((sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis)
+ || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason)) {
+ // Ignore screen off events triggered before the vibration started, and all
+ // automatic "go to sleep" events from allowlist.
+ Slog.d(TAG, "Ignoring screen off event triggered at uptime "
+ + sleepData.goToSleepUptimeMillis + " for reason "
+ + PowerManager.sleepReasonToString(sleepData.goToSleepReason));
+ return false;
+ }
+ }
if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(usage)) {
// Usages not allowed even for system vibrations should always be cancelled.
return true;
@@ -628,7 +671,8 @@ final class VibrationSettings {
}
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
- private final class SettingsContentObserver extends ContentObserver {
+ @VisibleForTesting
+ final class SettingsContentObserver extends ContentObserver {
SettingsContentObserver(Handler handler) {
super(handler);
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index d7341cb37685..f0911ca62027 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -47,6 +47,7 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -405,7 +406,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
// Force update of user settings before checking if this vibration effect should
// be ignored or scaled.
- mVibrationSettings.update();
+ mVibrationSettings.mSettingObserver.onChange(false);
}
synchronized (mLock) {
@@ -1103,7 +1104,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
Vibration vib = conductor.getVibration();
return mVibrationSettings.shouldCancelVibrationOnScreenOff(
- vib.uid, vib.opPkg, vib.attrs.getUsage());
+ vib.uid, vib.opPkg, vib.attrs.getUsage(), vib.startUptimeMillis);
}
@GuardedBy("mLock")
@@ -1308,13 +1309,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public final ExternalVibration externalVibration;
public int scale;
+ private final long mStartUptimeMillis;
private final long mStartTimeDebug;
+
+ private long mEndUptimeMillis;
private long mEndTimeDebug;
private Vibration.Status mStatus;
private ExternalVibrationHolder(ExternalVibration externalVibration) {
this.externalVibration = externalVibration;
this.scale = IExternalVibratorService.SCALE_NONE;
+ mStartUptimeMillis = SystemClock.uptimeMillis();
mStartTimeDebug = System.currentTimeMillis();
mStatus = Vibration.Status.RUNNING;
}
@@ -1325,6 +1330,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return;
}
mStatus = status;
+ mEndUptimeMillis = SystemClock.uptimeMillis();
mEndTimeDebug = System.currentTimeMillis();
}
@@ -1341,11 +1347,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
public Vibration.DebugInfo getDebugInfo() {
+ long durationMs = mEndUptimeMillis == 0 ? -1 : mEndUptimeMillis - mStartUptimeMillis;
return new Vibration.DebugInfo(
- mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
- scale, externalVibration.getVibrationAttributes(),
- externalVibration.getUid(), externalVibration.getPackage(),
- /* reason= */ null, mStatus);
+ mStartTimeDebug, mEndTimeDebug, durationMs,
+ /* effect= */ null, /* originalEffect= */ null, scale,
+ externalVibration.getVibrationAttributes(), externalVibration.getUid(),
+ externalVibration.getPackage(), /* reason= */ null, mStatus);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index f21f90631792..f6748de660e2 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -246,6 +246,18 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
+ public void activityLocalRelaunch(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ r.startRelaunching();
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
public void activityRelaunched(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 189fff865ea0..9fa68e959810 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2314,7 +2314,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
allowTaskSnapshot, activityCreated, activityAllDrawn, snapshot);
- //TODO(191787740) Remove for T
+ //TODO(191787740) Remove for T+
final boolean useLegacy = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN
&& mWmService.mStartingSurfaceController.isExceptionApp(packageName, mTargetSdk,
() -> {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 5c8cfffdd3b3..9e0d7b57264e 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -46,6 +46,7 @@ import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.StatusBarManager;
import android.app.WindowConfiguration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.IntArray;
@@ -123,14 +124,17 @@ class InsetsPolicy {
* Let remote insets controller control system bars regardless of other settings.
*/
private boolean mRemoteInsetsControllerControlsSystemBars;
+ private final boolean mHideNavBarForKeyboard;
private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
mDisplayContent = displayContent;
mPolicy = displayContent.getDisplayPolicy();
- mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
+ final Resources r = mPolicy.getContext().getResources();
+ mRemoteInsetsControllerControlsSystemBars = r.getBoolean(
R.bool.config_remoteInsetsControllerControlsSystemBars);
+ mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
}
boolean getRemoteInsetsControllerControlsSystemBars() {
@@ -428,13 +432,15 @@ class InsetsPolicy {
private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
boolean copyState) {
if (w.mIsImWindow) {
- // Navigation bar insets is always visible to IME.
+ // If navigation bar is not hidden by IME, IME should always receive visible
+ // navigation bar insets.
+ final boolean navVisible = !mHideNavBarForKeyboard;
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
- if (originalNavSource != null && !originalNavSource.isVisible()) {
+ if (originalNavSource != null && originalNavSource.isVisible() != navVisible) {
final InsetsState state = copyState ? new InsetsState(originalState)
: originalState;
final InsetsSource navSource = new InsetsSource(originalNavSource);
- navSource.setVisible(true);
+ navSource.setVisible(navVisible);
state.addSource(navSource);
return state;
}
@@ -573,8 +579,9 @@ class InsetsPolicy {
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
boolean fake) {
final WindowState imeWin = mDisplayContent.mInputMethodWindow;
- if (imeWin != null && imeWin.isVisible()) {
- // Force showing navigation bar while IME is visible.
+ if (imeWin != null && imeWin.isVisible() && !mHideNavBarForKeyboard) {
+ // Force showing navigation bar while IME is visible and if navigation bar is not
+ // configured to be hidden by the IME.
return null;
}
if (!fake && isShowingTransientTypes(Type.navigationBars())) {
diff --git a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
index 9ca49fe9557e..b3cd3f0df97e 100644
--- a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
+++ b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
@@ -70,7 +70,7 @@ class SplashScreenExceptionList {
}
/**
- * Returns true if the packageName is in the list and the target sdk is before S.
+ * Returns true if the packageName is in the list and the target sdk is before or including T.
*
* @param packageName The package name of the application to check
* @param targetSdk The target sdk of the application
@@ -82,7 +82,7 @@ class SplashScreenExceptionList {
@SuppressWarnings("AndroidFrameworkCompatChange") // Target sdk check
public boolean isException(@NonNull String packageName, int targetSdk,
@Nullable Supplier<ApplicationInfo> infoSupplier) {
- if (targetSdk >= Build.VERSION_CODES.S) {
+ if (targetSdk > Build.VERSION_CODES.TIRAMISU) {
return false;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 8a954caad939..5f9f1b226958 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -233,27 +233,34 @@ public class PrefetchControllerTest {
@Test
public void testConstantsUpdating_ValidValues() {
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 5 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 5 * MINUTE_IN_MILLIS);
assertEquals(5 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs());
+ assertEquals(5 * MINUTE_IN_MILLIS, mPrefetchController.getLaunchTimeAllowanceMs());
}
@Test
public void testConstantsUpdating_InvalidValues() {
// Test negatives/too low.
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 4 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, -MINUTE_IN_MILLIS);
assertEquals(HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs());
+ assertEquals(0, mPrefetchController.getLaunchTimeAllowanceMs());
// Test larger than a day. Controller should cap at one day.
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 5 * HOUR_IN_MILLIS);
assertEquals(24 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs());
+ assertEquals(2 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeAllowanceMs());
}
@Test
public void testConstantsUpdating_ThresholdChangesAlarms() {
final long launchDelayMs = 11 * HOUR_IN_MILLIS;
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0);
when(mUsageStatsManagerInternal
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
.thenReturn(sSystemClock.millis() + launchDelayMs);
@@ -276,6 +283,7 @@ public class PrefetchControllerTest {
@Test
public void testConstraintNotSatisfiedWhenLaunchLate() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0);
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
final JobStatus job = createJobStatus("testConstraintNotSatisfiedWhenLaunchLate", 1);
@@ -290,6 +298,8 @@ public class PrefetchControllerTest {
@Test
public void testConstraintSatisfiedWhenLaunchSoon() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0);
+
final JobStatus job = createJobStatus("testConstraintSatisfiedWhenLaunchSoon", 2);
when(mUsageStatsManagerInternal
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
@@ -338,6 +348,8 @@ public class PrefetchControllerTest {
@Test
public void testConstraintSatisfiedWhenWidget() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0);
+
final JobStatus jobNonWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 1);
final JobStatus jobWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 2);
@@ -365,6 +377,7 @@ public class PrefetchControllerTest {
@Test
public void testEstimatedLaunchTimeChangedToLate() {
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0);
when(mUsageStatsManagerInternal
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
.thenReturn(sSystemClock.millis() + HOUR_IN_MILLIS);
@@ -393,6 +406,7 @@ public class PrefetchControllerTest {
@Test
public void testEstimatedLaunchTimeChangedToSoon() {
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 0);
when(mUsageStatsManagerInternal
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
.thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
@@ -413,4 +427,36 @@ public class PrefetchControllerTest {
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
}
+
+ @Test
+ public void testEstimatedLaunchTimeAllowance() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_ALLOWANCE_MS, 15 * MINUTE_IN_MILLIS);
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
+
+ InOrder inOrder = inOrder(mUsageStatsManagerInternal);
+
+ JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeAllowance", 1);
+ trackJobs(jobStatus);
+ inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ // The allowance shouldn't shift the alarm
+ verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
+ .setWindow(
+ anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
+ anyLong(), eq(TAG_PREFETCH), any(), any());
+ assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+
+ mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
+ SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS);
+
+ inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
+ assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+
+ sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 8f5b0e19f07e..ab292ab5381e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -546,6 +546,18 @@ public class ApexManagerTest {
assertThat(backingApexFile).isNull();
}
+ @Test
+ public void testActiveApexChanged() throws RemoteException {
+ ApexInfo apex1 = createApexInfo(
+ "com.apex1", 37, true, true, new File("/data/apex/active/com.apex@37.apex"));
+ apex1.activeApexChanged = true;
+ apex1.preinstalledModulePath = apex1.modulePath;
+ when(mApexService.getActivePackages()).thenReturn(new ApexInfo[]{apex1});
+ final ApexManager.ActiveApexInfo activeApex = mApexManager.getActiveApexInfos().get(0);
+ assertThat(activeApex.apexModuleName).isEqualTo("com.apex1");
+ assertThat(activeApex.activeApexChanged).isTrue();
+ }
+
private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) {
File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME);
ApexInfo apexInfo = new ApexInfo();
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
index 7974718512a8..9674ebd75677 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
@@ -22,7 +22,7 @@ import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,6 +35,8 @@ import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -74,7 +76,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Executor;
@Presubmit
@RunWith(JUnit4.class)
@@ -103,7 +104,7 @@ public class AppsFilterImplTest {
@Mock
PackageDataSnapshot mSnapshot;
@Mock
- Executor mMockExecutor;
+ Handler mMockHandler;
@Mock
PackageManagerInternal mPmInternal;
@@ -210,10 +211,12 @@ public class AppsFilterImplTest {
when(mSnapshot.getUserInfos()).thenReturn(USER_INFO_LIST);
when(mPmInternal.snapshot()).thenReturn(mSnapshot);
- doAnswer(invocation -> {
- ((Runnable) invocation.getArgument(0)).run();
- return new Object();
- }).when(mMockExecutor).execute(any(Runnable.class));
+ // Can't mock postDelayed because of some weird bug in Mockito.
+ when(mMockHandler.sendMessageDelayed(any(Message.class), anyLong())).thenAnswer(
+ invocation -> {
+ ((Message) invocation.getArgument(0)).getCallback().run();
+ return null;
+ });
when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer(
@@ -226,7 +229,7 @@ public class AppsFilterImplTest {
public void testSystemReadyPropogates() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
appsFilter.onSystemReady(mPmInternal);
@@ -238,7 +241,7 @@ public class AppsFilterImplTest {
public void testQueriesAction_FilterMatches() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -263,7 +266,7 @@ public class AppsFilterImplTest {
public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -312,7 +315,7 @@ public class AppsFilterImplTest {
public void testQueriesProvider_FilterMatches() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -337,7 +340,7 @@ public class AppsFilterImplTest {
public void testOnUserUpdated_FilterMatches() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -385,7 +388,7 @@ public class AppsFilterImplTest {
public void testQueriesDifferentProvider_Filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -410,7 +413,7 @@ public class AppsFilterImplTest {
public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -429,7 +432,7 @@ public class AppsFilterImplTest {
public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -446,7 +449,7 @@ public class AppsFilterImplTest {
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -468,7 +471,7 @@ public class AppsFilterImplTest {
public void testNoQueries_Filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -486,7 +489,7 @@ public class AppsFilterImplTest {
public void testNoUsesLibrary_Filters() throws Exception {
final AppsFilterImpl appsFilter = new AppsFilterImpl(mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -513,7 +516,7 @@ public class AppsFilterImplTest {
public void testUsesLibrary_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter = new AppsFilterImpl(mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -541,7 +544,7 @@ public class AppsFilterImplTest {
public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter = new AppsFilterImpl(mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -569,7 +572,7 @@ public class AppsFilterImplTest {
public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter = new AppsFilterImpl(mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -602,7 +605,7 @@ public class AppsFilterImplTest {
public void testForceQueryable_SystemDoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -622,7 +625,7 @@ public class AppsFilterImplTest {
public void testForceQueryable_NonSystemFilters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -641,7 +644,7 @@ public class AppsFilterImplTest {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock,
new String[]{"com.some.package"}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -661,7 +664,7 @@ public class AppsFilterImplTest {
public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
appsFilter.onSystemReady(mPmInternal);
final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -692,7 +695,7 @@ public class AppsFilterImplTest {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock,
new String[]{"com.some.package"}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -711,7 +714,7 @@ public class AppsFilterImplTest {
public void testSystemQueryable_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{},
- true /* system force queryable */, null, mMockExecutor);
+ true /* system force queryable */, null, mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -730,7 +733,7 @@ public class AppsFilterImplTest {
public void testQueriesPackage_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -750,7 +753,7 @@ public class AppsFilterImplTest {
.thenReturn(false);
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -768,7 +771,7 @@ public class AppsFilterImplTest {
public void testSystemUid_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -785,7 +788,7 @@ public class AppsFilterImplTest {
public void testSystemUidSecondaryUser_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -803,7 +806,7 @@ public class AppsFilterImplTest {
public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -818,7 +821,7 @@ public class AppsFilterImplTest {
public void testNoTargetPackage_filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -876,7 +879,7 @@ public class AppsFilterImplTest {
return Collections.emptyMap();
}
},
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -966,7 +969,7 @@ public class AppsFilterImplTest {
return Collections.emptyMap();
}
},
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -990,7 +993,7 @@ public class AppsFilterImplTest {
public void testInitiatingApp_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -1009,7 +1012,7 @@ public class AppsFilterImplTest {
public void testUninstalledInitiatingApp_Filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
@@ -1028,7 +1031,7 @@ public class AppsFilterImplTest {
public void testOriginatingApp_Filters() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1054,7 +1057,7 @@ public class AppsFilterImplTest {
public void testInstallingApp_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1080,7 +1083,7 @@ public class AppsFilterImplTest {
public void testInstrumentation_DoesntFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1109,7 +1112,7 @@ public class AppsFilterImplTest {
public void testWhoCanSee() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1184,7 +1187,7 @@ public class AppsFilterImplTest {
public void testOnChangeReport() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1259,7 +1262,7 @@ public class AppsFilterImplTest {
public void testOnChangeReportedFilter() throws Exception {
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter");
@@ -1286,7 +1289,7 @@ public class AppsFilterImplTest {
when(mFeatureConfigMock.snapshot()).thenReturn(mFeatureConfigMock);
final AppsFilterImpl appsFilter =
new AppsFilterImpl(mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockHandler);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady(mPmInternal);
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 8167b44ee59d..758a56f3d2ad 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -360,67 +360,6 @@ public class SystemConfigTest {
.containsExactly("android.permission.BAR");
}
- @Test
- public void pruneVendorApexPrivappAllowlists_removeVendor()
- throws Exception {
- File apexDir = createTempSubfolder("apex");
-
- // Read non-vendor apex permission allowlists
- final String allowlistNonVendorContents =
- "<privapp-permissions package=\"com.android.apk_in_non_vendor_apex\">"
- + "<permission name=\"android.permission.FOO\"/>"
- + "<deny-permission name=\"android.permission.BAR\"/>"
- + "</privapp-permissions>";
- File nonVendorPermDir =
- createTempSubfolder("apex/com.android.non_vendor/etc/permissions");
- File nonVendorPermissionFile =
- createTempFile(nonVendorPermDir, "permissions.xml", allowlistNonVendorContents);
- XmlPullParser nonVendorParser = readXmlUntilStartTag(nonVendorPermissionFile);
- mSysConfig.readApexPrivAppPermissions(nonVendorParser, nonVendorPermissionFile,
- apexDir.toPath());
-
- // Read vendor apex permission allowlists
- final String allowlistVendorContents =
- "<privapp-permissions package=\"com.android.apk_in_vendor_apex\">"
- + "<permission name=\"android.permission.BAZ\"/>"
- + "<deny-permission name=\"android.permission.BAT\"/>"
- + "</privapp-permissions>";
- File vendorPermissionFile =
- createTempFile(createTempSubfolder("apex/com.android.vendor/etc/permissions"),
- "permissions.xml", allowlistNonVendorContents);
- XmlPullParser vendorParser = readXmlUntilStartTag(vendorPermissionFile);
- mSysConfig.readApexPrivAppPermissions(vendorParser, vendorPermissionFile,
- apexDir.toPath());
-
- // Read allowed vendor apex list
- final String allowedVendorContents =
- "<config>\n"
- + " <allowed-vendor-apex package=\"com.android.vendor\" "
- + "installerPackage=\"com.installer\" />\n"
- + "</config>";
- final File allowedVendorFolder = createTempSubfolder("folder");
- createTempFile(allowedVendorFolder, "vendor-apex-allowlist.xml", allowedVendorContents);
- readPermissions(allowedVendorFolder, /* Grant all permission flags */ ~0);
-
- // Finally, prune non-vendor allowlists.
- // There is no guarantee in which order the above reads will be done, however pruning
- // will always happen last.
- mSysConfig.pruneVendorApexPrivappAllowlists();
-
- assertThat(mSysConfig.getApexPrivAppPermissions("com.android.non_vendor",
- "com.android.apk_in_non_vendor_apex"))
- .containsExactly("android.permission.FOO");
- assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.non_vendor",
- "com.android.apk_in_non_vendor_apex"))
- .containsExactly("android.permission.BAR");
- assertThat(mSysConfig.getApexPrivAppPermissions("com.android.vendor",
- "com.android.apk_in_vendor_apex"))
- .isNull();
- assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.vendor",
- "com.android.apk_in_vendor_apex"))
- .isNull();
- }
-
/**
* Tests that readPermissions works correctly for a library with on-bootclasspath-before
* and on-bootclasspath-since.
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index b907c62be6fb..64950aa69e3b 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -277,6 +277,6 @@ public class VibrationScalerTest {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
- mVibrationSettings.update();
+ mVibrationSettings.mSettingObserver.onChange(false);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 0a50e790215f..b214dd0ad2ba 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -54,6 +54,7 @@ import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
@@ -146,6 +147,8 @@ public class VibrationSettingsTest {
mVibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
+ mockGoToSleep(/* goToSleepTime= */ 0, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+
// Simulate System defaults.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
@@ -164,18 +167,10 @@ public class VibrationSettingsTest {
public void addListener_settingsChangeTriggerListener() {
mVibrationSettings.addListener(mListenerMock);
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
- setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ mVibrationSettings.mSettingObserver.onChange(false);
+ mVibrationSettings.mSettingObserver.onChange(false);
- verify(mListenerMock, times(10)).onChange();
+ verify(mListenerMock, times(2)).onChange();
}
@Test
@@ -479,50 +474,112 @@ public class VibrationSettingsTest {
}
@Test
- public void shouldCancelVibrationOnScreenOff_withNonSystemPackageAndUid_returnsAlwaysTrue() {
+ public void shouldCancelVibrationOnScreenOff_withEventBeforeVibration_returnsAlwaysFalse() {
+ long vibrateStartTime = 100;
+ mockGoToSleep(vibrateStartTime - 10, PowerManager.GO_TO_SLEEP_REASON_APPLICATION);
+
+ for (int usage : ALL_USAGES) {
+ // Non-system vibration
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, "some.app", usage, vibrateStartTime));
+ // Vibration with UID zero
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ /* uid= */ 0, "", usage, vibrateStartTime));
+ // System vibration
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ Process.SYSTEM_UID, "", usage, vibrateStartTime));
+ // SysUI vibration
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
+ }
+ }
+
+ @Test
+ public void shouldCancelVibrationOnScreenOff_withSleepReasonInAllowlist_returnsAlwaysFalse() {
+ long vibrateStartTime = 100;
+ int[] allowedSleepReasons = new int[] {
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+ PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
+ };
+
+ for (int sleepReason : allowedSleepReasons) {
+ mockGoToSleep(vibrateStartTime + 10, sleepReason);
+
+ for (int usage : ALL_USAGES) {
+ // Non-system vibration
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, "some.app", usage, vibrateStartTime));
+ // Vibration with UID zero
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ /* uid= */ 0, "", usage, vibrateStartTime));
+ // System vibration
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ Process.SYSTEM_UID, "", usage, vibrateStartTime));
+ // SysUI vibration
+ assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
+ }
+ }
+ }
+
+ @Test
+ public void shouldCancelVibrationOnScreenOff_withNonSystem_returnsTrueIfReasonNotInAllowlist() {
+ long vibrateStartTime = 100;
+ mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
+
for (int usage : ALL_USAGES) {
- assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(UID, "some.app", usage));
+ assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
+ UID, "some.app", usage, vibrateStartTime));
}
}
@Test
public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() {
+ long vibrateStartTime = 100;
+ mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN);
+
for (int usage : ALL_USAGES) {
if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
|| usage == USAGE_PHYSICAL_EMULATION) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- /* uid= */ 0, "", usage));
+ /* uid= */ 0, "", usage, vibrateStartTime));
} else {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- /* uid= */ 0, "", usage));
+ /* uid= */ 0, "", usage, vibrateStartTime));
}
}
}
@Test
public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() {
+ long vibrateStartTime = 100;
+ mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD);
+
for (int usage : ALL_USAGES) {
if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
|| usage == USAGE_PHYSICAL_EMULATION) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- Process.SYSTEM_UID, "", usage));
+ Process.SYSTEM_UID, "", usage, vibrateStartTime));
} else {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- Process.SYSTEM_UID, "", usage));
+ Process.SYSTEM_UID, "", usage, vibrateStartTime));
}
}
}
@Test
- public void shouldCancelVibrationOnScreenOff_withSysUi_returnsFalseForTouchAndHardware() {
+ public void shouldCancelVibrationOnScreenOff_withSysUiPkg_returnsFalseForTouchAndHardware() {
+ long vibrateStartTime = 100;
+ mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_HDMI);
+
for (int usage : ALL_USAGES) {
if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
|| usage == USAGE_PHYSICAL_EMULATION) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, SYSUI_PACKAGE_NAME, usage));
+ UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
} else {
assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
- UID, SYSUI_PACKAGE_NAME, usage));
+ UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime));
}
}
}
@@ -581,7 +638,6 @@ public class VibrationSettingsTest {
public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- mVibrationSettings.update();
assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
// If haptic feedback is off, fallback to default value.
assertEquals(VIBRATION_INTENSITY_MEDIUM,
@@ -590,7 +646,6 @@ public class VibrationSettingsTest {
mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
- mVibrationSettings.update();
assertEquals(VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
// If haptic feedback is on, fallback to that value.
@@ -648,19 +703,25 @@ public class VibrationSettingsTest {
Settings.System.putStringForUser(
mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
- mVibrationSettings.update();
+ mVibrationSettings.mSettingObserver.onChange(false);
}
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
- mVibrationSettings.update();
+ mVibrationSettings.mSettingObserver.onChange(false);
}
private void setRingerMode(int ringerMode) {
mAudioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
- mVibrationSettings.update();
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+ }
+
+ private void mockGoToSleep(long sleepTime, int reason) {
+ when(mPowerManagerInternalMock.getLastGoToSleep()).thenReturn(
+ new PowerManager.SleepData(sleepTime, reason));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a34896a419ed..12e565394926 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2630,7 +2630,11 @@ public class ActivityRecordTests extends WindowTestsBase {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
"splash_screen_exception_list", DEFAULT_COMPONENT_PACKAGE_NAME, false);
testLegacySplashScreen(Build.VERSION_CODES.R, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
- testLegacySplashScreen(Build.VERSION_CODES.S, 0);
+ testLegacySplashScreen(Build.VERSION_CODES.S, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
+ testLegacySplashScreen(Build.VERSION_CODES.TIRAMISU,
+ TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
+ // Above T
+ testLegacySplashScreen(Build.VERSION_CODES.TIRAMISU + 1, 0);
} finally {
try {
DeviceConfig.setProperties(properties);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
index f5d915dee257..8425844d8042 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
@@ -90,30 +90,19 @@ public class SplashScreenExceptionListTest {
public void packageFromDeviceConfigIgnored() {
setExceptionListAndWaitForCallback("com.test.nosplashscreen1,com.test.nosplashscreen2");
- assertIsException("com.test.nosplashscreen1", null);
- assertIsException("com.test.nosplashscreen2", null);
-
- assertIsNotException("com.test.nosplashscreen1", VERSION_CODES.S, null);
- assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.S, null);
- assertIsNotException("com.test.splashscreen", VERSION_CODES.S, null);
- assertIsNotException("com.test.splashscreen", VERSION_CODES.R, null);
- }
-
- private void setExceptionListAndWaitForCallback(String commaSeparatedList) {
- CountDownLatch latch = new CountDownLatch(1);
- mOnUpdateDeviceConfig = rawList -> {
- if (commaSeparatedList.equals(rawList)) {
- latch.countDown();
- }
- };
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false);
- try {
- assertTrue("Timed out waiting for DeviceConfig to be updated.",
- latch.await(1, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- Assert.fail(e.getMessage());
- }
+ // In list, up to T included
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.R);
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.S);
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.TIRAMISU);
+
+ // In list, after T
+ assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.TIRAMISU + 1);
+ assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.CUR_DEVELOPMENT);
+
+ // Not in list, up to T included
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.S);
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.R);
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.TIRAMISU);
}
@Test
@@ -129,29 +118,60 @@ public class SplashScreenExceptionListTest {
metaData.putBoolean("android.splashscreen.exception_opt_out", true);
assertIsNotException(packageName, VERSION_CODES.R, activityInfo);
assertIsNotException(packageName, VERSION_CODES.S, activityInfo);
+ assertIsNotException(packageName, VERSION_CODES.TIRAMISU, activityInfo);
- // Exception Pre S
+ // Exception up to T
metaData.putBoolean("android.splashscreen.exception_opt_out", false);
- assertIsException(packageName, activityInfo);
- assertIsNotException(packageName, VERSION_CODES.S, activityInfo);
+ assertIsException(packageName, VERSION_CODES.R, activityInfo);
+ assertIsException(packageName, VERSION_CODES.S, activityInfo);
+ assertIsException(packageName, VERSION_CODES.TIRAMISU, activityInfo);
+
+ // No Exception after T
+ assertIsNotException(packageName, VERSION_CODES.TIRAMISU + 1, activityInfo);
+ assertIsNotException(packageName, VERSION_CODES.CUR_DEVELOPMENT, activityInfo);
// Edge Cases
activityInfo.metaData = null;
- assertIsException(packageName, activityInfo);
- assertIsException(packageName, null);
+ assertIsException(packageName, VERSION_CODES.R, activityInfo);
+ assertIsException(packageName, VERSION_CODES.R);
+ }
+
+ private void setExceptionListAndWaitForCallback(String commaSeparatedList) {
+ CountDownLatch latch = new CountDownLatch(1);
+ mOnUpdateDeviceConfig = rawList -> {
+ if (commaSeparatedList.equals(rawList)) {
+ latch.countDown();
+ }
+ };
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false);
+ try {
+ assertTrue("Timed out waiting for DeviceConfig to be updated.",
+ latch.await(1, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ Assert.fail(e.getMessage());
+ }
+ }
+
+ private void assertIsNotException(String packageName, int targetSdk) {
+ assertIsNotException(packageName, targetSdk, null);
}
private void assertIsNotException(String packageName, int targetSdk,
ApplicationInfo activityInfo) {
assertFalse(String.format("%s (sdk=%d) should have not been considered as an exception",
- packageName, targetSdk),
+ packageName, targetSdk),
mList.isException(packageName, targetSdk, () -> activityInfo));
}
+ private void assertIsException(String packageName, int targetSdk) {
+ assertIsException(packageName, targetSdk, null);
+ }
+
private void assertIsException(String packageName,
- ApplicationInfo activityInfo) {
+ int targetSdk, ApplicationInfo activityInfo) {
assertTrue(String.format("%s (sdk=%d) should have been considered as an exception",
- packageName, VERSION_CODES.R),
- mList.isException(packageName, VERSION_CODES.R, () -> activityInfo));
+ packageName, targetSdk),
+ mList.isException(packageName, targetSdk, () -> activityInfo));
}
}
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 6c20e746f35e..26d9b10c7907 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -3,4 +3,5 @@ varunshah@google.com
huiyu@google.com
yamasani@google.com
-per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS \ No newline at end of file
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
+per-file *Broadcast* = sudheersai@google.com \ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/BroadcastEvent.java b/services/usage/java/com/android/server/usage/BroadcastEvent.java
index ceb79c107d30..e56a541078e6 100644
--- a/services/usage/java/com/android/server/usage/BroadcastEvent.java
+++ b/services/usage/java/com/android/server/usage/BroadcastEvent.java
@@ -19,6 +19,7 @@ package com.android.server.usage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.util.LongArrayQueue;
import java.util.Objects;
@@ -30,6 +31,7 @@ class BroadcastEvent {
private String mTargetPackage;
private int mTargetUserId;
private long mIdForResponseEvent;
+ private final LongArrayQueue mTimestampsMs;
BroadcastEvent(int sourceUid, @NonNull String targetPackage, @UserIdInt int targetUserId,
long idForResponseEvent) {
@@ -37,6 +39,7 @@ class BroadcastEvent {
mTargetPackage = targetPackage;
mTargetUserId = targetUserId;
mIdForResponseEvent = idForResponseEvent;
+ mTimestampsMs = new LongArrayQueue();
}
public int getSourceUid() {
@@ -55,6 +58,14 @@ class BroadcastEvent {
return mIdForResponseEvent;
}
+ public LongArrayQueue getTimestampsMs() {
+ return mTimestampsMs;
+ }
+
+ public void addTimestampMs(long timestampMs) {
+ mTimestampsMs.addLast(timestampMs);
+ }
+
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index 76d2fe7917fc..7e3990d95915 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -24,8 +24,10 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessState;
import android.app.usage.BroadcastResponseStats;
+import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.LongSparseArray;
+import android.util.ArraySet;
+import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.SparseArray;
@@ -89,14 +91,14 @@ class BroadcastResponseStatsTracker {
return;
}
synchronized (mLock) {
- final LongSparseArray<BroadcastEvent> broadcastEvents =
+ final ArraySet<BroadcastEvent> broadcastEvents =
getOrCreateBroadcastEventsLocked(targetPackage, targetUser);
- final BroadcastEvent broadcastEvent = new BroadcastEvent(
+ final BroadcastEvent broadcastEvent = getOrCreateBroadcastEvent(broadcastEvents,
sourceUid, targetPackage, targetUser.getIdentifier(), idForResponseEvent);
- broadcastEvents.append(timestampMs, broadcastEvent);
- final BroadcastResponseStats responseStats =
- getOrCreateBroadcastResponseStats(broadcastEvent);
- responseStats.incrementBroadcastsDispatchedCount(1);
+ broadcastEvent.addTimestampMs(timestampMs);
+
+ // Delete any old broadcast event related data so that we don't keep accumulating them.
+ recordAndPruneOldBroadcastDispatchTimestamps(broadcastEvent);
}
}
@@ -120,44 +122,87 @@ class BroadcastResponseStatsTracker {
@NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
mLogger.logNotificationEvent(event, packageName, user, timestampMs);
synchronized (mLock) {
- final LongSparseArray<BroadcastEvent> broadcastEvents =
+ final ArraySet<BroadcastEvent> broadcastEvents =
getBroadcastEventsLocked(packageName, user);
if (broadcastEvents == null) {
return;
}
- // TODO (206518114): Add LongSparseArray.removeAtRange()
+ final long broadcastResponseWindowDurationMs =
+ mAppStandby.getBroadcastResponseWindowDurationMs();
+ final long broadcastsSessionWithResponseDurationMs =
+ mAppStandby.getBroadcastSessionsWithResponseDurationMs();
+ final boolean recordAllBroadcastsSessionsWithinResponseWindow =
+ mAppStandby.shouldNoteResponseEventForAllBroadcastSessions();
for (int i = broadcastEvents.size() - 1; i >= 0; --i) {
- final long dispatchTimestampMs = broadcastEvents.keyAt(i);
- final long elapsedDurationMs = timestampMs - dispatchTimestampMs;
- if (elapsedDurationMs <= 0) {
- continue;
- }
- if (dispatchTimestampMs >= timestampMs) {
- continue;
- }
- if (elapsedDurationMs <= mAppStandby.getBroadcastResponseWindowDurationMs()) {
- final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i);
- final BroadcastResponseStats responseStats =
- getBroadcastResponseStats(broadcastEvent);
- if (responseStats == null) {
- continue;
- }
- switch (event) {
- case NOTIFICATION_EVENT_TYPE_POSTED:
- responseStats.incrementNotificationsPostedCount(1);
- break;
- case NOTIFICATION_EVENT_TYPE_UPDATED:
- responseStats.incrementNotificationsUpdatedCount(1);
- break;
- case NOTIFICATION_EVENT_TYPE_CANCELLED:
- responseStats.incrementNotificationsCancelledCount(1);
+ final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i);
+ recordAndPruneOldBroadcastDispatchTimestamps(broadcastEvent);
+
+ final LongArrayQueue dispatchTimestampsMs = broadcastEvent.getTimestampsMs();
+ long broadcastsSessionEndTimestampMs = 0;
+ // We only need to look at the broadcast events that occurred before
+ // this notification related event.
+ while (dispatchTimestampsMs.size() > 0
+ && dispatchTimestampsMs.peekFirst() < timestampMs) {
+ final long dispatchTimestampMs = dispatchTimestampsMs.peekFirst();
+ final long elapsedDurationMs = timestampMs - dispatchTimestampMs;
+ // Only increment the counts if the broadcast was sent not too long ago, as
+ // decided by 'broadcastResponseWindowDurationMs' and is part of a new session.
+ // That is, it occurred 'broadcastsSessionWithResponseDurationMs' after the
+ // previously handled broadcast event which is represented by
+ // 'broadcastsSessionEndTimestampMs'.
+ if (elapsedDurationMs <= broadcastResponseWindowDurationMs
+ && dispatchTimestampMs >= broadcastsSessionEndTimestampMs) {
+ if (broadcastsSessionEndTimestampMs != 0
+ && !recordAllBroadcastsSessionsWithinResponseWindow) {
break;
- default:
- Slog.wtf(TAG, "Unknown event: " + event);
+ }
+ final BroadcastResponseStats responseStats =
+ getOrCreateBroadcastResponseStats(broadcastEvent);
+ responseStats.incrementBroadcastsDispatchedCount(1);
+ broadcastsSessionEndTimestampMs = dispatchTimestampMs
+ + broadcastsSessionWithResponseDurationMs;
+ switch (event) {
+ case NOTIFICATION_EVENT_TYPE_POSTED:
+ responseStats.incrementNotificationsPostedCount(1);
+ break;
+ case NOTIFICATION_EVENT_TYPE_UPDATED:
+ responseStats.incrementNotificationsUpdatedCount(1);
+ break;
+ case NOTIFICATION_EVENT_TYPE_CANCELLED:
+ responseStats.incrementNotificationsCancelledCount(1);
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown event: " + event);
+ }
}
+ dispatchTimestampsMs.removeFirst();
}
- broadcastEvents.removeAt(i);
+ if (dispatchTimestampsMs.size() == 0) {
+ broadcastEvents.removeAt(i);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void recordAndPruneOldBroadcastDispatchTimestamps(BroadcastEvent broadcastEvent) {
+ final LongArrayQueue timestampsMs = broadcastEvent.getTimestampsMs();
+ final long broadcastResponseWindowDurationMs =
+ mAppStandby.getBroadcastResponseWindowDurationMs();
+ final long broadcastsSessionDurationMs =
+ mAppStandby.getBroadcastSessionsDurationMs();
+ final long nowElapsedMs = SystemClock.elapsedRealtime();
+ long broadcastsSessionEndTimestampMs = 0;
+ while (timestampsMs.size() > 0
+ && timestampsMs.peekFirst() < (nowElapsedMs - broadcastResponseWindowDurationMs)) {
+ final long eventTimestampMs = timestampsMs.peekFirst();
+ if (eventTimestampMs >= broadcastsSessionEndTimestampMs) {
+ final BroadcastResponseStats responseStats =
+ getOrCreateBroadcastResponseStats(broadcastEvent);
+ responseStats.incrementBroadcastsDispatchedCount(1);
+ broadcastsSessionEndTimestampMs = eventTimestampMs + broadcastsSessionDurationMs;
}
+ timestampsMs.removeFirst();
}
}
@@ -247,7 +292,7 @@ class BroadcastResponseStatsTracker {
@GuardedBy("mLock")
@Nullable
- private LongSparseArray<BroadcastEvent> getBroadcastEventsLocked(
+ private ArraySet<BroadcastEvent> getBroadcastEventsLocked(
@NonNull String packageName, UserHandle user) {
final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(
user.getIdentifier());
@@ -259,7 +304,7 @@ class BroadcastResponseStatsTracker {
@GuardedBy("mLock")
@NonNull
- private LongSparseArray<BroadcastEvent> getOrCreateBroadcastEventsLocked(
+ private ArraySet<BroadcastEvent> getOrCreateBroadcastEventsLocked(
@NonNull String packageName, UserHandle user) {
UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(user.getIdentifier());
if (userBroadcastEvents == null) {
@@ -272,16 +317,6 @@ class BroadcastResponseStatsTracker {
@GuardedBy("mLock")
@Nullable
private BroadcastResponseStats getBroadcastResponseStats(
- @NonNull BroadcastEvent broadcastEvent) {
- final int sourceUid = broadcastEvent.getSourceUid();
- final SparseArray<UserBroadcastResponseStats> responseStatsForUid =
- mUserResponseStats.get(sourceUid);
- return getBroadcastResponseStats(responseStatsForUid, broadcastEvent);
- }
-
- @GuardedBy("mLock")
- @Nullable
- private BroadcastResponseStats getBroadcastResponseStats(
@Nullable SparseArray<UserBroadcastResponseStats> responseStatsForUid,
@NonNull BroadcastEvent broadcastEvent) {
if (responseStatsForUid == null) {
@@ -315,6 +350,20 @@ class BroadcastResponseStatsTracker {
return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
}
+ private static BroadcastEvent getOrCreateBroadcastEvent(
+ ArraySet<BroadcastEvent> broadcastEvents,
+ int sourceUid, String targetPackage, int targetUserId, long idForResponseEvent) {
+ final BroadcastEvent broadcastEvent = new BroadcastEvent(
+ sourceUid, targetPackage, targetUserId, idForResponseEvent);
+ final int index = broadcastEvents.indexOf(broadcastEvent);
+ if (index >= 0) {
+ return broadcastEvents.valueAt(index);
+ } else {
+ broadcastEvents.add(broadcastEvent);
+ return broadcastEvent;
+ }
+ }
+
void dump(@NonNull IndentingPrintWriter ipw) {
ipw.println("Broadcast response stats:");
ipw.increaseIndent();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 6f89bb25e2ca..078177b3a89f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2768,18 +2768,9 @@ public class UsageStatsService extends SystemService implements
throw new IllegalArgumentException("id needs to be >=0");
}
- final int result = getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS);
- // STOPSHIP (206518114): Temporarily check for PACKAGE_USAGE_STATS permission as well
- // until the clients switch to using the new permission.
- if (result != PackageManager.PERMISSION_GRANTED) {
- if (!hasPermission(callingPackage)) {
- throw new SecurityException(
- "Caller does not have the permission needed to call this API; "
- + "callingPackage=" + callingPackage
- + ", callingUid=" + Binder.getCallingUid());
- }
- }
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
+ "queryBroadcastResponseStats");
final int callingUid = Binder.getCallingUid();
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
userId, false /* allowAll */, false /* requireFull */,
@@ -2801,18 +2792,9 @@ public class UsageStatsService extends SystemService implements
}
- final int result = getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS);
- // STOPSHIP (206518114): Temporarily check for PACKAGE_USAGE_STATS permission as well
- // until the clients switch to using the new permission.
- if (result != PackageManager.PERMISSION_GRANTED) {
- if (!hasPermission(callingPackage)) {
- throw new SecurityException(
- "Caller does not have the permission needed to call this API; "
- + "callingPackage=" + callingPackage
- + ", callingUid=" + Binder.getCallingUid());
- }
- }
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
+ "clearBroadcastResponseStats");
final int callingUid = Binder.getCallingUid();
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
userId, false /* allowAll */, false /* requireFull */,
@@ -2825,18 +2807,9 @@ public class UsageStatsService extends SystemService implements
public void clearBroadcastEvents(@NonNull String callingPackage, @UserIdInt int userId) {
Objects.requireNonNull(callingPackage);
- final int result = getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS);
- // STOPSHIP (206518114): Temporarily check for PACKAGE_USAGE_STATS permission as well
- // until the clients switch to using the new permission.
- if (result != PackageManager.PERMISSION_GRANTED) {
- if (!hasPermission(callingPackage)) {
- throw new SecurityException(
- "Caller does not have the permission needed to call this API; "
- + "callingPackage=" + callingPackage
- + ", callingUid=" + Binder.getCallingUid());
- }
- }
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
+ "clearBroadcastEvents");
final int callingUid = Binder.getCallingUid();
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
userId, false /* allowAll */, false /* requireFull */,
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
index 0ec59c3e022f..fcef01058cbc 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
@@ -19,7 +19,8 @@ package com.android.server.usage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
-import android.util.LongSparseArray;
+import android.util.ArraySet;
+import android.util.LongArrayQueue;
import android.util.TimeUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -30,17 +31,17 @@ class UserBroadcastEvents {
* Here targetPackage refers to the package receiving the broadcast and BroadcastEvent objects
* corresponding to each broadcast it is receiving.
*/
- private ArrayMap<String, LongSparseArray<BroadcastEvent>> mBroadcastEvents = new ArrayMap();
+ private ArrayMap<String, ArraySet<BroadcastEvent>> mBroadcastEvents = new ArrayMap();
- @Nullable LongSparseArray<BroadcastEvent> getBroadcastEvents(@NonNull String packageName) {
+ @Nullable ArraySet<BroadcastEvent> getBroadcastEvents(@NonNull String packageName) {
return mBroadcastEvents.get(packageName);
}
- @NonNull LongSparseArray<BroadcastEvent> getOrCreateBroadcastEvents(
+ @NonNull ArraySet<BroadcastEvent> getOrCreateBroadcastEvents(
@NonNull String packageName) {
- LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.get(packageName);
+ ArraySet<BroadcastEvent> broadcastEvents = mBroadcastEvents.get(packageName);
if (broadcastEvents == null) {
- broadcastEvents = new LongSparseArray<>();
+ broadcastEvents = new ArraySet<>();
mBroadcastEvents.put(packageName, broadcastEvents);
}
return broadcastEvents;
@@ -56,7 +57,7 @@ class UserBroadcastEvents {
void clear(int uid) {
for (int i = mBroadcastEvents.size() - 1; i >= 0; --i) {
- final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
+ final ArraySet<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
for (int j = broadcastEvents.size() - 1; j >= 0; --j) {
if (broadcastEvents.valueAt(j).getSourceUid() == uid) {
broadcastEvents.removeAt(j);
@@ -68,18 +69,26 @@ class UserBroadcastEvents {
void dump(@NonNull IndentingPrintWriter ipw) {
for (int i = 0; i < mBroadcastEvents.size(); ++i) {
final String packageName = mBroadcastEvents.keyAt(i);
- final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
+ final ArraySet<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
ipw.println(packageName + ":");
ipw.increaseIndent();
if (broadcastEvents.size() == 0) {
ipw.println("<empty>");
} else {
for (int j = 0; j < broadcastEvents.size(); ++j) {
- final long timestampMs = broadcastEvents.keyAt(j);
final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(j);
- TimeUtils.formatDuration(timestampMs, ipw);
- ipw.print(": ");
ipw.println(broadcastEvent);
+ ipw.increaseIndent();
+ final LongArrayQueue timestampsMs = broadcastEvent.getTimestampsMs();
+ for (int timestampIdx = 0; timestampIdx < timestampsMs.size(); ++timestampIdx) {
+ if (timestampIdx > 0) {
+ ipw.print(',');
+ }
+ final long timestampMs = timestampsMs.get(timestampIdx);
+ TimeUtils.formatDuration(timestampMs, ipw);
+ }
+ ipw.println();
+ ipw.decreaseIndent();
}
}
ipw.decreaseIndent();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a6e3bb41b87c..b6f86527b747 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17006,4 +17006,41 @@ public class TelephonyManager {
}
mTelephonyRegistryMgr.removeCarrierPrivilegesCallback(callback);
}
+
+ /**
+ * set removable eSIM as default eUICC.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+ public void setRemovableEsimAsDefaultEuicc(boolean isDefault) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setRemovableEsimAsDefaultEuicc(isDefault, getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setRemovableEsimAsDefault: " + e);
+ }
+ }
+
+ /**
+ * Returns whether the removable eSIM is default eUICC or not.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+ public boolean isRemovableEsimDefaultEuicc() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isRemovableEsimDefaultEuicc(getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in isRemovableEsimDefaultEuicc: " + e);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 7d116f9d5063..0ce6b14ce3b0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2553,4 +2553,18 @@ interface ITelephony {
* for the slot, or {@code null} if none is resolved
*/
String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex);
+
+ /**
+ * set removable eSIM as default eUICC.
+ *
+ * @hide
+ */
+ void setRemovableEsimAsDefaultEuicc(boolean isDefault, String callingPackage);
+
+ /**
+ * Returns whether the removable eSIM is default eUICC or not.
+ *
+ * @hide
+ */
+ boolean isRemovableEsimDefaultEuicc(String callingPackage);
}