Merge "Add dump entry for ManagerRecord#mTargetPackageName" into main
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7c5885a..7e325a5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2187,6 +2187,7 @@
   }
 
   if (multiuser_get_app_id(uid) == AID_NETWORK_STACK) {
+    capabilities |= (1LL << CAP_WAKE_ALARM);
     capabilities |= (1LL << CAP_NET_ADMIN);
     capabilities |= (1LL << CAP_NET_BROADCAST);
     capabilities |= (1LL << CAP_NET_BIND_SERVICE);
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 59b55bf7..0a7872f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -17,11 +17,13 @@
 package com.android.server.vibrator;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.vibrator.V1_0.EffectStrength;
 import android.os.IExternalVibratorService;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.Slog;
@@ -56,6 +58,8 @@
     private final VibrationSettings mSettingsController;
     private final int mDefaultVibrationAmplitude;
 
+    private SparseArray<Float> mAdaptiveHapticsScales;
+
     VibrationScaler(Context context, VibrationSettings settingsController) {
         mSettingsController = settingsController;
         mDefaultVibrationAmplitude = context.getResources().getInteger(
@@ -140,6 +144,15 @@
             if (scaleLevel != null) {
                 segment = segment.scale(scaleLevel.factor);
             }
+
+            // If adaptive haptics scaling is available for this usage, apply it to the segment.
+            if (Flags.adaptiveHapticsEnabled()
+                    && mAdaptiveHapticsScales != null && mAdaptiveHapticsScales.size() > 0
+                    && mAdaptiveHapticsScales.contains(usageHint)) {
+                float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
+                segment = segment.scale(adaptiveScale);
+            }
+
             segments.set(i, segment);
         }
         if (segments.equals(composedEffect.getSegments())) {
@@ -173,6 +186,16 @@
         return prebaked.applyEffectStrength(newEffectStrength);
     }
 
+    /**
+     * Updates the adaptive haptics scales.
+     * @param scales the new vibration scales to apply.
+     *
+     * @hide
+     */
+    public void updateAdaptiveHapticsScales(@Nullable SparseArray<Float> scales) {
+        mAdaptiveHapticsScales = scales;
+    }
+
     /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
     private static int intensityToEffectStrength(int intensity) {
         switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 2eeb903..9d75249 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -16,14 +16,26 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.frameworks.vibrator.IVibratorControlService;
 import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.ScaleParam;
 import android.frameworks.vibrator.VibrationParam;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import java.util.Objects;
 
@@ -37,10 +49,13 @@
     private static final String TAG = "VibratorControlService";
 
     private final VibratorControllerHolder mVibratorControllerHolder;
+    private final VibrationScaler mVibrationScaler;
     private final Object mLock;
 
-    public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
+    public VibratorControlService(VibratorControllerHolder vibratorControllerHolder,
+            VibrationScaler vibrationScaler, Object lock) {
         mVibratorControllerHolder = vibratorControllerHolder;
+        mVibrationScaler = vibrationScaler;
         mLock = lock;
     }
 
@@ -70,25 +85,62 @@
                         + "controller doesn't match the registered one. " + this);
                 return;
             }
+            updateAdaptiveHapticsScales(/* params= */ null);
             mVibratorControllerHolder.setVibratorController(null);
         }
     }
 
     @Override
-    public void setVibrationParams(
-            @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
-            throws RemoteException {
-        // TODO(b/305939964): Add set vibration implementation.
+    public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params,
+            @NonNull IVibratorController token) throws RemoteException {
+        Objects.requireNonNull(token);
+
+        synchronized (mLock) {
+            if (mVibratorControllerHolder.getVibratorController() == null) {
+                Slog.w(TAG, "Received request to set VibrationParams for IVibratorController = "
+                        + token + ", but no controller was previously registered. Request "
+                        + "Ignored.");
+                return;
+            }
+            if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+                    token.asBinder())) {
+                Slog.wtf(TAG, "Failed to set new VibrationParams. The provided "
+                        + "controller doesn't match the registered one. " + this);
+                return;
+            }
+
+            updateAdaptiveHapticsScales(params);
+        }
     }
 
     @Override
