summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/system-current.txt2
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig7
-rw-r--r--core/java/android/os/IVibratorManagerService.aidl2
-rw-r--r--core/java/android/os/SystemVibrator.java5
-rw-r--r--core/java/android/os/SystemVibratorManager.java10
-rw-r--r--core/java/android/os/VibrationAttributes.java11
-rw-r--r--core/java/android/os/Vibrator.java4
-rw-r--r--core/java/android/os/VibratorManager.java4
-rw-r--r--core/java/android/os/vibrator/VibrationConfig.java14
-rw-r--r--core/java/android/view/IWindowSession.aidl4
-rw-r--r--core/java/android/view/View.java11
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/WindowlessWindowManager.java6
-rw-r--r--core/jni/LayoutlibLoader.cpp2
-rw-r--r--core/proto/android/server/vibrator/vibratormanagerservice.proto25
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java2
-rw-r--r--libs/WindowManager/Shell/Android.bp73
-rw-r--r--libs/WindowManager/Shell/multivalentTests/Android.bp97
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt319
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt4
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java4
-rw-r--r--services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java103
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java36
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java24
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java2
-rw-r--r--services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java182
-rw-r--r--services/core/java/com/android/server/vibrator/HalVibration.java28
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java8
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java23
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java52
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationScaler.java167
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java80
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorControlService.java277
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java5
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorControllerHolder.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java205
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java31
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java26
-rw-r--r--services/core/java/com/android/server/wm/Session.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java305
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java13
-rw-r--r--services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java10
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java267
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java64
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java14
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java42
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java2
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java2
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt4
76 files changed, 2321 insertions, 691 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1718452548b3..1f1a94ef31ea 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -15321,7 +15321,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
- method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @Nullable @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) public android.telephony.CellIdentity getLastKnownCellIdentity();
+ method @FlaggedApi("com.android.server.telecom.flags.get_last_known_cell_identity") @Nullable @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) public android.telephony.CellIdentity getLastKnownCellIdentity();
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public int getMaxNumberOfSimultaneouslyActiveSims();
method public static long getMaxNumberVerificationTimeoutMillis();
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 10954aba955c..e1a69139d88d 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -149,3 +149,10 @@ flag {
description: "Allow to query whether MTE is enabled or not to check for compliance for enterprise policy"
bug: "322777918"
}
+
+flag {
+ name: "esim_management_ux_enabled"
+ namespace: "enterprise"
+ description: "Enable UX changes for esim management"
+ bug: "295301164"
+}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index 65e498e14475..8b1577cb4b1c 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -41,5 +41,5 @@ interface IVibratorManagerService {
// There is no order guarantee with respect to the two-way APIs above like
// vibrate/isVibrating/cancel.
oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
- boolean always, String reason);
+ boolean always, String reason, boolean fromIme);
}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 04c257b92e29..2a62c24a86e1 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -206,12 +206,13 @@ public class SystemVibrator extends Vibrator {
}
@Override
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(
+ int constant, boolean always, String reason, boolean fromIme) {
if (mVibratorManager == null) {
Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager.");
return;
}
- mVibratorManager.performHapticFeedback(constant, always, reason);
+ mVibratorManager.performHapticFeedback(constant, always, reason, fromIme);
}
@Override
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index 8e8392302824..c80bcac2624f 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -147,14 +147,15 @@ public class SystemVibratorManager extends VibratorManager {
}
@Override
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(int constant, boolean always, String reason,
+ boolean fromIme) {
if (mService == null) {
Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service.");
return;
}
try {
mService.performHapticFeedback(
- mUid, mContext.getDeviceId(), mPackageName, constant, always, reason);
+ mUid, mContext.getDeviceId(), mPackageName, constant, always, reason, fromIme);
} catch (RemoteException e) {
Log.w(TAG, "Failed to perform haptic feedback.", e);
}
@@ -244,8 +245,9 @@ public class SystemVibratorManager extends VibratorManager {
}
@Override
- public void performHapticFeedback(int effectId, boolean always, String reason) {
- SystemVibratorManager.this.performHapticFeedback(effectId, always, reason);
+ public void performHapticFeedback(int effectId, boolean always, String reason,
+ boolean fromIme) {
+ SystemVibratorManager.this.performHapticFeedback(effectId, always, reason, fromIme);
}
@Override
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 46705a31f395..9df5b850188f 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -289,6 +289,15 @@ public final class VibrationAttributes implements Parcelable {
}
/**
+ * Return the original {@link AudioAttributes} used to create the vibration attributes.
+ * @hide
+ */
+ @AudioAttributes.AttributeUsage
+ public int getOriginalAudioUsage() {
+ return mOriginalAudioUsage;
+ }
+
+ /**
* Return the flags.
* @return a combined mask of all flags
*/
@@ -405,8 +414,8 @@ public final class VibrationAttributes implements Parcelable {
return "VibrationAttributes{"
+ "mUsage=" + usageToString()
+ ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
- + ", mFlags=" + mFlags
+ ", mCategory=" + categoryToString()
+ + ", mFlags=" + mFlags
+ '}';
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 2fc24142acf2..4b2d4eb833ff 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -534,10 +534,12 @@ public abstract class Vibrator {
* {@code false} if the vibration for the haptic feedback should respect the applicable
* vibration intensity settings.
* @param reason the reason for this haptic feedback.
+ * @param fromIme the haptic feedback is performed from an IME.
*
* @hide
*/
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(int constant, boolean always, String reason,
+ boolean fromIme) {
Log.w(TAG, "performHapticFeedback is not supported");
}
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index e0b6a9fd28f0..513c4bd7ec0c 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -146,9 +146,11 @@ public abstract class VibratorManager {
* vibration intensity settings applicable to the corresponding vibration.
* {@code false} otherwise.
* @param reason the reason for this haptic feedback.
+ * @param fromIme the haptic feedback is performed from an IME.
* @hide
*/
- public void performHapticFeedback(int constant, boolean always, String reason) {
+ public void performHapticFeedback(int constant, boolean always, String reason,
+ boolean fromIme) {
Log.w(TAG, "performHapticFeedback is not supported");
}
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index bcdb98282679..a14a2c7e54ca 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -224,17 +224,19 @@ public class VibrationConfig {
@Override
public String toString() {
return "VibrationConfig{"
- + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+ + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger
+ + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+ ", mRampStepDurationMs=" + mRampStepDurationMs
+ ", mRampDownDurationMs=" + mRampDownDurationMs
+ + ", mRequestVibrationParamsForUsages="
+ + Arrays.toString(getRequestVibrationParamsForUsagesNames())
+ + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs
+ ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity
+ ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity
+ ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
+ ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
+ ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
- + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs
- + ", mRequestVibrationParamsForUsages=" + Arrays.toString(
- getRequestVibrationParamsForUsagesNames())
+ + ", mDefaultKeyboardVibrationEnabled=" + mDefaultKeyboardVibrationEnabled
+ "}";
}
@@ -246,9 +248,13 @@ public class VibrationConfig {
public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) {
pw.println("VibrationConfig:");
pw.increaseIndent();
+ pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger);
pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
pw.println("rampStepDurationMs = " + mRampStepDurationMs);
pw.println("rampDownDurationMs = " + mRampDownDurationMs);
+ pw.println("requestVibrationParamsForUsages = "
+ + Arrays.toString(getRequestVibrationParamsForUsagesNames()));
+ pw.println("requestVibrationParamsTimeoutMs = " + mRequestVibrationParamsTimeoutMs);
pw.decreaseIndent();
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d68a47c54d4b..e126836020b4 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -148,13 +148,13 @@ interface IWindowSession {
int seqId);
@UnsupportedAppUsage
- boolean performHapticFeedback(int effectId, boolean always);
+ boolean performHapticFeedback(int effectId, boolean always, boolean fromIme);
/**
* Called by attached views to perform predefined haptic feedback without requiring VIBRATE
* permission.
*/
- oneway void performHapticFeedbackAsync(int effectId, boolean always);
+ oneway void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme);
/**
* Initiate the drag operation itself
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a2faabc575c4..3478286abd57 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25,6 +25,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -28376,15 +28377,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
final boolean always = (flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0;
+ boolean fromIme = false;
+ if (mAttachInfo.mViewRootImpl != null) {
+ fromIme = mAttachInfo.mViewRootImpl.mWindowAttributes.type == TYPE_INPUT_METHOD;
+ }
if (Flags.useVibratorHapticFeedback()) {
if (!mAttachInfo.canPerformHapticFeedback()) {
return false;
}
getSystemVibrator().performHapticFeedback(
- feedbackConstant, always, "View#performHapticFeedback");
+ feedbackConstant, always, "View#performHapticFeedback", fromIme);
return true;
}
- return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant, always);
+ return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant, always, fromIme);
}
private Vibrator getSystemVibrator() {
@@ -31427,7 +31432,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
interface Callbacks {
void playSoundEffect(int effectId);
- boolean performHapticFeedback(int effectId, boolean always);
+ boolean performHapticFeedback(int effectId, boolean always, boolean fromIme);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 333cbb39d9c7..708751a25053 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9201,18 +9201,18 @@ public final class ViewRootImpl implements ViewParent,
* {@inheritDoc}
*/
@Override
- public boolean performHapticFeedback(int effectId, boolean always) {
+ public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
if ((mDisplay.getFlags() & Display.FLAG_TOUCH_FEEDBACK_DISABLED) != 0) {
return false;
}
try {
if (USE_ASYNC_PERFORM_HAPTIC_FEEDBACK) {
- mWindowSession.performHapticFeedbackAsync(effectId, always);
+ mWindowSession.performHapticFeedbackAsync(effectId, always, fromIme);
return true;
} else {
// Original blocking binder call path.
- return mWindowSession.performHapticFeedback(effectId, always);
+ return mWindowSession.performHapticFeedback(effectId, always, fromIme);
}
} catch (RemoteException e) {
return false;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 2b2c50725749..22d8ed91d455 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -479,13 +479,13 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public boolean performHapticFeedback(int effectId, boolean always) {
+ public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
return false;
}
@Override
- public void performHapticFeedbackAsync(int effectId, boolean always) {
- performHapticFeedback(effectId, always);
+ public void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme) {
+ performHapticFeedback(effectId, always, fromIme);
}
@Override
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 06d5eb305ff0..d5f17da0a072 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -364,7 +364,7 @@ using namespace android;
// Called right before aborting by LOG_ALWAYS_FATAL. Print the pending exception.
void abort_handler(const char* abort_message) {
- ALOGE("Abort to abort the process...");
+ ALOGE("About to abort the process...");
JNIEnv* env = NULL;
if (javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index db99e5b53875..9151958e2b94 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -79,11 +79,28 @@ message CombinedVibrationEffectProto {
repeated int32 delays = 2;
}
+// Next Tag: 5
message VibrationAttributesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 usage = 1;
optional int32 audio_usage = 2;
optional int32 flags = 3;
+ optional int32 category = 4;
+}
+
+// Next Tag: 4
+message VibrationParamProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional VibrationScaleParamProto scale = 1;
+ optional int64 create_time = 2;
+ optional bool is_from_request = 3;
+}
+
+// Next Tag: 3
+message VibrationScaleParamProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 types_mask = 1;
+ optional float scale = 2;
}
// Next Tag: 9
@@ -132,16 +149,19 @@ message VibrationProto {
}
}
-// Next Tag: 25
+// Next Tag: 29
message VibratorManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 vibrator_ids = 1;
optional VibrationProto current_vibration = 2;
optional bool is_vibrating = 3;
+ optional int32 is_vibrator_controller_registered = 27;
optional VibrationProto current_external_vibration = 4;
optional bool vibrator_under_external_control = 5;
optional bool low_power_mode = 6;
optional bool vibrate_on = 24;
+ optional bool keyboard_vibration_on = 25;
+ optional int32 default_vibration_amplitude = 26;
optional int32 alarm_intensity = 18;
optional int32 alarm_default_intensity = 19;
optional int32 haptic_feedback_intensity = 7;
@@ -158,5 +178,6 @@ message VibratorManagerServiceDumpProto {
repeated VibrationProto previous_notification_vibrations = 14;
repeated VibrationProto previous_alarm_vibrations = 15;
repeated VibrationProto previous_vibrations = 16;
- repeated VibrationProto previous_external_vibrations = 17;
+ repeated VibrationParamProto previous_vibration_params = 28;
+ reserved 17; // prev previous_external_vibrations
} \ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6134e788be82..967edde83cc9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4101,6 +4101,13 @@
<!-- How close vibration request should be when they're aggregated for dumpsys, in ms. -->
<integer name="config_previousVibrationsDumpAggregationTimeMillisLimit">1000</integer>
+ <!-- How long history of vibration control service should be kept for the dumpsys. -->
+ <integer name="config_vibratorControlServiceDumpSizeLimit">50</integer>
+
+ <!-- How close requests to vibration control service should be when they're aggregated for
+ dumpsys, in ms. -->
+ <integer name="config_vibratorControlServiceDumpAggregationTimeMillisLimit">60000</integer>
+
<!-- The default vibration strength, must be between 1 and 255 inclusive. -->
<integer name="config_defaultVibrationAmplitude">255</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2f5183fc1455..ee51ed020be6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2083,6 +2083,8 @@
<java-symbol type="integer" name="config_recentVibrationsDumpSizeLimit" />
<java-symbol type="integer" name="config_previousVibrationsDumpSizeLimit" />
<java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
+ <java-symbol type="integer" name="config_vibratorControlServiceDumpSizeLimit" />
+ <java-symbol type="integer" name="config_vibratorControlServiceDumpAggregationTimeMillisLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 7c58de67ded6..1a242eff73b1 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -451,7 +451,7 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = new ViewRootImpl(sContext, display);
boolean result = viewRootImpl.performHapticFeedback(
- HapticFeedbackConstants.CONTEXT_CLICK, true);
+ HapticFeedbackConstants.CONTEXT_CLICK, true, false /* fromIme */);
assertThat(result).isFalse();
}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 0ecf1f8f1feb..8829d1b9e0e1 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -212,76 +212,3 @@ android_library {
plugins: ["dagger2-compiler"],
use_resource_processor: true,
}
-
-android_app {
- name: "WindowManagerShellRobolectric",
- platform_apis: true,
- static_libs: [
- "WindowManager-Shell",
- ],
- manifest: "multivalentTests/AndroidManifestRobolectric.xml",
- use_resource_processor: true,
-}
-
-android_robolectric_test {
- name: "WMShellRobolectricTests",
- instrumentation_for: "WindowManagerShellRobolectric",
- upstream: true,
- java_resource_dirs: [
- "multivalentTests/robolectric/config",
- ],
- srcs: [
- "multivalentTests/src/**/*.kt",
- ],
- // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
- exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
- static_libs: [
- "junit",
- "androidx.test.runner",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "mockito-robolectric-prebuilt",
- "mockito-kotlin2",
- "truth",
- ],
-}
-
-android_test {
- name: "WMShellMultivalentTestsOnDevice",
- srcs: [
- "multivalentTests/src/**/*.kt",
- ],
- static_libs: [
- "WindowManager-Shell",
- "junit",
- "androidx.test.runner",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "frameworks-base-testutils",
- "mockito-kotlin2",
- "mockito-target-extended-minus-junit4",
- "truth",
- "platform-test-annotations",
- "platform-test-rules",
- ],
- libs: [
- "android.test.base",
- "android.test.runner",
- ],
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
- kotlincflags: ["-Xjvm-default=all"],
- optimize: {
- enabled: false,
- },
- test_suites: ["device-tests"],
- platform_apis: true,
- certificate: "platform",
- aaptflags: [
- "--extra-packages",
- "com.android.wm.shell",
- ],
- manifest: "multivalentTests/AndroidManifest.xml",
-}
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
new file mode 100644
index 000000000000..1686d0d54dc4
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -0,0 +1,97 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_multitasking_windowing",
+}
+
+android_app {
+ name: "WindowManagerShellRobolectric",
+ platform_apis: true,
+ static_libs: [
+ "WindowManager-Shell",
+ ],
+ manifest: "AndroidManifestRobolectric.xml",
+ use_resource_processor: true,
+}
+
+android_robolectric_test {
+ name: "WMShellRobolectricTests",
+ instrumentation_for: "WindowManagerShellRobolectric",
+ upstream: true,
+ java_resource_dirs: [
+ "robolectric/config",
+ ],
+ srcs: [
+ "src/**/*.kt",
+ ],
+ // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
+ exclude_srcs: ["src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
+ static_libs: [
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "mockito-robolectric-prebuilt",
+ "mockito-kotlin2",
+ "truth",
+ ],
+ auto_gen_config: true,
+}
+
+android_test {
+ name: "WMShellMultivalentTestsOnDevice",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "WindowManager-Shell",
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "frameworks-base-testutils",
+ "mockito-kotlin2",
+ "mockito-target-extended-minus-junit4",
+ "truth",
+ "platform-test-annotations",
+ "platform-test-rules",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.wm.shell",
+ ],
+ manifest: "AndroidManifest.xml",
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ba7738005de2..cb6894eb87ba 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -563,3 +563,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "media_controls_refactor"
+ namespace: "systemui"
+ description: "Refactors media code to follow the recommended architecture"
+ bug: "326408371"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
new file mode 100644
index 000000000000..e789475b7877
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 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.bouncer.shared.flag
+
+import com.android.systemui.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import dagger.Module
+import dagger.Provides
+
+interface ComposeBouncerFlags {
+
+ /**
+ * Returns `true` if the Compose bouncer is enabled or if the scene container framework is
+ * enabled; `false` otherwise.
+ */
+ fun isComposeBouncerOrSceneContainerEnabled(): Boolean
+
+ /**
+ * Returns `true` if only compose bouncer is enabled and scene container framework is not
+ * enabled.
+ */
+ @Deprecated(
+ "Avoid using this, this is meant to be used only by the glue code " +
+ "that includes compose bouncer in legacy keyguard.",
+ replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
+ )
+ fun isOnlyComposeBouncerEnabled(): Boolean
+}
+
+class ComposeBouncerFlagsImpl(private val sceneContainerFlags: SceneContainerFlags) :
+ ComposeBouncerFlags {
+
+ override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+ return sceneContainerFlags.isEnabled() || Flags.composeBouncer()
+ }
+
+ @Deprecated(
+ "Avoid using this, this is meant to be used only by the glue code " +
+ "that includes compose bouncer in legacy keyguard.",
+ replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
+ )
+ override fun isOnlyComposeBouncerEnabled(): Boolean {
+ return !sceneContainerFlags.isEnabled() && Flags.composeBouncer()
+ }
+}
+
+@Module
+object ComposeBouncerFlagsModule {
+ @Provides
+ @SysUISingleton
+ fun impl(sceneContainerFlags: SceneContainerFlags): ComposeBouncerFlags {
+ return ComposeBouncerFlagsImpl(sceneContainerFlags)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index dd253a8f6eff..36d3ed52b655 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -4,10 +4,10 @@ import android.view.ViewGroup
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.ViewMediatorCallback
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.Flags
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
@@ -55,12 +55,15 @@ constructor(
class BouncerViewBinder
@Inject
constructor(
+ private val composeBouncerFlags: ComposeBouncerFlags,
private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>,
private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
) {
fun bind(view: ViewGroup) {
if (
- ComposeFacade.isComposeAvailable() && Flags.composeBouncer() && COMPOSE_BOUNCER_ENABLED
+ COMPOSE_BOUNCER_ENABLED &&
+ composeBouncerFlags.isOnlyComposeBouncerEnabled() &&
+ ComposeFacade.isComposeAvailable()
) {
val deps = composeBouncerDependencies.get()
ComposeBouncerViewBinder.bind(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 4466cbbe05be..62875783ef5f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -28,6 +28,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
@@ -35,7 +36,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -72,7 +72,7 @@ class BouncerViewModel(
private val simBouncerInteractor: SimBouncerInteractor,
private val authenticationInteractor: AuthenticationInteractor,
private val selectedUserInteractor: SelectedUserInteractor,
- flags: SceneContainerFlags,
+ flags: ComposeBouncerFlags,
selectedUser: Flow<UserViewModel>,
users: Flow<List<UserViewModel>>,
userSwitcherMenu: Flow<List<UserActionViewModel>>,
@@ -233,7 +233,7 @@ class BouncerViewModel(
private var lockoutCountdownJob: Job? = null
init {
- if (flags.isEnabled()) {
+ if (flags.isComposeBouncerOrSceneContainerEnabled()) {
// Keeps the lockout dialog up-to-date.
applicationScope.launch {
bouncerInteractor.onLockoutStarted.collect {
@@ -478,7 +478,7 @@ object BouncerViewModelModule {
actionButtonInteractor: BouncerActionButtonInteractor,
authenticationInteractor: AuthenticationInteractor,
selectedUserInteractor: SelectedUserInteractor,
- flags: SceneContainerFlags,
+ flags: ComposeBouncerFlags,
userSwitcherViewModel: UserSwitcherViewModel,
clock: SystemClock,
devicePolicyManager: DevicePolicyManager,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
index 6bfe8d91b5fc..846013cef326 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
@@ -21,9 +21,12 @@ import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInte
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceFailureMessage
+import com.android.systemui.deviceentry.shared.model.FaceLockoutMessage
import com.android.systemui.deviceentry.shared.model.FaceMessage
import com.android.systemui.deviceentry.shared.model.FaceTimeoutMessage
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FingerprintFailureMessage
import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
@@ -97,11 +100,7 @@ constructor(
fingerprintAuthInteractor.fingerprintHelp
.sample(biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed, ::Pair)
.filter { (_, fingerprintAuthAllowed) -> fingerprintAuthAllowed }
- .map { (helpStatus, _) ->
- FingerprintMessage(
- helpStatus.msg,
- )
- }
+ .map { (helpStatus, _) -> FingerprintMessage(helpStatus.msg) }
private val fingerprintFailMessage: Flow<FingerprintMessage> =
fingerprintPropertyInteractor.isUdfps.flatMapLatest { isUdfps ->
@@ -109,7 +108,7 @@ constructor(
.sample(biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed)
.filter { fingerprintAuthAllowed -> fingerprintAuthAllowed }
.map {
- FingerprintMessage(
+ FingerprintFailureMessage(
if (isUdfps) {
resources.getString(
com.android.internal.R.string.fingerprint_udfps_error_not_match
@@ -118,7 +117,7 @@ constructor(
resources.getString(
com.android.internal.R.string.fingerprint_error_not_match
)
- },
+ }
)
}
}
@@ -154,7 +153,7 @@ constructor(
faceFailure
.sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed)
.filter { faceAuthCurrentlyAllowed -> faceAuthCurrentlyAllowed }
- .map { FaceMessage(resources.getString(R.string.keyguard_face_failed)) }
+ .map { FaceFailureMessage(resources.getString(R.string.keyguard_face_failed)) }
private val faceErrorMessage: Flow<FaceMessage> =
faceError
@@ -173,6 +172,7 @@ constructor(
FaceTimeoutMessage(status.msg)
}
}
+ status.isLockoutError() -> FaceLockoutMessage(status.msg)
else -> FaceMessage(status.msg)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
index 59c3f7f8aded..2ced8c41713f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
@@ -32,9 +32,15 @@ data class FaceTimeoutMessage(
private val faceTimeoutMessage: String?,
) : FaceMessage(faceTimeoutMessage)
+data class FaceLockoutMessage(private val msg: String?) : FaceMessage(msg)
+
+data class FaceFailureMessage(private val msg: String) : FaceMessage(msg)
+
/** Fingerprint biometric message */
open class FingerprintMessage(fingerprintMessage: String?) : BiometricMessage(fingerprintMessage)
data class FingerprintLockoutMessage(
private val fingerprintLockoutMessage: String?,
) : FingerprintMessage(fingerprintLockoutMessage)
+
+data class FingerprintFailureMessage(private val msg: String?) : FingerprintMessage(msg)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 719edd7e8535..37b331cd8455 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -35,6 +35,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,6 +47,7 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates business-logic related to the keyguard transitions. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -206,6 +208,21 @@ constructor(
.shareIn(scope, SharingStarted.Eagerly, replay = 1)
/**
+ * A pair of the most recent STARTED step, and the transition step immediately preceding it. The
+ * transition framework enforces that the previous step is either a CANCELED or FINISHED step,
+ * and that the previous step was *to* the state the STARTED step is *from*.
+ *
+ * This flow can be used to access the previous step to determine whether it was CANCELED or
+ * FINISHED. In the case of a CANCELED step, we can also figure out which state we were coming
+ * from when we were canceled.
+ */
+ val startedStepWithPrecedingStep =
+ transitions
+ .pairwise()
+ .filter { it.newValue.transitionState == TransitionState.STARTED }
+ .stateIn(scope, SharingStarted.Eagerly, null)
+
+ /**
* The last [KeyguardState] to which we [TransitionState.FINISHED] a transition.
*
* WARNING: This will NOT emit a value if a transition is CANCELED, and will also not emit a
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index b81793ecec64..cff74b333530 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -19,7 +19,9 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -121,19 +123,27 @@ constructor(
* want to know if the AOD/clock/notifs/etc. are visible.
*/
val lockscreenVisibility: Flow<Boolean> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.finishedKeyguardState,
- ) { startedStep, finishedState ->
- // If we finished the transition, use the finished state. If we're running a
- // transition, use the state we're transitioning FROM. This can be different from
- // the last finished state if a transition is interrupted. For example, if we were
- // transitioning from GONE to AOD and then started AOD -> LOCKSCREEN mid-transition,
- // we want to immediately use the visibility for AOD (lockscreenVisibility=true)
- // even though the lastFinishedState is still GONE (lockscreenVisibility=false).
- if (finishedState == startedStep.to) finishedState else startedStep.from
+ transitionInteractor.currentKeyguardState
+ .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
+ .map { (currentState, startedWithPrev) ->
+ val startedFromStep = startedWithPrev?.previousValue
+ val startedStep = startedWithPrev?.newValue
+ val returningToGoneAfterCancellation =
+ startedStep?.to == KeyguardState.GONE &&
+ startedFromStep?.transitionState == TransitionState.CANCELED &&
+ startedFromStep.from == KeyguardState.GONE
+
+ if (!returningToGoneAfterCancellation) {
+ // By default, apply the lockscreen visibility of the current state.
+ KeyguardState.lockscreenVisibleInState(currentState)
+ } else {
+ // If we're transitioning to GONE after a prior canceled transition from GONE,
+ // then this is the camera launch transition from an asleep state back to GONE.
+ // We don't want to show the lockscreen since we're aborting the lock and going
+ // back to GONE.
+ KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+ }
}
- .map(KeyguardState::lockscreenVisibleInState)
.distinctUntilChanged()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt
new file mode 100644
index 000000000000..2850b4bb2358
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the media_controls_refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object MediaControlsRefactorFlag {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_MEDIA_CONTROLS_REFACTOR
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the flag enabled? */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.mediaControlsRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 15747b9eb515..d4bd6daedfab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -58,4 +58,7 @@ constructor(
/** Check whether to use scene framework */
fun isSceneContainerEnabled() =
sceneContainerFlags.isEnabled() && MediaInSceneContainerFlag.isEnabled
+
+ /** Check whether to use media refactor code */
+ fun isMediaControlsRefactorEnabled() = MediaControlsRefactorFlag.isEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index c7d3a4af24c9..7d2468b2f016 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule
@@ -34,6 +35,7 @@ import dagger.multibindings.IntoMap
[
BouncerSceneModule::class,
CommunalSceneModule::class,
+ ComposeBouncerFlagsModule::class,
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index a4483bdac467..6d605a564022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -375,4 +375,323 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
values
)
}
+
+ @Test
+ fun testLockscreenVisibility_usesFromState_ifCanceled() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockscreenVisibility)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ // Initially should be true, as we start in LOCKSCREEN.
+ true,
+ // Then, false, since we finish in GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ // Should remain false as we transition from GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ )
+
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // If we cancel and then go from LS -> GONE, we should immediately flip to the
+ // visibility of the from state (LS).
+ true,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ )
+
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ true,
+ ),
+ values
+ )
+ }
+
+ /**
+ * Tests the special case for insecure camera launch. CANCELING a transition from GONE and then
+ * STARTING a transition back to GONE should never show the lockscreen, even though the current
+ * state during the AOD/isAsleep -> GONE transition is AOD (where lockscreen visibility = true).
+ */
+ @Test
+ fun testLockscreenVisibility_falseDuringTransitionToGone_fromCanceledGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockscreenVisibility)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ // Not visible since we're GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.CANCELED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ // Remains not visible from GONE -> AOD (canceled) -> AOD since we never
+ // FINISHED in AOD, and special-case handling for the insecure camera launch
+ // ensures that we use the lockscreen visibility for GONE (false) if we're
+ // STARTED to GONE after a CANCELED from GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // Make sure there's no stuck overrides or something - we should make lockscreen
+ // visible again once we're finished in LOCKSCREEN.
+ true,
+ ),
+ values
+ )
+ }
+
+ /** */
+ @Test
+ fun testLockscreenVisibility_trueDuringTransitionToGone_fromNotCanceledGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockscreenVisibility)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ // Not visible when finished in GONE.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ // Still not visible during GONE -> AOD.
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // Visible now that we're FINISHED in AOD.
+ true
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ true,
+ false,
+ // Remains visible from AOD during transition.
+ true
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ )
+ )
+
+ runCurrent()
+ assertEquals(
+ listOf(
+ true,
+ false,
+ true,
+ // Until we're finished in GONE again.
+ false
+ ),
+ values
+ )
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
new file mode 100644
index 000000000000..5c3e1f410e63
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.bouncer.shared.flag
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+
+var Kosmos.fakeComposeBouncerFlags by
+ Kosmos.Fixture { FakeComposeBouncerFlags(fakeSceneContainerFlags) }
+val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
new file mode 100644
index 000000000000..c116bbd32f9e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.bouncer.shared.flag
+
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+
+class FakeComposeBouncerFlags(
+ private val sceneContainerFlags: SceneContainerFlags,
+ var composeBouncerEnabled: Boolean = false
+) : ComposeBouncerFlags {
+ override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+ return sceneContainerFlags.isEnabled() || composeBouncerEnabled
+ }
+
+ @Deprecated(
+ "Avoid using this, this is meant to be used only by the glue code " +
+ "that includes compose bouncer in legacy keyguard.",
+ replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
+ )
+ override fun isOnlyComposeBouncerEnabled(): Boolean = composeBouncerEnabled
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index 99dfe94af3df..6d97238ba48b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -21,12 +21,12 @@ import com.android.systemui.authentication.domain.interactor.authenticationInter
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
import com.android.systemui.util.mockito.mock
@@ -42,7 +42,7 @@ val Kosmos.bouncerViewModel by Fixture {
simBouncerInteractor = simBouncerInteractor,
authenticationInteractor = authenticationInteractor,
selectedUserInteractor = selectedUserInteractor,
- flags = sceneContainerFlags,
+ flags = composeBouncerFlags,
selectedUser = userSwitcherViewModel.selectedUser,
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index d4ff699c88a7..6b5ba96f6b1b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -50,7 +50,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
-import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyCache;
import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -940,7 +940,7 @@ public class ContentCaptureManagerService extends
return new ContentProtectionConsentManager(
BackgroundThread.getHandler(),
getContext().getContentResolver(),
- LocalServices.getService(DevicePolicyManagerInternal.class));
+ DevicePolicyCache.getInstance());
}
@Nullable
diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
index 488a51a56fee..9aa5d2fce6b8 100644
--- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
+++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
@@ -16,8 +16,13 @@
package com.android.server.contentprotection;
+import static android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -28,6 +33,7 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
/**
* Manages consent for content protection.
@@ -45,6 +51,8 @@ public class ContentProtectionConsentManager {
@NonNull private final ContentResolver mContentResolver;
+ @NonNull private final DevicePolicyCache mDevicePolicyCache;
+
@NonNull private final DevicePolicyManagerInternal mDevicePolicyManagerInternal;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -53,54 +61,98 @@ public class ContentProtectionConsentManager {
private volatile boolean mCachedPackageVerifierConsent;
- private volatile boolean mCachedContentProtectionConsent;
+ private volatile boolean mCachedContentProtectionUserConsent;
public ContentProtectionConsentManager(
@NonNull Handler handler,
@NonNull ContentResolver contentResolver,
- @NonNull DevicePolicyManagerInternal devicePolicyManagerInternal) {
+ @NonNull DevicePolicyCache devicePolicyCache) {
mContentResolver = contentResolver;
- mDevicePolicyManagerInternal = devicePolicyManagerInternal;
+ mDevicePolicyCache = devicePolicyCache;
+ mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mContentObserver = new SettingsObserver(handler);
- contentResolver.registerContentObserver(
- Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT),
- /* notifyForDescendants= */ false,
- mContentObserver,
- UserHandle.USER_ALL);
-
- mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
- mCachedContentProtectionConsent = isContentProtectionConsentGranted();
+ registerSettingsGlobalObserver(KEY_PACKAGE_VERIFIER_USER_CONSENT);
+ registerSettingsGlobalObserver(KEY_CONTENT_PROTECTION_USER_CONSENT);
+ readPackageVerifierConsentGranted();
+ readContentProtectionUserConsentGranted();
}
- /**
- * Returns true if all the consents are granted
- */
+ /** Returns true if the consent is ultimately granted. */
public boolean isConsentGranted(@UserIdInt int userId) {
- return mCachedPackageVerifierConsent
- && mCachedContentProtectionConsent
- && !isUserOrganizationManaged(userId);
+ return mCachedPackageVerifierConsent && isContentProtectionConsentGranted(userId);
}
+ /**
+ * Not always cached internally and can be expensive, when possible prefer to use {@link
+ * #mCachedPackageVerifierConsent} instead.
+ */
private boolean isPackageVerifierConsentGranted() {
- // Not always cached internally
return Settings.Global.getInt(
mContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, /* def= */ 0)
>= 1;
}
- private boolean isContentProtectionConsentGranted() {
- // Not always cached internally
+ /**
+ * Not always cached internally and can be expensive, when possible prefer to use {@link
+ * #mCachedContentProtectionUserConsent} instead.
+ */
+ private boolean isContentProtectionUserConsentGranted() {
return Settings.Global.getInt(
mContentResolver, KEY_CONTENT_PROTECTION_USER_CONSENT, /* def= */ 0)
>= 0;
}
+ private void readPackageVerifierConsentGranted() {
+ mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+ }
+
+ private void readContentProtectionUserConsentGranted() {
+ mCachedContentProtectionUserConsent = isContentProtectionUserConsentGranted();
+ }
+
+ /** Always cached internally, cheap and safe to use. */
private boolean isUserOrganizationManaged(@UserIdInt int userId) {
- // Cached internally
return mDevicePolicyManagerInternal.isUserOrganizationManaged(userId);
}
+ /** Always cached internally, cheap and safe to use. */
+ private boolean isContentProtectionPolicyGranted(@UserIdInt int userId) {
+ if (!manageDevicePolicyEnabled()) {
+ return false;
+ }
+
+ @DevicePolicyManager.ContentProtectionPolicy
+ int policy = mDevicePolicyCache.getContentProtectionPolicy(userId);
+
+ return switch (policy) {
+ case DevicePolicyManager.CONTENT_PROTECTION_ENABLED -> true;
+ case DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY ->
+ mCachedContentProtectionUserConsent;
+ default -> false;
+ };
+ }
+
+ /** Always cached internally, cheap and safe to use. */
+ private boolean isContentProtectionConsentGranted(@UserIdInt int userId) {
+ if (!manageDevicePolicyEnabled()) {
+ return mCachedContentProtectionUserConsent && !isUserOrganizationManaged(userId);
+ }
+
+ return isUserOrganizationManaged(userId)
+ ? isContentProtectionPolicyGranted(userId)
+ : mCachedContentProtectionUserConsent;
+ }
+
+ private void registerSettingsGlobalObserver(@NonNull String key) {
+ registerSettingsObserver(Settings.Global.getUriFor(key));
+ }
+
+ private void registerSettingsObserver(@NonNull Uri uri) {
+ mContentResolver.registerContentObserver(
+ uri, /* notifyForDescendants= */ false, mContentObserver, UserHandle.USER_ALL);
+ }
+
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
@@ -108,17 +160,20 @@ public class ContentProtectionConsentManager {
}
@Override
- public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ public void onChange(boolean selfChange, @Nullable Uri uri, @UserIdInt int userId) {
+ if (uri == null) {
+ return;
+ }
final String property = uri.getLastPathSegment();
if (property == null) {
return;
}
switch (property) {
case KEY_PACKAGE_VERIFIER_USER_CONSENT:
- mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+ readPackageVerifierConsentGranted();
return;
case KEY_CONTENT_PROTECTION_USER_CONSENT:
- mCachedContentProtectionConsent = isContentProtectionConsentGranted();
+ readContentProtectionUserConsentGranted();
return;
default:
Slog.w(TAG, "Ignoring unexpected property: " + property);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7d3af99b74d1..b8e09cce93b9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -372,15 +372,6 @@ public final class ActiveServices {
@Overridable
public static final long FGS_BOOT_COMPLETED_RESTRICTIONS = 296558535L;
- /**
- * Disables foreground service background starts in System Alert Window for all types
- * unless it already has a System Overlay Window.
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
- @Overridable
- public static final long FGS_SAW_RESTRICTIONS = 319471980L;
-
final ActivityManagerService mAm;
// Maximum number of services that we allow to start in the background
@@ -8535,31 +8526,10 @@ public final class ActiveServices {
}
}
- // The flag being enabled isn't enough to deny background start: we need to also check
- // if there is a system alert UI present.
if (ret == REASON_DENIED) {
- // Flag check: are we disabling SAW FGS background starts?
- final boolean shouldDisableSaw = Flags.fgsDisableSaw()
- && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, callingUid);
- if (shouldDisableSaw) {
- final ProcessRecord processRecord = mAm
- .getProcessRecordLocked(targetService.processName,
- targetService.appInfo.uid);
- if (processRecord != null) {
- if (processRecord.mState.hasOverlayUi()) {
- if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
- callingPackage)) {
- ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
- }
- }
- } else {
- Slog.e(TAG, "Could not find process record for SAW check");
- }
- } else {
- if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
- callingPackage)) {
- ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
- }
+ if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
+ callingPackage)) {
+ ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 34ba7f0debb0..3abfe082db27 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1468,7 +1468,7 @@ class UserController implements Handler.Callback {
// Send PROFILE_INACCESSIBLE broadcast if a profile was stopped
final UserInfo userInfo = getUserInfo(userId);
- if (userInfo.isProfile()) {
+ if (userInfo != null && userInfo.isProfile()) {
UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null) {
broadcastProfileAccessibleStateChanged(userId, parent.id,
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index b1823b4ee38f..c06bdf90a75a 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -23,13 +23,6 @@ flag {
}
flag {
- name: "fgs_disable_saw"
- namespace: "backstage_power"
- description: "Disable System Alert Window FGS start"
- bug: "296558535"
-}
-
-flag {
name: "bfgs_managed_network_access"
namespace: "backstage_power"
description: "Restrict network access for certain applications in BFGS process state"
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index a597edd93515..8fdc22b81769 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -59,7 +59,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider implements
private static final String EXTRA_LOCATION_TAGS = "android:location_allow_listed_tags";
private static final String LOCATION_TAGS_SEPARATOR = ";";
- private static final long RESET_DELAY_MS = 1000;
+ private static final long RESET_DELAY_MS = 10000;
/**
* Creates and registers this proxy. If no suitable service is available for the proxy, returns
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9e98a5809650..09c6dc0e603c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -128,6 +128,22 @@ public class MediaSessionService extends SystemService implements Monitor {
*/
private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+ /**
+ * Action reported to UsageStatsManager when a media session becomes active and user engaged
+ * for a given app. App is expected to show an ongoing notification after this.
+ */
+ private static final String USAGE_STATS_ACTION_START = "start";
+
+ /**
+ * Action reported to UsageStatsManager when a media session is no longer active and user
+ * engaged for a given app. If media session only pauses for a brief time the event will not
+ * necessarily be reported in case user is still "engaging" and will restart it momentarily.
+ * In such case, action may be reported after a short delay to ensure user is truly no longer
+ * engaging. Afterwards, the app is no longer expected to show an ongoing notification.
+ */
+ private static final String USAGE_STATS_ACTION_STOP = "stop";
+ private static final String USAGE_STATS_CATEGORY = "android.media";
+
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
@@ -639,13 +655,15 @@ public class MediaSessionService extends SystemService implements Monitor {
if (userEngaged) {
if (!mUserEngagingSessions.contains(sessionUid)) {
mUserEngagingSessions.put(sessionUid, new HashSet<>());
- reportUserInteractionEvent(/* action= */ "start", record.getUserId(), packageName);
+ reportUserInteractionEvent(
+ USAGE_STATS_ACTION_START, record.getUserId(), packageName);
}
mUserEngagingSessions.get(sessionUid).add(token);
} else if (mUserEngagingSessions.contains(sessionUid)) {
mUserEngagingSessions.get(sessionUid).remove(token);
if (mUserEngagingSessions.get(sessionUid).isEmpty()) {
- reportUserInteractionEvent(/* action= */ "stop", record.getUserId(), packageName);
+ reportUserInteractionEvent(
+ USAGE_STATS_ACTION_STOP, record.getUserId(), packageName);
mUserEngagingSessions.remove(sessionUid);
}
}
@@ -653,7 +671,7 @@ public class MediaSessionService extends SystemService implements Monitor {
private void reportUserInteractionEvent(String action, int userId, String packageName) {
PersistableBundle extras = new PersistableBundle();
- extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media");
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, USAGE_STATS_CATEGORY);
extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
mUsageStatsManagerInternal.reportUserInteractionEvent(packageName, userId, extras);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 428fca082f75..e9a7fe1371ac 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6410,7 +6410,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private boolean performHapticFeedback(int effectId, boolean always, String reason) {
return performHapticFeedback(Process.myUid(), mContext.getOpPackageName(),
- effectId, always, reason);
+ effectId, always, reason, false /* fromIme */);
}
@Override
@@ -6420,7 +6420,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public boolean performHapticFeedback(int uid, String packageName, int effectId,
- boolean always, String reason) {
+ boolean always, String reason, boolean fromIme) {
if (!mVibrator.hasVibrator()) {
return false;
}
@@ -6431,7 +6431,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
VibrationAttributes attrs =
mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ always);
+ effectId, /* bypassVibrationIntensitySetting= */ always, fromIme);
VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, effectId);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 2174fd62ea00..5956594acd26 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1072,7 +1072,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* Call from application to perform haptic feedback on its window.
*/
public boolean performHapticFeedback(int uid, String packageName, int effectId,
- boolean always, String reason);
+ boolean always, String reason, boolean fromIme);
/**
* Called when we have started keeping the screen on because a window
diff --git a/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java b/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java
new file mode 100644
index 000000000000..7ee29016a58b
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import java.util.ArrayDeque;
+
+/**
+ * A generic grouped list of aggregated log records to be printed in dumpsys.
+ *
+ * <p>This can be used to dump history of operations or requests to the vibrator services, e.g.
+ * vibration requests grouped by usage or vibration parameters sent to the vibrator control service.
+ *
+ * @param <T> The type of log entries aggregated in this record.
+ */
+abstract class GroupedAggregatedLogRecords<T extends GroupedAggregatedLogRecords.SingleLogRecord> {
+ private final SparseArray<ArrayDeque<AggregatedLogRecord<T>>> mGroupedRecords;
+ private final int mSizeLimit;
+ private final int mAggregationTimeLimitMs;
+
+ GroupedAggregatedLogRecords(int sizeLimit, int aggregationTimeLimitMs) {
+ mGroupedRecords = new SparseArray<>();
+ mSizeLimit = sizeLimit;
+ mAggregationTimeLimitMs = aggregationTimeLimitMs;
+ }
+
+ /** Prints a header to identify the group to be logged. */
+ abstract void dumpGroupHeader(IndentingPrintWriter pw, int groupKey);
+
+ /** Returns the {@link ProtoOutputStream} repeated field id to log records of this group. */
+ abstract long findGroupKeyProtoFieldId(int groupKey);
+
+ /**
+ * Adds given entry to this record list, dropping the oldest record if size limit was reached
+ * for its group.
+ *
+ * @param record The new {@link SingleLogRecord} to be recorded.
+ * @return The oldest {@link AggregatedLogRecord} entry being dropped from the group list if
+ * it's full, null otherwise.
+ */
+ final synchronized AggregatedLogRecord<T> add(T record) {
+ int groupKey = record.getGroupKey();
+ if (!mGroupedRecords.contains(groupKey)) {
+ mGroupedRecords.put(groupKey, new ArrayDeque<>(mSizeLimit));
+ }
+ ArrayDeque<AggregatedLogRecord<T>> records = mGroupedRecords.get(groupKey);
+ if (mAggregationTimeLimitMs > 0 && !records.isEmpty()) {
+ AggregatedLogRecord<T> lastAggregatedRecord = records.getLast();
+ if (lastAggregatedRecord.mayAggregate(record, mAggregationTimeLimitMs)) {
+ lastAggregatedRecord.record(record);
+ return null;
+ }
+ }
+ AggregatedLogRecord<T> removedRecord = null;
+ if (records.size() >= mSizeLimit) {
+ removedRecord = records.removeFirst();
+ }
+ records.addLast(new AggregatedLogRecord<>(record));
+ return removedRecord;
+ }
+
+ final synchronized void dump(IndentingPrintWriter pw) {
+ for (int i = 0; i < mGroupedRecords.size(); i++) {
+ dumpGroupHeader(pw, mGroupedRecords.keyAt(i));
+ pw.increaseIndent();
+ for (AggregatedLogRecord<T> records : mGroupedRecords.valueAt(i)) {
+ records.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+
+ final synchronized void dump(ProtoOutputStream proto) {
+ for (int i = 0; i < mGroupedRecords.size(); i++) {
+ long fieldId = findGroupKeyProtoFieldId(mGroupedRecords.keyAt(i));
+ for (AggregatedLogRecord<T> records : mGroupedRecords.valueAt(i)) {
+ records.dump(proto, fieldId);
+ }
+ }
+ }
+
+ /**
+ * Represents an aggregation of log record entries that can be printed in a compact manner.
+ *
+ * <p>The aggregation is controlled by a time limit on the difference between the creation time
+ * of two consecutive entries that {@link SingleLogRecord#mayAggregate}.
+ *
+ * @param <T> The type of log entries aggregated in this record.
+ */
+ static final class AggregatedLogRecord<T extends SingleLogRecord> {
+ private final T mFirst;
+ private T mLatest;
+ private int mCount;
+
+ AggregatedLogRecord(T record) {
+ mLatest = mFirst = record;
+ mCount = 1;
+ }
+
+ T getLatest() {
+ return mLatest;
+ }
+
+ synchronized boolean mayAggregate(T record, long timeLimitMs) {
+ long timeDeltaMs = Math.abs(mLatest.getCreateUptimeMs() - record.getCreateUptimeMs());
+ return mLatest.mayAggregate(record) && timeDeltaMs < timeLimitMs;
+ }
+
+ synchronized void record(T record) {
+ mLatest = record;
+ mCount++;
+ }
+
+ synchronized void dump(IndentingPrintWriter pw) {
+ mFirst.dump(pw);
+ if (mCount == 1) {
+ return;
+ }
+ if (mCount > 2) {
+ pw.println("-> Skipping " + (mCount - 2) + " aggregated entries, latest:");
+ }
+ mLatest.dump(pw);
+ }
+
+ synchronized void dump(ProtoOutputStream proto, long fieldId) {
+ mFirst.dump(proto, fieldId);
+ if (mCount > 1) {
+ mLatest.dump(proto, fieldId);
+ }
+ }
+ }
+
+ /**
+ * Represents a single log entry that can be grouped and aggregated for compact logging.
+ *
+ * <p>Entries are first grouped by an integer group key, and then aggregated with consecutive
+ * entries of same group within a limited timespan.
+ */
+ interface SingleLogRecord {
+
+ /** The group identifier for this record (e.g. vibration usage). */
+ int getGroupKey();
+
+ /**
+ * The timestamp in millis that should be used for aggregation of close entries.
+ *
+ * <p>Should be {@link SystemClock#uptimeMillis()} to be used for calculations.
+ */
+ long getCreateUptimeMs();
+
+ /**
+ * Returns true if this record can be aggregated with the given one (e.g. the represent the
+ * same vibration request from the same process client).
+ */
+ boolean mayAggregate(SingleLogRecord record);
+
+ /** Writes this record into given {@link IndentingPrintWriter}. */
+ void dump(IndentingPrintWriter pw);
+
+ /** Writes this record into given {@link ProtoOutputStream} field. */
+ void dump(ProtoOutputStream proto, long fieldId);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 70e2e27a3bae..8f755f4ecec8 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -54,12 +54,18 @@ final class HalVibration extends Vibration {
/** Vibration status. */
private Vibration.Status mStatus;
+ /** Reported scale values applied to the vibration effects. */
+ private int mScaleLevel;
+ private float mAdaptiveScale;
+
HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect,
@NonNull CallerInfo callerInfo) {
super(token, callerInfo);
mOriginalEffect = effect;
mEffectToPlay = effect;
mStatus = Vibration.Status.RUNNING;
+ mScaleLevel = VibrationScaler.SCALE_NONE;
+ mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE;
}
/**
@@ -119,20 +125,24 @@ final class HalVibration extends Vibration {
}
/**
- * Scales the {@link #getEffectToPlay()} and each fallback effect with a scaling transformation.
- *
- * @param scaler A {@link VibrationEffect.Transformation<Integer>} that takes one of the
- * {@code VibrationAttributes.USAGE_*} as the modifier to scale the effect
- * based on the user settings.
+ * Scales the {@link #getEffectToPlay()} and each fallback effect based on the vibration usage.
*/
- public void scaleEffects(VibrationEffect.Transformation<Integer> scaler) {
+ public void scaleEffects(VibrationScaler scaler) {
int vibrationUsage = callerInfo.attrs.getUsage();
- CombinedVibration newEffect = mEffectToPlay.transform(scaler, vibrationUsage);
+
+ // Save scale values for debugging purposes.
+ mScaleLevel = scaler.getScaleLevel(vibrationUsage);
+ mAdaptiveScale = scaler.getAdaptiveHapticsScale(vibrationUsage);
+
+ // Scale all VibrationEffect instances in given CombinedVibration.
+ CombinedVibration newEffect = mEffectToPlay.transform(scaler::scale, vibrationUsage);
if (!Objects.equals(mEffectToPlay, newEffect)) {
mEffectToPlay = newEffect;
}
+
+ // Scale all fallback VibrationEffect instances that can be used by VibrationThread.
for (int i = 0; i < mFallbacks.size(); i++) {
- mFallbacks.setValueAt(i, scaler.transform(mFallbacks.valueAt(i), vibrationUsage));
+ mFallbacks.setValueAt(i, scaler.scale(mFallbacks.valueAt(i), vibrationUsage));
}
}
@@ -171,7 +181,7 @@ final class HalVibration extends Vibration {
CombinedVibration originalEffect =
Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect;
return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect,
- /* scale= */ 0, callerInfo);
+ mScaleLevel, mAdaptiveScale, callerInfo);
}
/** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index a34621642bcd..9756094e5af0 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -19,8 +19,8 @@ package com.android.server.vibrator;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.VibrationEffect;
-import android.os.vibrator.Flags;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
import android.os.vibrator.persistence.ParsedVibration;
import android.os.vibrator.persistence.VibrationXmlParser;
import android.text.TextUtils;
@@ -73,8 +73,6 @@ import java.io.IOException;
*
* <p>After a successful parsing of the customization XML file, it returns a {@link SparseArray}
* that maps each customized haptic feedback effect ID to its respective {@link VibrationEffect}.
- *
- * @hide
*/
final class HapticFeedbackCustomization {
private static final String TAG = "HapticFeedbackCustomization";
@@ -104,8 +102,6 @@ final class HapticFeedbackCustomization {
* @throws {@link IOException} if an IO error occurs while parsing the customization XML.
* @throws {@link CustomizationParserException} for any non-IO error that occurs when parsing
* the XML, like an invalid XML content or an invalid haptic feedback constant.
- *
- * @hide
*/
@Nullable
static SparseArray<VibrationEffect> loadVibrations(Resources res, VibratorInfo vibratorInfo)
@@ -202,8 +198,6 @@ final class HapticFeedbackCustomization {
/**
* Represents an error while parsing a haptic feedback customization XML.
- *
- * @hide
*/
static final class CustomizationParserException extends Exception {
private CustomizationParserException(String message) {
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 519acec2f7b4..96f045d7e258 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -34,8 +34,6 @@ import java.io.PrintWriter;
/**
* Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
- *
- * @hide
*/
public final class HapticFeedbackVibrationProvider {
private static final String TAG = "HapticFeedbackVibrationProvider";
@@ -58,17 +56,14 @@ public final class HapticFeedbackVibrationProvider {
private float mKeyboardVibrationFixedAmplitude;
- /** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
this(res, vibrator.getInfo());
}
- /** @hide */
public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
}
- /** @hide */
@VisibleForTesting HapticFeedbackVibrationProvider(
Resources res,
VibratorInfo vibratorInfo,
@@ -190,10 +185,11 @@ public final class HapticFeedbackVibrationProvider {
* to get.
* @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
* vibration intensity settings. {@code false} otherwise.
+ * @param fromIme the haptic feedback is performed from an IME.
* @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
*/
public VibrationAttributes getVibrationAttributesForHapticFeedback(
- int effectId, boolean bypassVibrationIntensitySetting) {
+ int effectId, boolean bypassVibrationIntensitySetting, boolean fromIme) {
VibrationAttributes attrs;
switch (effectId) {
case HapticFeedbackConstants.EDGE_SQUEEZE:
@@ -209,7 +205,7 @@ public final class HapticFeedbackVibrationProvider {
break;
case HapticFeedbackConstants.KEYBOARD_TAP:
case HapticFeedbackConstants.KEYBOARD_RELEASE:
- attrs = createKeyboardVibrationAttributes();
+ attrs = createKeyboardVibrationAttributes(fromIme);
break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
@@ -222,7 +218,7 @@ public final class HapticFeedbackVibrationProvider {
if (shouldBypassInterruptionPolicy(effectId)) {
flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
}
- if (shouldBypassIntensityScale(effectId)) {
+ if (shouldBypassIntensityScale(effectId, fromIme)) {
flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
}
@@ -337,9 +333,9 @@ public final class HapticFeedbackVibrationProvider {
/* fallbackForPredefinedEffect= */ predefinedEffectFallback);
}
- private boolean shouldBypassIntensityScale(int effectId) {
- if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0) {
- // shouldn't bypass if not support keyboard category or no fixed amplitude
+ private boolean shouldBypassIntensityScale(int effectId, boolean isIme) {
+ if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0 || !isIme) {
+ // Shouldn't bypass if not support keyboard category, no fixed amplitude or not an IME.
return false;
}
switch (effectId) {
@@ -353,8 +349,9 @@ public final class HapticFeedbackVibrationProvider {
return false;
}
- private static VibrationAttributes createKeyboardVibrationAttributes() {
- if (!Flags.keyboardCategoryEnabled()) {
+ private VibrationAttributes createKeyboardVibrationAttributes(boolean fromIme) {
+ // Use touch attribute when the keyboard category is disable or it's not from an IME.
+ if (!Flags.keyboardCategoryEnabled() || !fromIme) {
return TOUCH_VIBRATION_ATTRIBUTES;
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index b2e808ac8e95..b490f57a936e 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -218,12 +218,13 @@ abstract class Vibration {
private final long mDurationMs;
@Nullable
private final CombinedVibration mOriginalEffect;
- private final float mScale;
+ private final int mScaleLevel;
+ private final float mAdaptiveScale;
private final Status mStatus;
DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
- @Nullable CombinedVibration originalEffect, float scale,
- @NonNull CallerInfo callerInfo) {
+ @Nullable CombinedVibration originalEffect, int scaleLevel,
+ float adaptiveScale, @NonNull CallerInfo callerInfo) {
Objects.requireNonNull(callerInfo);
mCreateTime = stats.getCreateTimeDebug();
mStartTime = stats.getStartTimeDebug();
@@ -231,7 +232,8 @@ abstract class Vibration {
mDurationMs = stats.getDurationDebug();
mPlayedEffect = playedEffect;
mOriginalEffect = originalEffect;
- mScale = scale;
+ mScaleLevel = scaleLevel;
+ mAdaptiveScale = adaptiveScale;
mCallerInfo = callerInfo;
mStatus = status;
}
@@ -246,7 +248,8 @@ abstract class Vibration {
+ ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ ", playedEffect: " + mPlayedEffect
+ ", originalEffect: " + mOriginalEffect
- + ", scale: " + String.format(Locale.ROOT, "%.2f", mScale)
+ + ", scaleLevel: " + VibrationScaler.scaleLevelToString(mScaleLevel)
+ + ", adaptiveScale: " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale)
+ ", callerInfo: " + mCallerInfo;
}
@@ -259,26 +262,39 @@ abstract class Vibration {
void dumpCompact(IndentingPrintWriter pw) {
boolean isExternalVibration = mPlayedEffect == null;
String timingsStr = String.format(Locale.ROOT,
- "%s | %8s | %20s | duration: %5dms | start: %12s | end: %10s",
+ "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s",
DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
isExternalVibration ? "external" : "effect",
mStatus.name().toLowerCase(Locale.ROOT),
mDurationMs,
mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
- String callerInfoStr = String.format(Locale.ROOT,
- " | %s (uid=%d, deviceId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
- mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId,
- mCallerInfo.attrs.usageToString(),
- AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
+ String paramStr = String.format(Locale.ROOT,
+ " | scale: %8s (adaptive=%.2f) | flags: %4s | usage: %s",
+ VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale,
Long.toBinaryString(mCallerInfo.attrs.getFlags()),
- mCallerInfo.reason);
+ mCallerInfo.attrs.usageToString());
+ // Optional, most vibrations have category unknown so skip them to simplify the logs
+ String categoryStr =
+ mCallerInfo.attrs.getCategory() != VibrationAttributes.CATEGORY_UNKNOWN
+ ? " | category=" + VibrationAttributes.categoryToString(
+ mCallerInfo.attrs.getCategory())
+ : "";
+ // Optional, most vibrations should not be defined via AudioAttributes
+ // so skip them to simplify the logs
+ String audioUsageStr =
+ mCallerInfo.attrs.getOriginalAudioUsage() != AudioAttributes.USAGE_UNKNOWN
+ ? " | audioUsage=" + AudioAttributes.usageToString(
+ mCallerInfo.attrs.getOriginalAudioUsage())
+ : "";
+ String callerStr = String.format(Locale.ROOT,
+ " | %s (uid=%d, deviceId=%d) | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, mCallerInfo.reason);
String effectStr = String.format(Locale.ROOT,
- " | played: %s | original: %s | scale: %.2f",
+ " | played: %s | original: %s",
mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
- mOriginalEffect == null ? null : mOriginalEffect.toDebugString(),
- mScale);
- pw.println(timingsStr + callerInfoStr + effectStr);
+ mOriginalEffect == null ? null : mOriginalEffect.toDebugString());
+ pw.println(timingsStr + paramStr + categoryStr + audioUsageStr + callerStr + effectStr);
}
/** Write this info into given {@link PrintWriter}. */
@@ -293,7 +309,8 @@ abstract class Vibration {
+ (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime))));
pw.println("playedEffect = " + mPlayedEffect);
pw.println("originalEffect = " + mOriginalEffect);
- pw.println("scale = " + String.format(Locale.ROOT, "%.2f", mScale));
+ pw.println("scale = " + VibrationScaler.scaleLevelToString(mScaleLevel));
+ pw.println("adaptiveScale = " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale));
pw.println("callerInfo = " + mCallerInfo);
pw.decreaseIndent();
}
@@ -310,6 +327,7 @@ abstract class Vibration {
final VibrationAttributes attrs = mCallerInfo.attrs;
proto.write(VibrationAttributesProto.USAGE, attrs.getUsage());
proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage());
+ proto.write(VibrationAttributesProto.CATEGORY, attrs.getCategory());
proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags());
proto.end(attrsToken);
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 5d17884c769b..d9ca71003aae 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -26,10 +26,14 @@ import android.os.Vibrator;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Locale;
/** Controls vibration scaling. */
final class VibrationScaler {
@@ -37,13 +41,12 @@ final class VibrationScaler {
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
- private static final int SCALE_VERY_LOW =
- ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
- private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
- private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
- private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
- private static final int SCALE_VERY_HIGH =
- ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
+ static final int SCALE_VERY_LOW = ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
+ static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
+ static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
+ static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
+ static final int SCALE_VERY_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
+ static final float ADAPTIVE_SCALE_NONE = 1f;
// Scale factors for each level.
private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
@@ -52,6 +55,8 @@ final class VibrationScaler {
private static final float SCALE_FACTOR_HIGH = 1.2f;
private static final float SCALE_FACTOR_VERY_HIGH = 1.4f;
+ private static final ScaleLevel SCALE_LEVEL_NONE = new ScaleLevel(SCALE_FACTOR_NONE);
+
// A mapping from the intensity adjustment to the scaling to apply, where the intensity
// adjustment is defined as the delta between the default intensity level and the user selected
// intensity level. It's important that we apply the scaling on the delta between the two so
@@ -69,7 +74,7 @@ final class VibrationScaler {
mScaleLevels = new SparseArray<>();
mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW));
mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW));
- mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_FACTOR_NONE));
+ mScaleLevels.put(SCALE_NONE, SCALE_LEVEL_NONE);
mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
}
@@ -87,25 +92,24 @@ final class VibrationScaler {
* @param usageHint one of VibrationAttributes.USAGE_*
* @return one of ExternalVibrationScale.ScaleLevel.SCALE_*
*/
- public int getExternalVibrationScaleLevel(int usageHint) {
+ public int getScaleLevel(int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
// Bypassing user settings, or it has changed between checking and scaling. Use default.
return SCALE_NONE;
}
int scaleLevel = currentIntensity - defaultIntensity;
-
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
return scaleLevel;
- } else {
- // Something about our scaling has gone wrong, so just play with no scaling.
- Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level "
- + scaleLevel + " for vibration with usage " + usageHint);
- return SCALE_NONE;
}
+
+ // Something about our scaling has gone wrong, so just play with no scaling.
+ Slog.wtf(TAG, "Error in scaling calculations, ended up with invalid scale level "
+ + scaleLevel + " for vibration with usage " + usageHint);
+
+ return SCALE_NONE;
}
/**
@@ -117,11 +121,9 @@ final class VibrationScaler {
* @return The adaptive haptics scale.
*/
public float getAdaptiveHapticsScale(int usageHint) {
- if (shouldApplyAdaptiveHapticsScale(usageHint)) {
- return mAdaptiveHapticsScales.get(usageHint);
- }
-
- return 1f; // no scaling
+ return Flags.adaptiveHapticsEnabled()
+ ? mAdaptiveHapticsScales.get(usageHint, ADAPTIVE_SCALE_NONE)
+ : ADAPTIVE_SCALE_NONE;
}
/**
@@ -140,21 +142,16 @@ final class VibrationScaler {
return effect;
}
- int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
- int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
- if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- // Bypassing user settings, or it has changed between checking and scaling. Use default.
- currentIntensity = defaultIntensity;
- }
-
- int newEffectStrength = intensityToEffectStrength(currentIntensity);
- ScaleLevel scaleLevel = mScaleLevels.get(currentIntensity - defaultIntensity);
+ int newEffectStrength = getEffectStrength(usageHint);
+ ScaleLevel scaleLevel = mScaleLevels.get(getScaleLevel(usageHint));
+ float adaptiveScale = getAdaptiveHapticsScale(usageHint);
if (scaleLevel == null) {
// Something about our scaling has gone wrong, so just play with no scaling.
- Slog.e(TAG, "No configured scaling level!"
- + " (current=" + currentIntensity + ", default= " + defaultIntensity + ")");
+ Slog.e(TAG, "No configured scaling level found! (current="
+ + mSettingsController.getCurrentIntensity(usageHint) + ", default= "
+ + mSettingsController.getDefaultIntensity(usageHint) + ")");
+ scaleLevel = SCALE_LEVEL_NONE;
}
VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect;
@@ -162,20 +159,11 @@ final class VibrationScaler {
new ArrayList<>(composedEffect.getSegments());
int segmentCount = segments.size();
for (int i = 0; i < segmentCount; i++) {
- VibrationEffectSegment segment = segments.get(i);
- segment = segment.resolve(mDefaultVibrationAmplitude)
- .applyEffectStrength(newEffectStrength);
- if (scaleLevel != null) {
- segment = segment.scale(scaleLevel.factor);
- }
-
- // If adaptive haptics scaling is available for this usage, apply it to the segment.
- if (shouldApplyAdaptiveHapticsScale(usageHint)) {
- float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
- segment = segment.scaleLinearly(adaptiveScale);
- }
-
- segments.set(i, segment);
+ segments.set(i,
+ segments.get(i).resolve(mDefaultVibrationAmplitude)
+ .applyEffectStrength(newEffectStrength)
+ .scale(scaleLevel.factor)
+ .scaleLinearly(adaptiveScale));
}
if (segments.equals(composedEffect.getSegments())) {
// No segment was updated, return original effect.
@@ -197,15 +185,7 @@ final class VibrationScaler {
* updated effect strength
*/
public PrebakedSegment scale(PrebakedSegment prebaked, int usageHint) {
- int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
- if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- // Bypassing user settings, or it has changed between checking and scaling. Use default.
- currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
- }
-
- int newEffectStrength = intensityToEffectStrength(currentIntensity);
- return prebaked.applyEffectStrength(newEffectStrength);
+ return prebaked.applyEffectStrength(getEffectStrength(usageHint));
}
/**
@@ -213,8 +193,6 @@ final class VibrationScaler {
*
* @param usageHint one of VibrationAttributes.USAGE_*.
* @param scale The scaling factor that should be applied to vibrations of this usage.
- *
- * @hide
*/
public void updateAdaptiveHapticsScale(@VibrationAttributes.Usage int usageHint, float scale) {
mAdaptiveHapticsScales.put(usageHint, scale);
@@ -224,24 +202,68 @@ final class VibrationScaler {
* Removes the usage from the cached adaptive haptics scales list.
*
* @param usageHint one of VibrationAttributes.USAGE_*.
- *
- * @hide
*/
public void removeAdaptiveHapticsScale(@VibrationAttributes.Usage int usageHint) {
mAdaptiveHapticsScales.remove(usageHint);
}
- /**
- * Removes all cached adaptive haptics scales.
- *
- * @hide
- */
+ /** Removes all cached adaptive haptics scales. */
public void clearAdaptiveHapticsScales() {
mAdaptiveHapticsScales.clear();
}
- private boolean shouldApplyAdaptiveHapticsScale(int usageHint) {
- return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint);
+ /** Write current settings into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ pw.println("VibrationScaler:");
+ pw.increaseIndent();
+ pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude);
+
+ pw.println("ScaleLevels:");
+ pw.increaseIndent();
+ for (int i = 0; i < mScaleLevels.size(); i++) {
+ int scaleLevelKey = mScaleLevels.keyAt(i);
+ ScaleLevel scaleLevel = mScaleLevels.valueAt(i);
+ pw.println(scaleLevelToString(scaleLevelKey) + " = " + scaleLevel);
+ }
+ pw.decreaseIndent();
+
+ pw.println("AdaptiveHapticsScales:");
+ pw.increaseIndent();
+ for (int i = 0; i < mAdaptiveHapticsScales.size(); i++) {
+ int usage = mAdaptiveHapticsScales.keyAt(i);
+ float scale = mAdaptiveHapticsScales.valueAt(i);
+ pw.println(VibrationAttributes.usageToString(usage)
+ + " = " + String.format(Locale.ROOT, "%.2f", scale));
+ }
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+
+ /** Write current settings into given {@link ProtoOutputStream}. */
+ void dump(ProtoOutputStream proto) {
+ proto.write(VibratorManagerServiceDumpProto.DEFAULT_VIBRATION_AMPLITUDE,
+ mDefaultVibrationAmplitude);
+ }
+
+ @Override
+ public String toString() {
+ return "VibrationScaler{"
+ + "mScaleLevels=" + mScaleLevels
+ + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude
+ + ", mAdaptiveHapticsScales=" + mAdaptiveHapticsScales
+ + '}';
+ }
+
+ private int getEffectStrength(int usageHint) {
+ int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+ if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ // Bypassing user settings, or it has changed between checking and scaling. Use default.
+ currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
+ }
+
+ return intensityToEffectStrength(currentIntensity);
}
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
@@ -259,6 +281,17 @@ final class VibrationScaler {
}
}
+ static String scaleLevelToString(int scaleLevel) {
+ return switch (scaleLevel) {
+ case SCALE_VERY_LOW -> "VERY_LOW";
+ case SCALE_LOW -> "LOW";
+ case SCALE_NONE -> "NONE";
+ case SCALE_HIGH -> "HIGH";
+ case SCALE_VERY_HIGH -> "VERY_HIGH";
+ default -> String.valueOf(scaleLevel);
+ };
+ }
+
/** Represents the scale that must be applied to a vibration effect intensity. */
private static final class ScaleLevel {
public final float factor;
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 99ce3e2fb740..5b77433fa6d9 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -386,7 +386,6 @@ final class VibrationSettings {
* Returns the duration, in milliseconds, that the vibrator control service will wait for new
* vibration params.
* @return The request vibration params timeout in milliseconds.
- * @hide
*/
public int getRequestVibrationParamsTimeoutMs() {
return mVibrationConfig.getRequestVibrationParamsTimeoutMs();
@@ -645,11 +644,16 @@ final class VibrationSettings {
.append("), ");
}
vibrationIntensitiesString.append('}');
+ String keyboardVibrationOnString = mKeyboardVibrationOn
+ + " (default: " + mVibrationConfig.isDefaultKeyboardVibrationEnabled() + ")";
return "VibrationSettings{"
+ "mVibratorConfig=" + mVibrationConfig
+ + ", mVibrateOn=" + mVibrateOn
+ + ", mKeyboardVibrationOn=" + keyboardVibrationOnString
+ ", mVibrateInputDevices=" + mVibrateInputDevices
+ ", mBatterySaverMode=" + mBatterySaverMode
- + ", mVibrateOn=" + mVibrateOn
+ + ", mRingerMode=" + ringerModeToString(mRingerMode)
+ + ", mOnWirelessCharger=" + mOnWirelessCharger
+ ", mVibrationIntensities=" + vibrationIntensitiesString
+ ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ '}';
@@ -658,32 +662,40 @@ final class VibrationSettings {
/** Write current settings into given {@link PrintWriter}. */
void dump(IndentingPrintWriter pw) {
- pw.println("VibrationSettings:");
- pw.increaseIndent();
- pw.println("vibrateOn = " + mVibrateOn);
- pw.println("vibrateInputDevices = " + mVibrateInputDevices);
- pw.println("batterySaverMode = " + mBatterySaverMode);
- pw.println("VibrationIntensities:");
+ synchronized (mLock) {
+ pw.println("VibrationSettings:");
+ pw.increaseIndent();
+ pw.println("vibrateOn = " + mVibrateOn);
+ pw.println("keyboardVibrationOn = " + mKeyboardVibrationOn
+ + ", default: " + mVibrationConfig.isDefaultKeyboardVibrationEnabled());
+ pw.println("vibrateInputDevices = " + mVibrateInputDevices);
+ pw.println("batterySaverMode = " + mBatterySaverMode);
+ pw.println("ringerMode = " + ringerModeToString(mRingerMode));
+ pw.println("onWirelessCharger = " + mOnWirelessCharger);
+ pw.println("processStateCache size = " + mUidObserver.mProcStatesCache.size());
+
+ pw.println("VibrationIntensities:");
+ pw.increaseIndent();
+ for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+ int usage = mCurrentVibrationIntensities.keyAt(i);
+ int intensity = mCurrentVibrationIntensities.valueAt(i);
+ pw.println(VibrationAttributes.usageToString(usage) + " = "
+ + intensityToString(intensity)
+ + ", default: " + intensityToString(getDefaultIntensity(usage)));
+ }
+ pw.decreaseIndent();
- pw.increaseIndent();
- for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
- int usage = mCurrentVibrationIntensities.keyAt(i);
- int intensity = mCurrentVibrationIntensities.valueAt(i);
- pw.println(VibrationAttributes.usageToString(usage) + " = "
- + intensityToString(intensity)
- + ", default: " + intensityToString(getDefaultIntensity(usage)));
+ mVibrationConfig.dumpWithoutDefaultSettings(pw);
+ pw.decreaseIndent();
}
- pw.decreaseIndent();
-
- mVibrationConfig.dumpWithoutDefaultSettings(pw);
- pw.println("processStateCache = " + mUidObserver.mProcStatesCache);
- pw.decreaseIndent();
}
/** Write current settings into given {@link ProtoOutputStream}. */
void dump(ProtoOutputStream proto) {
synchronized (mLock) {
proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
+ proto.write(VibratorManagerServiceDumpProto.KEYBOARD_VIBRATION_ON,
+ mKeyboardVibrationOn);
proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
getCurrentIntensity(USAGE_ALARM));
@@ -723,18 +735,22 @@ final class VibrationSettings {
}
private static String intensityToString(int intensity) {
- switch (intensity) {
- case Vibrator.VIBRATION_INTENSITY_OFF:
- return "OFF";
- case Vibrator.VIBRATION_INTENSITY_LOW:
- return "LOW";
- case Vibrator.VIBRATION_INTENSITY_MEDIUM:
- return "MEDIUM";
- case Vibrator.VIBRATION_INTENSITY_HIGH:
- return "HIGH";
- default:
- return "UNKNOWN INTENSITY " + intensity;
- }
+ return switch (intensity) {
+ case Vibrator.VIBRATION_INTENSITY_OFF -> "OFF";
+ case Vibrator.VIBRATION_INTENSITY_LOW -> "LOW";
+ case Vibrator.VIBRATION_INTENSITY_MEDIUM -> "MEDIUM";
+ case Vibrator.VIBRATION_INTENSITY_HIGH -> "HIGH";
+ default -> "UNKNOWN INTENSITY " + intensity;
+ };
+ }
+
+ private static String ringerModeToString(int ringerMode) {
+ return switch (ringerMode) {
+ case AudioManager.RINGER_MODE_SILENT -> "silent";
+ case AudioManager.RINGER_MODE_VIBRATE -> "vibrate";
+ case AudioManager.RINGER_MODE_NORMAL -> "normal";
+ default -> String.valueOf(ringerMode);
+ };
}
@VibrationIntensity
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index f6af9ad991ff..f510b4e8ab30 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -161,7 +161,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
waitForVibrationParamsIfRequired();
}
// Scale resolves the default amplitudes from the effect before scaling them.
- mVibration.scaleEffects(mVibrationScaler::scale);
+ mVibration.scaleEffects(mVibrationScaler);
} else {
mVibration.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 17a9e3330375..ec3d99b24656 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -30,6 +30,7 @@ import static android.os.VibrationAttributes.USAGE_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.content.Context;
import android.frameworks.vibrator.IVibratorControlService;
import android.frameworks.vibrator.IVibratorController;
import android.frameworks.vibrator.ScaleParam;
@@ -37,27 +38,38 @@ import android.frameworks.vibrator.VibrationParam;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
* Implementation of {@link IVibratorControlService} which allows the registration of
* {@link IVibratorController} to set and receive vibration params.
- *
- * @hide
*/
-public final class VibratorControlService extends IVibratorControlService.Stub {
+final class VibratorControlService extends IVibratorControlService.Stub {
private static final String TAG = "VibratorControlService";
private static final int UNRECOGNIZED_VIBRATION_TYPE = -1;
private static final int NO_SCALE = -1;
+ private static final SimpleDateFormat DEBUG_DATE_TIME_FORMAT =
+ new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
+ private final VibrationParamsRecords mVibrationParamsRecords;
private final VibratorControllerHolder mVibratorControllerHolder;
private final VibrationScaler mVibrationScaler;
private final Object mLock;
@@ -68,25 +80,32 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
@GuardedBy("mLock")
private IBinder mRequestVibrationParamsToken;
- public VibratorControlService(VibratorControllerHolder vibratorControllerHolder,
- VibrationScaler vibrationScaler, VibrationSettings vibrationSettings, Object lock) {
+ VibratorControlService(Context context,
+ VibratorControllerHolder vibratorControllerHolder, VibrationScaler vibrationScaler,
+ VibrationSettings vibrationSettings, Object lock) {
mVibratorControllerHolder = vibratorControllerHolder;
mVibrationScaler = vibrationScaler;
mLock = lock;
mRequestVibrationParamsForUsages = vibrationSettings.getRequestVibrationParamsForUsages();
+
+ int dumpSizeLimit = context.getResources().getInteger(
+ com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit);
+ int dumpAggregationTimeLimit = context.getResources().getInteger(
+ com.android.internal.R.integer
+ .config_previousVibrationsDumpAggregationTimeMillisLimit);
+ mVibrationParamsRecords =
+ new VibrationParamsRecords(dumpSizeLimit, dumpAggregationTimeLimit);
}
@Override
- public void registerVibratorController(IVibratorController controller)
- throws RemoteException {
+ public void registerVibratorController(IVibratorController controller) {
synchronized (mLock) {
mVibratorControllerHolder.setVibratorController(controller);
}
}
@Override
- public void unregisterVibratorController(@NonNull IVibratorController controller)
- throws RemoteException {
+ public void unregisterVibratorController(@NonNull IVibratorController controller) {
Objects.requireNonNull(controller);
synchronized (mLock) {
@@ -110,7 +129,7 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
@Override
public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params,
- @NonNull IVibratorController token) throws RemoteException {
+ @NonNull IVibratorController token) {
Objects.requireNonNull(token);
synchronized (mLock) {
@@ -128,12 +147,12 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
updateAdaptiveHapticsScales(params);
+ recordUpdateVibrationParams(params, /* fromRequest= */ false);
}
}
@Override
- public void clearVibrationParams(int types, @NonNull IVibratorController token)
- throws RemoteException {
+ public void clearVibrationParams(int types, @NonNull IVibratorController token) {
Objects.requireNonNull(token);
synchronized (mLock) {
@@ -151,13 +170,13 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
updateAdaptiveHapticsScales(types, NO_SCALE);
+ recordClearVibrationParams(types);
}
}
@Override
public void onRequestVibrationParamsComplete(
- @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
- throws RemoteException {
+ @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) {
Objects.requireNonNull(requestToken);
synchronized (mLock) {
@@ -177,16 +196,17 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
updateAdaptiveHapticsScales(result);
endOngoingRequestVibrationParamsLocked(/* wasCancelled= */ false);
+ recordUpdateVibrationParams(result, /* fromRequest= */ true);
}
}
@Override
- public int getInterfaceVersion() throws RemoteException {
+ public int getInterfaceVersion() {
return this.VERSION;
}
@Override
- public String getInterfaceHash() throws RemoteException {
+ public String getInterfaceHash() {
return this.HASH;
}
@@ -266,6 +286,42 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
}
+ /** Write current settings into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ boolean isVibratorControllerRegistered;
+ boolean hasPendingVibrationParamsRequest;
+ synchronized (mLock) {
+ isVibratorControllerRegistered =
+ mVibratorControllerHolder.getVibratorController() != null;
+ hasPendingVibrationParamsRequest = mRequestVibrationParamsFuture != null;
+ }
+
+ pw.println("VibratorControlService:");
+ pw.increaseIndent();
+ pw.println("isVibratorControllerRegistered = " + isVibratorControllerRegistered);
+ pw.println("hasPendingVibrationParamsRequest = " + hasPendingVibrationParamsRequest);
+
+ pw.println();
+ pw.println("Vibration parameters update history:");
+ pw.increaseIndent();
+ mVibrationParamsRecords.dump(pw);
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+
+ /** Write current settings into given {@link ProtoOutputStream}. */
+ void dump(ProtoOutputStream proto) {
+ boolean isVibratorControllerRegistered;
+ synchronized (mLock) {
+ isVibratorControllerRegistered =
+ mVibratorControllerHolder.getVibratorController() != null;
+ }
+ proto.write(VibratorManagerServiceDumpProto.IS_VIBRATOR_CONTROLLER_REGISTERED,
+ isVibratorControllerRegistered);
+ mVibrationParamsRecords.dump(proto);
+ }
+
/**
* Completes or cancels the vibration params request future and resets the future and token
* to null.
@@ -312,6 +368,33 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
}
}
+ private static int[] mapFromAdaptiveVibrationTypeToVibrationUsages(int types) {
+ IntArray usages = new IntArray(15);
+ if ((ScaleParam.TYPE_ALARM & types) != 0) {
+ usages.add(USAGE_ALARM);
+ }
+
+ if ((ScaleParam.TYPE_NOTIFICATION & types) != 0) {
+ usages.add(USAGE_NOTIFICATION);
+ usages.add(USAGE_COMMUNICATION_REQUEST);
+ }
+
+ if ((ScaleParam.TYPE_RINGTONE & types) != 0) {
+ usages.add(USAGE_RINGTONE);
+ }
+
+ if ((ScaleParam.TYPE_MEDIA & types) != 0) {
+ usages.add(USAGE_MEDIA);
+ usages.add(USAGE_UNKNOWN);
+ }
+
+ if ((ScaleParam.TYPE_INTERACTIVE & types) != 0) {
+ usages.add(USAGE_TOUCH);
+ usages.add(USAGE_HARDWARE_FEEDBACK);
+ }
+ return usages.toArray();
+ }
+
/**
* Updates the adaptive haptics scales cached in {@link VibrationScaler} with the
* provided params.
@@ -319,7 +402,14 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
* @param params the new vibration params.
*/
private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) {
+ if (params == null) {
+ return;
+ }
for (VibrationParam param : params) {
+ if (param.getTag() != VibrationParam.scale) {
+ Slog.e(TAG, "Unsupported vibration param: " + param);
+ continue;
+ }
ScaleParam scaleParam = param.getScale();
updateAdaptiveHapticsScales(scaleParam.typesMask, scaleParam.scale);
}
@@ -333,27 +423,8 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
* @param scale The scaling factor that should be applied to the vibrations.
*/
private void updateAdaptiveHapticsScales(int types, float scale) {
- if ((ScaleParam.TYPE_ALARM & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_ALARM, scale);
- }
-
- if ((ScaleParam.TYPE_NOTIFICATION & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_NOTIFICATION, scale);
- updateOrRemoveAdaptiveHapticsScale(USAGE_COMMUNICATION_REQUEST, scale);
- }
-
- if ((ScaleParam.TYPE_RINGTONE & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_RINGTONE, scale);
- }
-
- if ((ScaleParam.TYPE_MEDIA & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_MEDIA, scale);
- updateOrRemoveAdaptiveHapticsScale(USAGE_UNKNOWN, scale);
- }
-
- if ((ScaleParam.TYPE_INTERACTIVE & types) != 0) {
- updateOrRemoveAdaptiveHapticsScale(USAGE_TOUCH, scale);
- updateOrRemoveAdaptiveHapticsScale(USAGE_HARDWARE_FEEDBACK, scale);
+ for (int usage : mapFromAdaptiveVibrationTypeToVibrationUsages(types)) {
+ updateOrRemoveAdaptiveHapticsScale(usage, scale);
}
}
@@ -375,4 +446,136 @@ public final class VibratorControlService extends IVibratorControlService.Stub {
mVibrationScaler.updateAdaptiveHapticsScale(usageHint, scale);
}
+
+ private void recordUpdateVibrationParams(@Nullable VibrationParam[] params,
+ boolean fromRequest) {
+ if (params == null) {
+ return;
+ }
+ VibrationParamsRecords.Operation operation =
+ fromRequest ? VibrationParamsRecords.Operation.PULL
+ : VibrationParamsRecords.Operation.PUSH;
+ long createTime = SystemClock.uptimeMillis();
+ for (VibrationParam param : params) {
+ if (param.getTag() != VibrationParam.scale) {
+ Slog.w(TAG, "Unsupported vibration param ignored from dumpsys records: " + param);
+ continue;
+ }
+ ScaleParam scaleParam = param.getScale();
+ mVibrationParamsRecords.add(new VibrationScaleParamRecord(operation, createTime,
+ scaleParam.typesMask, scaleParam.scale));
+ }
+ }
+
+ private void recordClearVibrationParams(int typesMask) {
+ long createTime = SystemClock.uptimeMillis();
+ mVibrationParamsRecords.add(new VibrationScaleParamRecord(
+ VibrationParamsRecords.Operation.CLEAR, createTime, typesMask, NO_SCALE));
+ }
+
+ /**
+ * Keep records of {@link VibrationParam} values received by this service from a registered
+ * {@link VibratorController} and provide debug information for this service.
+ */
+ private static final class VibrationParamsRecords
+ extends GroupedAggregatedLogRecords<VibrationScaleParamRecord> {
+
+ /** The type of operations on vibration parameters that the service is recording. */
+ enum Operation {
+ PULL, PUSH, CLEAR
+ };
+
+ VibrationParamsRecords(int sizeLimit, int aggregationTimeLimit) {
+ super(sizeLimit, aggregationTimeLimit);
+ }
+
+ @Override
+ synchronized void dumpGroupHeader(IndentingPrintWriter pw, int paramType) {
+ if (paramType == VibrationParam.scale) {
+ pw.println("SCALE:");
+ } else {
+ pw.println("UNKNOWN:");
+ }
+ }
+
+ @Override
+ synchronized long findGroupKeyProtoFieldId(int usage) {
+ return VibratorManagerServiceDumpProto.PREVIOUS_VIBRATION_PARAMS;
+ }
+ }
+
+ /**
+ * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
+ * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
+ */
+ private static final class VibrationScaleParamRecord
+ implements GroupedAggregatedLogRecords.SingleLogRecord {
+
+ private final VibrationParamsRecords.Operation mOperation;
+ private final long mCreateTime;
+ private final int mTypesMask;
+ private final float mScale;
+
+ VibrationScaleParamRecord(VibrationParamsRecords.Operation operation, long createTime,
+ int typesMask, float scale) {
+ mOperation = operation;
+ mCreateTime = createTime;
+ mTypesMask = typesMask;
+ mScale = scale;
+ }
+
+ @Override
+ public int getGroupKey() {
+ return VibrationParam.scale;
+ }
+
+ @Override
+ public long getCreateUptimeMs() {
+ return mCreateTime;
+ }
+
+ @Override
+ public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) {
+ if (!(record instanceof VibrationScaleParamRecord param)) {
+ return false;
+ }
+ return mTypesMask == param.mTypesMask && mOperation == param.mOperation;
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ String line = String.format(Locale.ROOT,
+ "%s | %6s | scale: %5s | typesMask: %6s | usages: %s",
+ DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
+ mOperation.name().toLowerCase(Locale.ROOT),
+ (mScale == NO_SCALE) ? "" : String.format(Locale.ROOT, "%.2f", mScale),
+ Long.toBinaryString(mTypesMask), createVibrationUsagesString());
+ pw.println(line);
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(VibrationParamProto.CREATE_TIME, mCreateTime);
+ proto.write(VibrationParamProto.IS_FROM_REQUEST,
+ mOperation == VibrationParamsRecords.Operation.PULL);
+
+ final long scaleToken = proto.start(VibrationParamProto.SCALE);
+ proto.write(VibrationScaleParamProto.TYPES_MASK, mTypesMask);
+ proto.write(VibrationScaleParamProto.SCALE, mScale);
+ proto.end(scaleToken);
+
+ proto.end(token);
+ }
+
+ private String createVibrationUsagesString() {
+ StringBuilder sb = new StringBuilder();
+ int[] usages = mapFromAdaptiveVibrationTypeToVibrationUsages(mTypesMask);
+ for (int i = 0; i < usages.length; i++) {
+ if (i > 0) sb.append(", ");
+ sb.append(VibrationAttributes.usageToString(usages[i]));
+ }
+ return sb.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index f5d4d1e3926b..6710d02bee90 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -17,7 +17,6 @@
package com.android.server.vibrator;
import android.annotation.Nullable;
-import android.annotation.Nullable;
import android.hardware.vibrator.IVibrator;
import android.os.Binder;
import android.os.IVibratorStateListener;
@@ -354,13 +353,13 @@ final class VibratorController {
}
void dump(IndentingPrintWriter pw) {
- pw.println("VibratorController:");
+ pw.println("Vibrator (id=" + mVibratorInfo.getId() + "):");
pw.increaseIndent();
pw.println("isVibrating = " + mIsVibrating);
pw.println("isUnderExternalControl = " + mIsUnderExternalControl);
pw.println("currentAmplitude = " + mCurrentAmplitude);
pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
- pw.println("vibratorStateListenerCount = "
+ pw.println("vibratorStateListener size = "
+ mVibratorStateListeners.getRegisteredCallbackCount());
mVibratorInfo.dump(pw);
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
index 79a99b3ee2ff..b49fb85ecf3f 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java
@@ -24,8 +24,6 @@ import android.util.Slog;
/**
* Holder class for {@link IVibratorController}.
- *
- * @hide
*/
public final class VibratorControllerHolder implements IBinder.DeathRecipient {
private static final String TAG = "VibratorControllerHolder";
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 78e0ebbb53fa..c1bf0393fc85 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -16,7 +16,6 @@
package com.android.server.vibrator;
-import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -84,7 +83,6 @@ import java.lang.ref.WeakReference;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@@ -217,7 +215,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibrationSettings = new VibrationSettings(mContext, mHandler);
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
- mVibratorControlService = new VibratorControlService(
+ mVibratorControlService = new VibratorControlService(mContext,
injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
mLock);
mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
@@ -416,14 +414,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override // Binder call
- public void performHapticFeedback(
- int uid, int deviceId, String opPkg, int constant, boolean always, String reason) {
+ public void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
+ boolean always, String reason, boolean fromIme) {
// Note that the `performHapticFeedback` method does not take a token argument from the
// caller, and instead, uses this service as the token. This is to mitigate performance
// impact that would otherwise be caused due to marshal latency. Haptic feedback effects are
// short-lived, so we don't need to cancel when the process dies.
performHapticFeedbackInternal(
- uid, deviceId, opPkg, constant, always, reason, /* token= */ this);
+ uid, deviceId, opPkg, constant, always, reason, /* token= */ this, fromIme);
}
/**
@@ -435,7 +433,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Nullable
HalVibration performHapticFeedbackInternal(
int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
- IBinder token) {
+ IBinder token, boolean fromIme) {
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
if (hapticVibrationProvider == null) {
Slog.w(TAG, "performHapticFeedback; haptic vibration provider not ready.");
@@ -449,7 +447,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
CombinedVibration combinedVibration = CombinedVibration.createParallel(effect);
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
- constant, /* bypassVibrationIntensitySetting= */ always);
+ constant, /* bypassVibrationIntensitySetting= */ always, fromIme);
VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
@@ -639,13 +637,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ " ");
synchronized (mLock) {
- pw.println("Vibrator Manager Service:");
+ pw.println("VibratorManagerService:");
pw.increaseIndent();
mVibrationSettings.dump(pw);
pw.println();
- pw.println("VibratorControllers:");
+ mVibrationScaler.dump(pw);
+ pw.println();
+
+ pw.println("Vibrators:");
pw.increaseIndent();
for (int i = 0; i < mVibrators.size(); i++) {
mVibrators.valueAt(i).dump(pw);
@@ -686,6 +687,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println();
pw.println();
mVibratorManagerRecords.dump(pw);
+
+ pw.println();
+ pw.println();
+ mVibratorControlService.dump(pw);
}
private void dumpProto(FileDescriptor fd) {
@@ -695,6 +700,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
synchronized (mLock) {
mVibrationSettings.dump(proto);
+ mVibrationScaler.dump(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
@@ -716,6 +722,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
isUnderExternalControl);
}
mVibratorManagerRecords.dump(proto);
+ mVibratorControlService.dump(proto);
proto.flush();
}
@@ -887,7 +894,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (!vib.callerInfo.attrs.isFlagSet(
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
// Scale resolves the default amplitudes from the effect before scaling them.
- vib.scaleEffects(mVibrationScaler::scale);
+ vib.scaleEffects(mVibrationScaler);
} else {
vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
@@ -1663,7 +1670,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public Vibration.DebugInfo getDebugInfo() {
return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
- /* originalEffect= */ null, scale.scaleLevel, callerInfo);
+ /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale,
+ callerInfo);
}
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1739,8 +1747,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
int aggregationTimeLimit) {
mAggregatedVibrationHistory =
new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit);
- mRecentVibrations = new VibrationRecords(
- recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
+ // Recent vibrations are not aggregated, to help debugging issues that just happened.
+ mRecentVibrations =
+ new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
}
synchronized void record(HalVibration vib) {
@@ -1752,9 +1761,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
private synchronized void record(Vibration.DebugInfo info) {
- AggregatedVibrationRecord removedRecord = mRecentVibrations.record(info);
- if (removedRecord != null) {
- mAggregatedVibrationHistory.record(removedRecord.mLatestVibration);
+ GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord =
+ mRecentVibrations.add(new VibrationRecord(info));
+ if (droppedRecord != null) {
+ // Move dropped record from recent list to aggregated history list.
+ mAggregatedVibrationHistory.add(droppedRecord.getLatest());
}
}
@@ -1763,9 +1774,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.increaseIndent();
mRecentVibrations.dump(pw);
pw.decreaseIndent();
+
pw.println();
pw.println();
-
pw.println("Aggregated vibration history:");
pw.increaseIndent();
mAggregatedVibrationHistory.dump(pw);
@@ -1778,127 +1789,75 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/** Keep records of vibrations played and provide debug information for this service. */
- private static final class VibrationRecords {
- private final SparseArray<LinkedList<AggregatedVibrationRecord>> mVibrations =
- new SparseArray<>();
- private final int mSizeLimit;
- private final int mAggregationTimeLimit;
+ private static final class VibrationRecords
+ extends GroupedAggregatedLogRecords<VibrationRecord> {
VibrationRecords(int sizeLimit, int aggregationTimeLimit) {
- mSizeLimit = sizeLimit;
- mAggregationTimeLimit = aggregationTimeLimit;
+ super(sizeLimit, aggregationTimeLimit);
}
- synchronized AggregatedVibrationRecord record(Vibration.DebugInfo info) {
- int usage = info.mCallerInfo.attrs.getUsage();
- if (!mVibrations.contains(usage)) {
- mVibrations.put(usage, new LinkedList<>());
- }
- LinkedList<AggregatedVibrationRecord> records = mVibrations.get(usage);
- if (mAggregationTimeLimit > 0 && !records.isEmpty()) {
- AggregatedVibrationRecord lastRecord = records.getLast();
- if (lastRecord.mayAggregate(info, mAggregationTimeLimit)) {
- lastRecord.record(info);
- return null;
- }
- }
- AggregatedVibrationRecord removedRecord = null;
- if (records.size() > mSizeLimit) {
- removedRecord = records.removeFirst();
- }
- records.addLast(new AggregatedVibrationRecord(info));
- return removedRecord;
- }
-
- synchronized void dump(IndentingPrintWriter pw) {
- for (int i = 0; i < mVibrations.size(); i++) {
- pw.println(VibrationAttributes.usageToString(mVibrations.keyAt(i)) + ":");
- pw.increaseIndent();
- for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
- info.dump(pw);
- }
- pw.decreaseIndent();
- pw.println();
- }
- }
-
- synchronized void dump(ProtoOutputStream proto) {
- for (int i = 0; i < mVibrations.size(); i++) {
- long fieldId;
- switch (mVibrations.keyAt(i)) {
- case VibrationAttributes.USAGE_RINGTONE:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
- break;
- case VibrationAttributes.USAGE_NOTIFICATION:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
- break;
- case VibrationAttributes.USAGE_ALARM:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
- break;
- default:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
- }
- for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
- if (info.mLatestVibration.mPlayedEffect == null) {
- // External vibrations are reported separately in the dump proto
- info.dump(proto,
- VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
- } else {
- info.dump(proto, fieldId);
- }
- }
- }
+ @Override
+ void dumpGroupHeader(IndentingPrintWriter pw, int usage) {
+ pw.println(VibrationAttributes.usageToString(usage) + ":");
}
- synchronized void dumpOnSingleField(ProtoOutputStream proto, long fieldId) {
- for (int i = 0; i < mVibrations.size(); i++) {
- for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
- info.dump(proto, fieldId);
- }
- }
+ @Override
+ long findGroupKeyProtoFieldId(int usage) {
+ return switch (usage) {
+ case VibrationAttributes.USAGE_RINGTONE ->
+ VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
+ case VibrationAttributes.USAGE_NOTIFICATION ->
+ VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
+ case VibrationAttributes.USAGE_ALARM ->
+ VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
+ default ->
+ VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
+ };
}
}
/**
- * Record that keeps the last {@link Vibration.DebugInfo} played, aggregating close vibrations
- * from the same uid that have the same {@link VibrationAttributes} and {@link VibrationEffect}.
+ * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
+ * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
*/
- private static final class AggregatedVibrationRecord {
- private final Vibration.DebugInfo mFirstVibration;
- private Vibration.DebugInfo mLatestVibration;
- private int mVibrationCount;
+ private static final class VibrationRecord
+ implements GroupedAggregatedLogRecords.SingleLogRecord {
+ private final Vibration.DebugInfo mInfo;
- AggregatedVibrationRecord(Vibration.DebugInfo info) {
- mLatestVibration = mFirstVibration = info;
- mVibrationCount = 1;
+ VibrationRecord(Vibration.DebugInfo info) {
+ mInfo = info;
}
- synchronized boolean mayAggregate(Vibration.DebugInfo info, long timeLimit) {
- return Objects.equals(mLatestVibration.mCallerInfo.uid, info.mCallerInfo.uid)
- && Objects.equals(mLatestVibration.mCallerInfo.attrs, info.mCallerInfo.attrs)
- && Objects.equals(mLatestVibration.mPlayedEffect, info.mPlayedEffect)
- && Math.abs(mLatestVibration.mCreateTime - info.mCreateTime) < timeLimit;
+ @Override
+ public int getGroupKey() {
+ return mInfo.mCallerInfo.attrs.getUsage();
}
- synchronized void record(Vibration.DebugInfo vib) {
- mLatestVibration = vib;
- mVibrationCount++;
+ @Override
+ public long getCreateUptimeMs() {
+ return mInfo.mCreateTime;
}
- synchronized void dump(IndentingPrintWriter pw) {
- mFirstVibration.dumpCompact(pw);
- if (mVibrationCount == 1) {
- return;
- }
- if (mVibrationCount > 2) {
- pw.println(
- "-> Skipping " + (mVibrationCount - 2) + " aggregated vibrations, latest:");
+ @Override
+ public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) {
+ if (!(record instanceof VibrationRecord)) {
+ return false;
}
- mLatestVibration.dumpCompact(pw);
+ Vibration.DebugInfo info = ((VibrationRecord) record).mInfo;
+ return mInfo.mCallerInfo.uid == info.mCallerInfo.uid
+ && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs)
+ && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect);
}
- synchronized void dump(ProtoOutputStream proto, long fieldId) {
- mLatestVibration.dump(proto, fieldId);
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ // Prints a compact version of each vibration request for dumpsys.
+ mInfo.dumpCompact(pw);
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ mInfo.dump(proto, fieldId);
}
}
@@ -2001,7 +1960,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override
public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
-
if (!hasExternalControlCapability()) {
return SCALE_MUTE;
}
@@ -2085,10 +2043,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
mCurrentExternalVibration = vibHolder;
vibHolder.linkToDeath();
- vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel(
- attrs.getUsage());
- vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale(
- attrs.getUsage());
+ vibHolder.scale.scaleLevel = mVibrationScaler.getScaleLevel(attrs.getUsage());
+ vibHolder.scale.adaptiveHapticsScale =
+ mVibrationScaler.getAdaptiveHapticsScale(attrs.getUsage());
}
if (waitForCompletion) {
@@ -2300,7 +2257,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant,
/* always= */ commonOptions.force, /* reason= */ commonOptions.description,
- deathBinder);
+ deathBinder, false /* fromIme */);
maybeWaitOnVibration(vib, commonOptions);
return 0;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cd968067e289..fa76774a604f 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -520,11 +520,37 @@ class InsetsSourceProvider {
updateVisibility();
mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
mClientVisible, surfacePosition, getInsetsHint());
+ mStateController.notifySurfaceTransactionReady(this, getSurfaceTransactionId(leash), true);
ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
}
+ private long getSurfaceTransactionId(SurfaceControl leash) {
+ // Here returns mNativeObject (long) as the ID instead of the leash itself so that
+ // InsetsStateController won't keep referencing the leash unexpectedly.
+ return leash != null ? leash.mNativeObject : 0;
+ }
+
+ /**
+ * This is called when the surface transaction of the leash initialization has been committed.
+ *
+ * @param id Indicates which transaction is committed so that stale callbacks can be dropped.
+ */
+ void onSurfaceTransactionCommitted(long id) {
+ if (mIsLeashReadyForDispatching) {
+ return;
+ }
+ if (mControl == null) {
+ return;
+ }
+ if (id != getSurfaceTransactionId(mControl.getLeash())) {
+ return;
+ }
+ mIsLeashReadyForDispatching = true;
+ mStateController.notifySurfaceTransactionReady(this, 0, false);
+ }
+
void startSeamlessRotation() {
if (!mSeamlessRotating) {
mSeamlessRotating = true;
@@ -545,10 +571,6 @@ class InsetsSourceProvider {
return true;
}
- void onSurfaceTransactionApplied() {
- mIsLeashReadyForDispatching = true;
- }
-
void setClientVisible(boolean clientVisible) {
if (mClientVisible == clientVisible) {
return;
@@ -733,6 +755,7 @@ class InsetsSourceProvider {
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mAdapter == this) {
mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
+ mStateController.notifySurfaceTransactionReady(InsetsSourceProvider.this, 0, false);
mControl = null;
mControlTarget = null;
mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 6b9fcf411ce1..ba578f642429 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -34,6 +34,7 @@ import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import android.util.SparseLongArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
@@ -58,6 +59,7 @@ class InsetsStateController {
private final DisplayContent mDisplayContent;
private final SparseArray<InsetsSourceProvider> mProviders = new SparseArray<>();
+ private final SparseLongArray mSurfaceTransactionIds = new SparseLongArray();
private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
mControlTargetProvidersMap = new ArrayMap<>();
private final SparseArray<InsetsControlTarget> mIdControlTargetMap = new SparseArray<>();
@@ -360,14 +362,32 @@ class InsetsStateController {
notifyPendingInsetsControlChanged();
}
+ void notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready) {
+ if (ready) {
+ mSurfaceTransactionIds.put(provider.getSource().getId(), id);
+ } else {
+ mSurfaceTransactionIds.delete(provider.getSource().getId());
+ }
+ }
+
private void notifyPendingInsetsControlChanged() {
if (mPendingControlChanged.isEmpty()) {
return;
}
+ final int size = mSurfaceTransactionIds.size();
+ final SparseLongArray surfaceTransactionIds = new SparseLongArray(size);
+ for (int i = 0; i < size; i++) {
+ surfaceTransactionIds.append(
+ mSurfaceTransactionIds.keyAt(i), mSurfaceTransactionIds.valueAt(i));
+ }
mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- final InsetsSourceProvider provider = mProviders.valueAt(i);
- provider.onSurfaceTransactionApplied();
+ for (int i = 0; i < size; i++) {
+ final int sourceId = surfaceTransactionIds.keyAt(i);
+ final InsetsSourceProvider provider = mProviders.get(sourceId);
+ if (provider == null) {
+ continue;
+ }
+ provider.onSurfaceTransactionCommitted(surfaceTransactionIds.valueAt(i));
}
final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
int displayId = mDisplayContent.getDisplayId();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 908cbd340236..30134d815fa6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -336,19 +336,19 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public boolean performHapticFeedback(int effectId, boolean always) {
+ public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
final long ident = Binder.clearCallingIdentity();
try {
return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
- effectId, always, null);
+ effectId, always, null, fromIme);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
- public void performHapticFeedbackAsync(int effectId, boolean always) {
- performHapticFeedback(effectId, always);
+ public void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme) {
+ performHapticFeedback(effectId, always, fromIme);
}
/* Drag/drop */
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b43a4540bbde..5e7f1cbdd06e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -28,6 +28,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.Context;
+import android.os.HandlerExecutor;
import android.os.Trace;
import android.util.Slog;
import android.util.TimeUtils;
@@ -69,6 +70,8 @@ public class WindowAnimator {
private Choreographer mChoreographer;
+ private final HandlerExecutor mExecutor;
+
/**
* Indicates whether we have an animation frame callback scheduled, which will happen at
* vsync-app and then schedule the animation tick at the right time (vsync-sf).
@@ -80,8 +83,7 @@ public class WindowAnimator {
* A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
* executed and the corresponding transaction is closed and applied.
*/
- private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
- private boolean mInExecuteAfterPrepareSurfacesRunnables;
+ private ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
private final SurfaceControl.Transaction mTransaction;
@@ -92,6 +94,7 @@ public class WindowAnimator {
mTransaction = service.mTransactionFactory.get();
service.mAnimationHandler.runWithScissors(
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
+ mExecutor = new HandlerExecutor(service.mAnimationHandler);
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mGlobalLock) {
@@ -197,6 +200,19 @@ public class WindowAnimator {
updateRunningExpensiveAnimationsLegacy();
}
+ final ArrayList<Runnable> afterPrepareSurfacesRunnables = mAfterPrepareSurfacesRunnables;
+ if (!afterPrepareSurfacesRunnables.isEmpty()) {
+ mAfterPrepareSurfacesRunnables = new ArrayList<>();
+ mTransaction.addTransactionCommittedListener(mExecutor, () -> {
+ synchronized (mService.mGlobalLock) {
+ // Traverse in order they were added.
+ for (int i = 0, size = afterPrepareSurfacesRunnables.size(); i < size; i++) {
+ afterPrepareSurfacesRunnables.get(i).run();
+ }
+ afterPrepareSurfacesRunnables.clear();
+ }
+ });
+ }
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyTransaction");
mTransaction.apply();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -204,7 +220,6 @@ public class WindowAnimator {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
@@ -286,34 +301,10 @@ public class WindowAnimator {
/**
* Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
- * the corresponding transaction is closed and applied.
+ * the corresponding transaction is closed, applied, and committed.
*/
void addAfterPrepareSurfacesRunnable(Runnable r) {
- // If runnables are already being handled in executeAfterPrepareSurfacesRunnable, then just
- // immediately execute the runnable passed in.
- if (mInExecuteAfterPrepareSurfacesRunnables) {
- r.run();
- return;
- }
-
mAfterPrepareSurfacesRunnables.add(r);
scheduleAnimation();
}
-
- void executeAfterPrepareSurfacesRunnables() {
-
- // Don't even think about to start recursing!
- if (mInExecuteAfterPrepareSurfacesRunnables) {
- return;
- }
- mInExecuteAfterPrepareSurfacesRunnables = true;
-
- // Traverse in order they were added.
- final int size = mAfterPrepareSurfacesRunnables.size();
- for (int i = 0; i < size; i++) {
- mAfterPrepareSurfacesRunnables.get(i).run();
- }
- mAfterPrepareSurfacesRunnables.clear();
- mInExecuteAfterPrepareSurfacesRunnables = false;
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index 5fe60d779fa6..b012aaaed3bf 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -16,18 +16,26 @@
package com.android.server.contentprotection;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_ENABLED;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY;
+import static android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyManagerInternal;
-import android.content.ContentResolver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
-import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.TestableContentResolver;
import android.testing.TestableContext;
@@ -36,6 +44,9 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.LocalServices;
+
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,29 +89,36 @@ public class ContentProtectionConsentManagerTest {
public final TestableContext mTestableContext =
new TestableContext(ApplicationProvider.getApplicationContext());
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final TestableContentResolver mTestableContentResolver =
mTestableContext.getContentResolver();
- @Mock private ContentResolver mMockContentResolver;
-
@Mock private DevicePolicyManagerInternal mMockDevicePolicyManagerInternal;
+ @Mock private DevicePolicyCache mMockDevicePolicyCache;
+
+ @Before
+ public void setup() {
+ setupLocalService(DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+ }
+
@Test
- public void constructor_registersContentObserver() {
+ public void isConsentGranted_policyFlagDisabled_packageVerifierNotGranted() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
- createContentProtectionConsentManager(mMockContentResolver);
+ createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
- assertThat(manager.mContentObserver).isNotNull();
- verify(mMockContentResolver)
- .registerContentObserver(
- URI_PACKAGE_VERIFIER_USER_CONSENT,
- /* notifyForDescendants= */ false,
- manager.mContentObserver,
- UserHandle.USER_ALL);
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_packageVerifierNotGranted() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierNotGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
@@ -108,10 +126,12 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_contentProtectionNotGranted() {
+ public void isConsentGranted_policyFlagDisabled_contentProtectionNotGranted() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
@@ -119,10 +139,52 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagDisabled_packageVerifierGranted_userNotManaged() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagDisabled_packageVerifierGranted_userManaged() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userNotManaged_contentProtectionNotGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_packageVerifierGranted_userNotManaged() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userNotManaged_contentProtectionGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
@@ -130,22 +192,110 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyDisabled() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_DISABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyEnabled() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
}
@Test
- public void isConsentGranted_packageVerifierGranted_userManaged() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyNotControlled_contentProtectionGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
.thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_TRUE);
boolean actual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyNotControlled_contentProtectionNotGranted() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isFalse();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagEnabled_packageVerifierGranted_userManaged_policyNotControlled_contentProtectionDefault() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+ .thenReturn(true);
+ when(mMockDevicePolicyCache.getContentProtectionPolicy(TEST_USER_ID))
+ .thenReturn(CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+ assertThat(actual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verify(mMockDevicePolicyCache).getContentProtectionPolicy(TEST_USER_ID);
+ }
+
+ @Test
+ public void isConsentGranted_policyFlagDisabled_packageVerifierDefault() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_DEFAULT, VALUE_TRUE);
+
+ boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_packageVerifierDefault() {
+ public void isConsentGranted_policyFlagEnabled_packageVerifierDefault() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_DEFAULT, VALUE_TRUE);
@@ -153,10 +303,12 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isFalse();
verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void isConsentGranted_contentProtectionDefault() {
+ public void isConsentGranted_policyFlagDisabled_contentProtectionDefault() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
@@ -164,57 +316,108 @@ public class ContentProtectionConsentManagerTest {
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void contentObserver_packageVerifier() {
+ public void contentObserver_policyFlagDisabled_packageVerifier() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
- createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
+ createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
+
boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
- notifyContentObserver(
- manager,
- URI_PACKAGE_VERIFIER_USER_CONSENT,
- KEY_PACKAGE_VERIFIER_USER_CONSENT,
- VALUE_FALSE);
+ putGlobalSettings(KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_TRUE);
boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
-
- assertThat(firstActual).isTrue();
assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
+
+ notifyContentObserver(manager, URI_PACKAGE_VERIFIER_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
@Test
- public void contentObserver_contentProtection() {
+ public void contentObserver_policyFlagEnabled_packageVerifier() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
ContentProtectionConsentManager manager =
- createContentProtectionConsentManager(VALUE_TRUE, VALUE_DEFAULT);
+ createContentProtectionConsentManager(VALUE_FALSE, VALUE_TRUE);
+
boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
- notifyContentObserver(
- manager,
- URI_CONTENT_PROTECTION_USER_CONSENT,
- KEY_CONTENT_PROTECTION_USER_CONSENT,
- VALUE_FALSE);
+ putGlobalSettings(KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_TRUE);
boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
- assertThat(firstActual).isTrue();
+ notifyContentObserver(manager, URI_PACKAGE_VERIFIER_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void contentObserver_policyFlagDisabled_contentProtection() {
+ mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
+
+ putGlobalSettings(KEY_CONTENT_PROTECTION_USER_CONSENT, VALUE_TRUE);
+ boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, never()).isUserOrganizationManaged(anyInt());
+
+ notifyContentObserver(manager, URI_CONTENT_PROTECTION_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
+ verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
+ }
+
+ @Test
+ public void contentObserver_policyFlagEnabled_contentProtection() {
+ mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
+ ContentProtectionConsentManager manager =
+ createContentProtectionConsentManager(VALUE_TRUE, VALUE_FALSE);
+
+ boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(firstActual).isFalse();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+
+ putGlobalSettings(KEY_CONTENT_PROTECTION_USER_CONSENT, VALUE_TRUE);
+ boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(secondActual).isFalse();
+ verify(mMockDevicePolicyManagerInternal, times(2)).isUserOrganizationManaged(TEST_USER_ID);
+
+ notifyContentObserver(manager, URI_CONTENT_PROTECTION_USER_CONSENT);
+ boolean thirdActual = manager.isConsentGranted(TEST_USER_ID);
+ assertThat(thirdActual).isTrue();
+ verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
+
+ verifyZeroInteractions(mMockDevicePolicyCache);
}
- private void notifyContentObserver(
- ContentProtectionConsentManager manager, Uri uri, String key, int value) {
+ private void putGlobalSettings(String key, int value) {
Settings.Global.putInt(mTestableContentResolver, key, value);
- // Observer has to be called manually, mTestableContentResolver is not propagating
- manager.mContentObserver.onChange(/* selfChange= */ false, uri, TEST_USER_ID);
}
- private ContentProtectionConsentManager createContentProtectionConsentManager(
- ContentResolver contentResolver) {
- return new ContentProtectionConsentManager(
- new Handler(Looper.getMainLooper()),
- contentResolver,
- mMockDevicePolicyManagerInternal);
+ private void notifyContentObserver(ContentProtectionConsentManager manager, Uri uri) {
+ // Observer has to be called manually, mTestableContentResolver is not propagating
+ manager.mContentObserver.onChange(/* selfChange= */ false, uri, TEST_USER_ID);
}
private ContentProtectionConsentManager createContentProtectionConsentManager(
@@ -227,6 +430,14 @@ public class ContentProtectionConsentManagerTest {
mTestableContentResolver,
KEY_CONTENT_PROTECTION_USER_CONSENT,
valueContentProtectionUserConsent);
- return createContentProtectionConsentManager(mTestableContentResolver);
+ return new ContentProtectionConsentManager(
+ new Handler(Looper.getMainLooper()),
+ mTestableContentResolver,
+ mMockDevicePolicyCache);
+ }
+
+ private <T> void setupLocalService(Class<T> clazz, T service) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, service);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index 076d5caf5954..44d116181be5 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -147,7 +147,7 @@ public class AnrTimerTest {
final int n = 4;
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
if (stack.length < n+1) return "test";
- return stack[n].getMethodName();
+ return stack[n].getClassName() + "." + stack[n].getMethodName();
}
}
@@ -318,8 +318,11 @@ public class AnrTimerTest {
public void testDumpOutput() throws Exception {
if (!AnrTimer.nativeTimersSupported()) return;
+ // The timers in this class are named "class.method".
+ final String timerName = "timer: com.android.server.utils.AnrTimerTest";
+
String r1 = getDumpOutput();
- assertThat(r1).doesNotContain("timer:");
+ assertThat(r1).doesNotContain(timerName);
Helper helper = new Helper(2);
TestArg t1 = new TestArg(1, 1);
@@ -333,14 +336,14 @@ public class AnrTimerTest {
String r2 = getDumpOutput();
// There are timers in the list if and only if the feature is enabled.
if (mEnabled) {
- assertThat(r2).contains("timer:");
+ assertThat(r2).contains(timerName);
} else {
- assertThat(r2).doesNotContain("timer:");
+ assertThat(r2).doesNotContain(timerName);
}
}
String r3 = getDumpOutput();
- assertThat(r3).doesNotContain("timer:");
+ assertThat(r3).doesNotContain(timerName);
}
/**
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index e27bb4c8c3b6..b9ece9360980 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -40,12 +40,19 @@ import java.util.concurrent.Executor;
public class StubTransaction extends SurfaceControl.Transaction {
private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>();
+ private HashSet<SurfaceControl.TransactionCommittedListener> mTransactionCommittedListeners =
+ new HashSet<>();
@Override
public void apply() {
for (Runnable listener : mWindowInfosReportedListeners) {
listener.run();
}
+ for (SurfaceControl.TransactionCommittedListener listener
+ : mTransactionCommittedListeners) {
+ listener.onTransactionCommitted();
+ }
+ mTransactionCommittedListeners.clear();
}
@Override
@@ -239,6 +246,9 @@ public class StubTransaction extends SurfaceControl.Transaction {
@Override
public SurfaceControl.Transaction addTransactionCommittedListener(Executor executor,
SurfaceControl.TransactionCommittedListener listener) {
+ SurfaceControl.TransactionCommittedListener listenerInner =
+ () -> executor.execute(listener::onTransactionCommitted);
+ mTransactionCommittedListeners.add(listenerInner);
return this;
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java
new file mode 100644
index 000000000000..038f1db32d18
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.util.IndentingPrintWriter;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.vibrator.GroupedAggregatedLogRecords.AggregatedLogRecord;
+import com.android.server.vibrator.GroupedAggregatedLogRecords.SingleLogRecord;
+
+import org.junit.Test;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class GroupedAggregatedLogRecordsTest {
+
+ private static final int AGGREGATION_TIME_LIMIT = 1000;
+ private static final int NO_AGGREGATION_TIME_LIMIT = 0;
+ private static final long PROTO_FIELD_ID = 1;
+ private static final int GROUP_1 = 1;
+ private static final int GROUP_2 = 2;
+ private static final int KEY_1 = 1;
+ private static final int KEY_2 = 2;
+
+ private static final IndentingPrintWriter WRITER = new IndentingPrintWriter(new StringWriter());
+ private static final ProtoOutputStream PROTO_OUTPUT_STREAM = new ProtoOutputStream();
+
+ private final List<TestSingleLogRecord> mTestRecords = new ArrayList<>();
+
+ @Test
+ public void record_noAggregation_keepsIndividualRecords() {
+ int sizeLimit = 10;
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ sizeLimit, NO_AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ for (int i = 0; i < sizeLimit; i++) {
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+ }
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeWrittenOnce(0, sizeLimit);
+ }
+
+ @Test
+ public void record_sizeLimit_dropsOldestEntriesForNewOnes() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 2, NO_AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ TestSingleLogRecord firstRecord = createRecord(GROUP_1, KEY_1, createTime++);
+ assertThat(records.add(firstRecord)).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+
+ // Adding third record drops first record
+ AggregatedLogRecord<TestSingleLogRecord> droppedRecord =
+ records.add(createRecord(GROUP_1, KEY_1, createTime++));
+ assertThat(droppedRecord).isNotNull();
+ assertThat(droppedRecord.getLatest()).isEqualTo(firstRecord);
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeNotWritten(0, 1); // First record not written
+ assertRecordsInRangeWrittenOnce(1, 3); // All newest records written
+ }
+
+ @Test
+ public void record_timeAggregation_aggregatesCloseRecordAndPrintsOnlyFirstAndLast() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ // No record dropped, all aggregated in a single entry
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime + 1))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1,
+ createTime + AGGREGATION_TIME_LIMIT - 2))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1,
+ createTime + AGGREGATION_TIME_LIMIT - 1))).isNull();
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeWrittenOnce(0, 1); // Writes first record
+ assertRecordsInRangeNotWritten(1, 3); // Skips aggregated records in between
+ assertRecordsInRangeWrittenOnce(3, 4); // Writes last record
+ }
+
+ @Test
+ public void record_differentGroups_recordsKeptSeparate() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ // No record dropped, all kept in separate aggregated lists
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+ assertThat(records.add(createRecord(GROUP_2, KEY_2, createTime++))).isNull();
+ assertThat(records.add(createRecord(GROUP_2, KEY_2, createTime++))).isNull();
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1, GROUP_2);
+ assertRecordsInRangeWrittenOnce(0, 4);
+ }
+
+ @Test
+ public void record_sameGroupDifferentAggregationKeys_recordsNotAggregated() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull();
+
+ // Second record on same group with different key not aggregated, drops first record
+ AggregatedLogRecord<TestSingleLogRecord> droppedRecord =
+ records.add(createRecord(GROUP_1, KEY_2, createTime++));
+ assertThat(droppedRecord).isNotNull();
+ assertThat(droppedRecord.getLatest()).isEqualTo(mTestRecords.getFirst());
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeNotWritten(0, 1); // Skips first record that was dropped
+ assertRecordsInRangeWrittenOnce(1, 2); // Writes last record
+ }
+
+ @Test
+ public void record_sameGroupAndAggregationKeysDistantTimes_recordsNotAggregated() {
+ long createTime = 100;
+ TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords(
+ /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID);
+
+ assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime))).isNull();
+
+ // Second record after aggregation time limit not aggregated, drops first record
+ AggregatedLogRecord<TestSingleLogRecord> droppedRecord =
+ records.add(createRecord(GROUP_1, KEY_1, createTime + AGGREGATION_TIME_LIMIT));
+ assertThat(droppedRecord).isNotNull();
+ assertThat(droppedRecord.getLatest()).isEqualTo(mTestRecords.getFirst());
+
+ dumpRecords(records);
+ assertGroupHeadersWrittenOnce(records, GROUP_1);
+ assertRecordsInRangeNotWritten(0, 1); // Skips first record that was dropped
+ assertRecordsInRangeWrittenOnce(1, 2); // Writes last record
+ }
+
+ private TestSingleLogRecord createRecord(int groupKey, int aggregateKey, long createTime) {
+ TestSingleLogRecord record = new TestSingleLogRecord(groupKey, aggregateKey, createTime);
+ mTestRecords.add(record);
+ return record;
+ }
+
+ private void dumpRecords(TestGroupedAggregatedLogRecords records) {
+ records.dump(WRITER);
+ records.dump(PROTO_OUTPUT_STREAM);
+ }
+
+ private void assertGroupHeadersWrittenOnce(TestGroupedAggregatedLogRecords records,
+ int... groupKeys) {
+ assertThat(records.dumpGroupKeys).containsExactlyElementsIn(
+ Arrays.stream(groupKeys).boxed().toList());
+ }
+
+ private void assertRecordsInRangeWrittenOnce(int startIndexInclusive, int endIndexExclusive) {
+ for (int i = startIndexInclusive; i < endIndexExclusive; i++) {
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpTextCount)
+ .isEqualTo(1);
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpProtoFieldIds)
+ .containsExactly(PROTO_FIELD_ID);
+ }
+ }
+
+ private void assertRecordsInRangeNotWritten(int startIndexInclusive, int endIndexExclusive) {
+ for (int i = startIndexInclusive; i < endIndexExclusive; i++) {
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpTextCount)
+ .isEqualTo(0);
+ assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpProtoFieldIds)
+ .isEmpty();
+ }
+ }
+
+ private static final class TestGroupedAggregatedLogRecords
+ extends GroupedAggregatedLogRecords<TestSingleLogRecord> {
+
+ public final List<Integer> dumpGroupKeys = new ArrayList<>();
+
+ private final long mProtoFieldId;
+
+ TestGroupedAggregatedLogRecords(int sizeLimit, int aggregationTimeLimitMs,
+ long protoFieldId) {
+ super(sizeLimit, aggregationTimeLimitMs);
+ mProtoFieldId = protoFieldId;
+ }
+
+ @Override
+ void dumpGroupHeader(IndentingPrintWriter pw, int groupKey) {
+ dumpGroupKeys.add(groupKey);
+ }
+
+ @Override
+ long findGroupKeyProtoFieldId(int groupKey) {
+ return mProtoFieldId;
+ }
+ }
+
+ private static final class TestSingleLogRecord implements SingleLogRecord {
+ public final List<Long> dumpProtoFieldIds = new ArrayList<>();
+ public int dumpTextCount = 0;
+
+ private final int mGroupKey;
+ private final int mAggregateKey;
+ private final long mCreateTime;
+
+ TestSingleLogRecord(int groupKey, int aggregateKey, long createTime) {
+ mGroupKey = groupKey;
+ mAggregateKey = aggregateKey;
+ mCreateTime = createTime;
+ }
+
+ @Override
+ public int getGroupKey() {
+ return mGroupKey;
+ }
+
+ @Override
+ public long getCreateUptimeMs() {
+ return mCreateTime;
+ }
+
+ @Override
+ public boolean mayAggregate(SingleLogRecord record) {
+ if (record instanceof TestSingleLogRecord param) {
+ return mAggregateKey == param.mAggregateKey;
+ }
+ return false;
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw) {
+ dumpTextCount++;
+ }
+
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ dumpProtoFieldIds.add(fieldId);
+ }
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 3d0dca0de87e..e3d45967848a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -17,9 +17,11 @@
package com.android.server.vibrator;
import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
+import static android.os.VibrationAttributes.CATEGORY_UNKNOWN;
import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
import static android.os.VibrationEffect.EFFECT_CLICK;
@@ -285,7 +287,8 @@ public class HapticFeedbackVibrationProviderTest {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false);
+ SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false,
+ false /* fromIme*/);
assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse();
}
@@ -295,7 +298,7 @@ public class HapticFeedbackVibrationProviderTest {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true);
+ SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true, false /* fromIme*/);
assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isTrue();
}
@@ -307,7 +310,7 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue();
}
@@ -320,40 +323,59 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse();
}
}
@Test
- public void testVibrationAttribute_keyboardCategoryOff_notUseKeyboardCategory() {
+ public void testVibrationAttribute_keyboardCategoryOff_isIme_notUseKeyboardCategory() {
mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
+ assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
+ .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
assertWithMessage("Expected no CATEGORY_KEYBOARD for effect " + effectId)
- .that(attrs.getCategory()).isEqualTo(0);
+ .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
}
}
@Test
- public void testVibrationAttribute_keyboardCategoryOn_useKeyboardCategory() {
+ public void testVibrationAttribute_keyboardCategoryOn_notIme_notUseKeyboardCategory() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
+ assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
+ .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
+ assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
+ .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_keyboardCategoryOn_isIme_useKeyboardCategory() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
+ assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
+ .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
.that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
}
}
@Test
- public void testVibrationAttribute_noFixAmplitude_keyboardCategoryOn_noBypassIntensityScale() {
+ public void testVibrationAttribute_noFixAmplitude_notBypassIntensityScale() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
mockKeyboardVibrationFixedAmplitude(-1);
@@ -361,7 +383,23 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
+ assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_notIme_notBypassIntensityScale() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false, false /* fromIme*/);
assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
@@ -369,7 +407,7 @@ public class HapticFeedbackVibrationProviderTest {
}
@Test
- public void testVibrationAttribute_fixAmplitude_keyboardCategoryOn_bypassIntensityScale() {
+ public void testVibrationAttribute_fixAmplitude_isIme_bypassIntensityScale() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
@@ -377,7 +415,7 @@ public class HapticFeedbackVibrationProviderTest {
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
- effectId, /* bypassVibrationIntensitySetting= */ false);
+ effectId, /* bypassVibrationIntensitySetting= */ false, true /* fromIme*/);
assertWithMessage("Expected FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ effectId)
.that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isTrue();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index 3e59878f9e1e..b2644350dfdd 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -117,32 +117,32 @@ public class VibrationScalerTest {
}
@Test
- public void testGetExternalVibrationScale() {
+ public void testGetScaleLevel() {
setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
// Vibration setting being bypassed will use default setting and not scale.
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ mVibrationScaler.getScaleLevel(USAGE_TOUCH));
}
@Test
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 0d5bf95d959d..3799abc100c9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -38,10 +38,10 @@ import android.frameworks.vibrator.ScaleParam;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.test.TestLooper;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.util.ArrayUtils;
@@ -86,20 +86,20 @@ public class VibratorControlServiceTest {
ApplicationProvider.getApplicationContext(), new Handler(testLooper.getLooper()));
mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper());
- mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(),
+ mVibratorControlService = new VibratorControlService(
+ InstrumentationRegistry.getContext(), new VibratorControllerHolder(),
mMockVibrationScaler, mVibrationSettings, mLock);
}
@Test
- public void testRegisterVibratorController() throws RemoteException {
+ public void testRegisterVibratorController() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
assertThat(mFakeVibratorController.isLinkedToDeath).isTrue();
}
@Test
- public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest()
- throws RemoteException {
+ public void testUnregisterVibratorController_providingRegisteredController_performsRequest() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
mVibratorControlService.unregisterVibratorController(mFakeVibratorController);
@@ -108,8 +108,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest()
- throws RemoteException {
+ public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() {
FakeVibratorController controller1 = new FakeVibratorController(mTestLooper.getLooper());
FakeVibratorController controller2 = new FakeVibratorController(mTestLooper.getLooper());
mVibratorControlService.registerVibratorController(controller1);
@@ -120,8 +119,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testOnRequestVibrationParamsComplete_cachesAdaptiveHapticsScalesCorrectly()
- throws RemoteException {
+ public void testOnRequestVibrationParamsComplete_cachesAdaptiveHapticsScalesCorrectly() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
int timeoutInMillis = 10;
CompletableFuture<Void> future =
@@ -148,8 +146,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testOnRequestVibrationParamsComplete_withIncorrectToken_ignoresRequest()
- throws RemoteException, InterruptedException {
+ public void testOnRequestVibrationParamsComplete_withIncorrectToken_ignoresRequest() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
int timeoutInMillis = 10;
CompletableFuture<Void> unusedFuture =
@@ -167,8 +164,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly()
- throws RemoteException {
+ public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
SparseArray<Float> vibrationScales = new SparseArray<>();
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
@@ -187,8 +183,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testSetVibrationParams_withUnregisteredController_ignoresRequest()
- throws RemoteException {
+ public void testSetVibrationParams_withUnregisteredController_ignoresRequest() {
SparseArray<Float> vibrationScales = new SparseArray<>();
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
@@ -201,8 +196,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales()
- throws RemoteException {
+ public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales() {
mVibratorControlService.registerVibratorController(mFakeVibratorController);
int types = buildVibrationTypesMask(ScaleParam.TYPE_ALARM, ScaleParam.TYPE_NOTIFICATION);
@@ -216,8 +210,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testClearVibrationParams_withUnregisteredController_ignoresRequest()
- throws RemoteException {
+ public void testClearVibrationParams_withUnregisteredController_ignoresRequest() {
mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM,
mFakeVibratorController);
@@ -225,8 +218,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testRequestVibrationParams_createsFutureRequestProperly()
- throws RemoteException {
+ public void testRequestVibrationParams_createsFutureRequestProperly() {
int timeoutInMillis = 10;
mVibratorControlService.registerVibratorController(mFakeVibratorController);
CompletableFuture<Void> future =
@@ -243,8 +235,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testShouldRequestVibrationParams_returnsTrueForVibrationsThatShouldRequestParams()
- throws RemoteException {
+ public void testShouldRequestVibrationParams_returnsTrueForVibrationsThatShouldRequestParams() {
int[] vibrations =
new int[]{USAGE_ALARM, USAGE_RINGTONE, USAGE_MEDIA, USAGE_TOUCH, USAGE_NOTIFICATION,
USAGE_HARDWARE_FEEDBACK, USAGE_UNKNOWN, USAGE_COMMUNICATION_REQUEST};
@@ -258,8 +249,7 @@ public class VibratorControlServiceTest {
}
@Test
- public void testShouldRequestVibrationParams_unregisteredVibratorController_returnsFalse()
- throws RemoteException {
+ public void testShouldRequestVibrationParams_unregisteredVibratorController_returnsFalse() {
int[] vibrations =
new int[]{USAGE_ALARM, USAGE_RINGTONE, USAGE_MEDIA, USAGE_TOUCH, USAGE_NOTIFICATION,
USAGE_HARDWARE_FEEDBACK, USAGE_UNKNOWN, USAGE_COMMUNICATION_REQUEST};
@@ -269,7 +259,7 @@ public class VibratorControlServiceTest {
}
}
- private int buildVibrationTypesMask(int... types) {
+ private static int buildVibrationTypesMask(int... types) {
int typesMask = 0;
for (int type : types) {
typesMask |= type;
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index d2ad61f2ba9f..1ea90f55b727 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -2526,7 +2526,7 @@ public class VibratorManagerServiceTest {
int constant, boolean always) throws InterruptedException {
HalVibration vib =
service.performHapticFeedbackInternal(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
- constant, always, "some reason", service);
+ constant, always, "some reason", service, false /* fromIme */);
if (vib != null) {
vib.waitForEnd();
}
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
index 2a010f0a82a9..0cd88ef7a4b4 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -60,11 +60,7 @@ public final class FakeVibratorController extends IVibratorController.Stub {
requestTimeoutInMillis = timeoutInMillis;
mHandler.post(() -> {
if (mVibratorControlService != null) {
- try {
- mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult);
}
});
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0c1fbf3cb3d7..1a1fe95756d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -94,6 +94,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
public void setUp() throws Exception {
assumeFalse(WindowManagerService.sEnableShellTransitions);
mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
+ mWm.mAnimator.ready();
}
@Test
@@ -855,7 +856,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -886,7 +887,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity,
null /* changingTaskFragment */);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation is not run by the remote handler because the activity is filling the Task.
assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -921,7 +922,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity,
null /* changingTaskFragment */);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -946,7 +947,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -973,7 +974,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -997,7 +998,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation not run by the remote handler.
assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -1024,7 +1025,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation should not run by the remote handler when there are non-embedded activities of
// different UID.
@@ -1051,7 +1052,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// Animation should not run by the remote handler when there is wallpaper in the transition.
assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -1085,7 +1086,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// The animation will be animated remotely by client and all activities are input disabled
// for untrusted animation.
@@ -1136,7 +1137,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// The animation will be animated remotely by client and all activities are input disabled
// for untrusted animation.
@@ -1178,7 +1179,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
// The animation will be animated remotely by client, but input should not be dropped for
// fully trusted.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2085d6140f68..1f15ec3be3a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -371,6 +371,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
mDisplayContent.getInsetsPolicy().updateBarControlTarget(app);
mDisplayContent.getInsetsPolicy().showTransient(statusBars(),
true /* isGestureOnSystemBar */);
+ mWm.mAnimator.ready();
waitUntilWindowAnimatorIdle();
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(statusBars()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 11d9629cf25e..a1638019359b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -43,7 +43,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -113,6 +112,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter,
mHandler, false /*isActivityEmbedding*/);
+ mWm.mAnimator.ready();
}
private WindowState createAppOverlayWindow() {
@@ -136,7 +136,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -168,7 +168,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -290,7 +290,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -336,7 +336,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN,
false /* isVoiceInteraction */, null /* sources */);
mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
try {
@@ -363,7 +363,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -417,7 +417,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -471,7 +471,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -526,7 +526,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -559,7 +559,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -595,7 +595,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -645,7 +645,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -782,7 +782,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mDisplayContent.applySurfaceChangesTransaction();
mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
any(), any(), any(), any());
@@ -810,7 +810,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(transit);
- mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ waitUntilWindowAnimatorIdle();
return adapter;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a8f6fe86c823..7ab093d0ae13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -546,7 +546,7 @@ public class SystemServicesTestRule implements TestRule {
// This makes sure all previous messages in the handler are fully processed vs. just popping
// them from the message queue.
final AtomicBoolean currentMessagesProcessed = new AtomicBoolean(false);
- wm.mAnimator.getChoreographer().postFrameCallback(time -> {
+ wm.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
synchronized (currentMessagesProcessed) {
currentMessagesProcessed.set(true);
currentMessagesProcessed.notifyAll();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 7551b1650ad0..1233686a4b48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -265,7 +265,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
@Override
public boolean performHapticFeedback(int uid, String packageName, int effectId,
- boolean always, String reason) {
+ boolean always, String reason, boolean fromIme) {
return false;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 626a2e574881..9d277c8fcf10 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -18535,7 +18535,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_GET_LAST_KNOWN_CELL_IDENTITY)
@RequiresPermission(allOf = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID})
public @Nullable CellIdentity getLastKnownCellIdentity() {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
index 83e09bf7043e..fd7474b55fa6 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
@@ -20,7 +20,7 @@ import com.android.hoststubgen.asm.toJvmClassName
/**
* Filter to apply a policy to classes extending or implementing a class,
- * either directly or indirectly. (with a breadth first search.)
+ * either directly or indirectly.
*
* The policy won't apply to the super class itself.
*/
@@ -42,7 +42,7 @@ class SubclassFilter(
}
/**
- * Find a policy for a class with a breadth-first search.
+ * Find a policy for a class.
*/
private fun findPolicyForClass(className: String): FilterPolicyWithReason? {
val cn = classes.findClass(className) ?: return null