-    public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
-        // TODO(b/305939964): Add clear vibration implementation.
+    public void clearVibrationParams(int types, @NonNull IVibratorController token)
+            throws RemoteException {
+        Objects.requireNonNull(token);
+
+        synchronized (mLock) {
+            if (mVibratorControllerHolder.getVibratorController() == null) {
+                Slog.w(TAG, "Received request to clear VibrationParams for IVibratorController = "
+                        + token + ", but no controller was previously registered. Request "
+                        + "Ignored.");
+                return;
+            }
+            if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+                    token.asBinder())) {
+                Slog.wtf(TAG, "Failed to clear VibrationParams. The provided "
+                        + "controller doesn't match the registered one. " + this);
+                return;
+            }
+            //TODO(305942827): Update this method to only clear the specified vibration types.
+            // Perhaps look into whether it makes more sense to have this clear all scales and
+            // rely on setVibrationParams for clearing the scales for specific vibrations.
+            updateAdaptiveHapticsScales(/* params= */ null);
+        }
     }
 
     @Override
     public void onRequestVibrationParamsComplete(
-            IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
+            @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
             throws RemoteException {
         // TODO(305942827): Cache the vibration params in VibrationScaler
     }
@@ -102,4 +154,52 @@
     public String getInterfaceHash() throws RemoteException {
         return this.HASH;
     }
+
+    /**
+     * Extracts the vibration scales and caches them in {@link VibrationScaler}.
+     *
+     * @param params the new vibration params to cache.
+     */
+    private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) {
+        if (params == null || params.length == 0) {
+            mVibrationScaler.updateAdaptiveHapticsScales(null);
+            return;
+        }
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        for (int i = 0; i < params.length; i++) {
+            ScaleParam scaleParam = params[i].getScale();
+            extractVibrationScales(scaleParam, vibrationScales);
+        }
+        mVibrationScaler.updateAdaptiveHapticsScales(vibrationScales);
+    }
+
+    /**
+     * Extracts the vibration scales and map them to their corresponding
+     * {@link android.os.VibrationAttributes} usages.
+     */
+    private void extractVibrationScales(ScaleParam scaleParam, SparseArray<Float> vibrationScales) {
+        if ((ScaleParam.TYPE_ALARM & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_ALARM, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_NOTIFICATION & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_NOTIFICATION, scaleParam.scale);
+            vibrationScales.put(USAGE_COMMUNICATION_REQUEST, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_RINGTONE & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_RINGTONE, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_MEDIA & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_MEDIA, scaleParam.scale);
+            vibrationScales.put(USAGE_UNKNOWN, scaleParam.scale);
+        }
+
+        if ((ScaleParam.TYPE_INTERACTIVE & scaleParam.typesMask) != 0) {
+            vibrationScales.put(USAGE_TOUCH, scaleParam.scale);
+            vibrationScales.put(USAGE_HARDWARE_FEEDBACK, scaleParam.scale);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index fc824ab..2c1ab95 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -273,7 +273,8 @@
         injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
         if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
             injector.addService(VIBRATOR_CONTROL_SERVICE,
-                    new VibratorControlService(new VibratorControllerHolder(), mLock));
+                    new VibratorControlService(new VibratorControllerHolder(), mVibrationScaler,
+                            mLock));
         }
 
     }
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 6f37967..66dcaff 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -31,13 +31,13 @@
         "frameworks-base-testutils",
         "frameworks-services-vibrator-testutils",
         "junit",
-        "mockito-target-minus-junit4",
+        "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
         "service-permission.stubs.system_server",
         "services.core",
         "flag-junit",
     ],
-
+    jni_libs: ["libdexmakerjvmtiagent"],
     platform_apis: true,
     certificate: "platform",
     dxflags: ["--multi-dex"],
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 bbca704e..f9fe6a9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -43,12 +43,17 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
+import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -68,6 +73,9 @@
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Mock private PowerManagerInternal mPowerManagerInternalMock;
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
@@ -256,6 +264,29 @@
         assertEquals(0.5, scaled.getScale(), 1e-5);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    public void scale_withAdaptiveHaptics_scalesVibrationsCorrectly() {
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+
+        SparseArray<Float> adaptiveHapticsScales = new SparseArray<>();
+        adaptiveHapticsScales.put(USAGE_RINGTONE, 0.5f);
+        adaptiveHapticsScales.put(USAGE_NOTIFICATION, 0.5f);
+        mVibrationScaler.updateAdaptiveHapticsScales(adaptiveHapticsScales);
+
+        StepSegment scaled = getFirstSegment(mVibrationScaler.scale(
+                VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE));
+        // Ringtone scales down.
+        assertTrue(scaled.getAmplitude() < 0.5);
+
+        scaled = getFirstSegment(mVibrationScaler.scale(
+                VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1),
+                USAGE_NOTIFICATION));
+        // Notification scales down.
+        assertTrue(scaled.getAmplitude() < 0.5);
+    }
+
     private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
             @Vibrator.VibrationIntensity int intensity) {
         when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
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 49efd1b..1e0b1df 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -16,21 +16,49 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
 import android.os.RemoteException;
+import android.util.SparseArray;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class VibratorControlServiceTest {
 
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    @Mock
+    private VibrationScaler mMockVibrationScaler;
+    @Captor
+    private ArgumentCaptor<SparseArray<Float>> mVibrationScalesCaptor;
+
     private VibratorControlService mVibratorControlService;
     private final Object mLock = new Object();
 
     @Before
     public void setUp() throws Exception {
-        mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
+        mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(),
+                mMockVibrationScaler, mLock);
     }
 
     @Test
@@ -47,6 +75,8 @@
         FakeVibratorController fakeController = new FakeVibratorController();
         mVibratorControlService.registerVibratorController(fakeController);
         mVibratorControlService.unregisterVibratorController(fakeController);
+
+        verify(mMockVibrationScaler).updateAdaptiveHapticsScales(null);
         assertThat(fakeController.isLinkedToDeath).isFalse();
     }
 
@@ -56,8 +86,91 @@
         FakeVibratorController fakeController1 = new FakeVibratorController();
         FakeVibratorController fakeController2 = new FakeVibratorController();
         mVibratorControlService.registerVibratorController(fakeController1);
-
         mVibratorControlService.unregisterVibratorController(fakeController2);
+
+        verifyZeroInteractions(mMockVibrationScaler);
         assertThat(fakeController1.isLinkedToDeath).isTrue();
     }
+
+    @Test
+    public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+        mVibratorControlService.registerVibratorController(fakeController);
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+                fakeController);
+
+        verify(mMockVibrationScaler).updateAdaptiveHapticsScales(mVibrationScalesCaptor.capture());
+        SparseArray<Float> cachedVibrationScales = mVibrationScalesCaptor.getValue();
+        assertThat(cachedVibrationScales.size()).isEqualTo(3);
+        assertThat(cachedVibrationScales.keyAt(0)).isEqualTo(USAGE_ALARM);
+        assertThat(cachedVibrationScales.valueAt(0)).isEqualTo(0.7f);
+        assertThat(cachedVibrationScales.keyAt(1)).isEqualTo(USAGE_NOTIFICATION);
+        assertThat(cachedVibrationScales.valueAt(1)).isEqualTo(0.4f);
+        // Setting ScaleParam.TYPE_NOTIFICATION will update vibration scaling for both
+        // notification and communication request usages.
+        assertThat(cachedVibrationScales.keyAt(2)).isEqualTo(USAGE_COMMUNICATION_REQUEST);
+        assertThat(cachedVibrationScales.valueAt(2)).isEqualTo(0.4f);
+    }
+
+    @Test
+    public void testSetVibrationParams_withUnregisteredController_ignoresRequest()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+                fakeController);
+
+        verifyZeroInteractions(mMockVibrationScaler);
+    }
+
+    @Test
+    public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+        mVibratorControlService.registerVibratorController(fakeController);
+        mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, fakeController);
+
+        verify(mMockVibrationScaler).updateAdaptiveHapticsScales(null);
+    }
+
+    @Test
+    public void testClearVibrationParams_withUnregisteredController_ignoresRequest()
+            throws RemoteException {
+        FakeVibratorController fakeController = new FakeVibratorController();
+
+        mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, fakeController);
+
+        verifyZeroInteractions(mMockVibrationScaler);
+    }
+
+    private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
+        List<VibrationParam> vibrationParamList = new ArrayList<>();
+        for (int i = 0; i < vibrationScales.size(); i++) {
+            int type = vibrationScales.keyAt(i);
+            float scale = vibrationScales.valueAt(i);
+
+            vibrationParamList.add(generateVibrationParam(type, scale));
+        }
+
+        return vibrationParamList.toArray(new VibrationParam[0]);
+    }
+
+    private VibrationParam generateVibrationParam(int type, float scale) {
+        ScaleParam scaleParam = new ScaleParam();
+        scaleParam.typesMask = type;
+        scaleParam.scale = scale;
+        VibrationParam vibrationParam = new VibrationParam();
+        vibrationParam.setScale(scaleParam);
+
+        return vibrationParam;
+    }
 }