Merge "Enable the customised vibration of (1) default notification (2) channel notification." into main
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e541246..b9f0968 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -330,10 +330,19 @@
}
final long[] vibrationPattern = channel.getVibrationPattern();
- if (vibrationPattern == null) {
- return helper.createDefaultVibration(insistent);
+ if (vibrationPattern != null) {
+ return helper.createWaveformVibration(vibrationPattern, insistent);
}
- return helper.createWaveformVibration(vibrationPattern, insistent);
+
+ if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) {
+ final VibrationEffect vibrationEffectFromSoundUri =
+ helper.createVibrationEffectFromSoundUri(channel.getSound());
+ if (vibrationEffectFromSoundUri != null) {
+ return vibrationEffectFromSoundUri;
+ }
+ }
+
+ return helper.createDefaultVibration(insistent);
}
private VibrationEffect calculateVibration() {
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index 8a0e595..fbe7772 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -24,6 +24,9 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.media.AudioAttributes;
+import android.media.RingtoneManager;
+import android.media.Utils;
+import android.net.Uri;
import android.os.Process;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -51,6 +54,7 @@
@Nullable private final float[] mDefaultPwlePattern;
@Nullable private final float[] mFallbackPwlePattern;
private final int mDefaultVibrationAmplitude;
+ private final Context mContext;
public VibratorHelper(Context context) {
mVibrator = context.getSystemService(Vibrator.class);
@@ -68,6 +72,7 @@
com.android.internal.R.array.config_notificationFallbackVibeWaveform);
mDefaultVibrationAmplitude = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultVibrationAmplitude);
+ mContext = context;
}
/**
@@ -184,6 +189,16 @@
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createDefaultVibration(boolean insistent) {
+ if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) {
+ final Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(mContext,
+ RingtoneManager.TYPE_NOTIFICATION);
+ final VibrationEffect vibrationEffectFromSoundUri =
+ createVibrationEffectFromSoundUri(defaultRingtoneUri);
+ if (vibrationEffectFromSoundUri != null) {
+ return vibrationEffectFromSoundUri;
+ }
+ }
+
if (mVibrator.hasFrequencyControl()) {
VibrationEffect effect = createPwleWaveformVibration(mDefaultPwlePattern, insistent);
if (effect != null) {
@@ -193,6 +208,22 @@
return createWaveformVibration(mDefaultPattern, insistent);
}
+ /**
+ * Safely create a {@link VibrationEffect} from given an uri {@code Uri}.
+ * with query parameter "vibration_uri"
+ *
+ * Use this function if the {@code Uri} is with a query parameter "vibration_uri" and the
+ * vibration_uri represents a valid vibration effect in xml
+ *
+ * @param uri {@code Uri} an uri including query parameter "vibraiton_uri"
+ */
+ public @Nullable VibrationEffect createVibrationEffectFromSoundUri(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ return Utils.parseVibrationEffect(mVibrator, Utils.getVibrationUri(uri));
+ }
+
/** Returns if a given vibration can be played by the vibrator that does notification buzz. */
public boolean areEffectComponentsSupported(VibrationEffect effect) {
return mVibrator.areVibrationFeaturesSupported(effect);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index aac2c40..be3adc1 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -156,3 +156,10 @@
description: "This flag enables forced auto-grouping conversations"
bug: "336488844"
}
+
+flag {
+ name: "notification_vibration_in_sound_uri"
+ namespace: "systemui"
+ description: "This flag enables sound uri with vibration source"
+ bug: "358524009"
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index f572e7a..50a5f65 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -62,6 +62,8 @@
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
+import android.media.RingtoneManager;
+import android.media.Utils;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Build;
@@ -69,6 +71,7 @@
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -93,6 +96,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
import java.util.ArrayList;
@SmallTest
@@ -141,6 +147,7 @@
when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(true);
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
final Resources res = mContext.getResources();
when(mMockContext.getResources()).thenReturn(res);
when(mMockContext.getPackageManager()).thenReturn(mPm);
@@ -511,6 +518,51 @@
}
@Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI)
+ public void testVibration_customVibrationForSound_withoutVibrationUri() {
+ // prepare testing data
+ Uri backupDefaultUri = RingtoneManager.getActualDefaultRingtoneUri(mMockContext,
+ RingtoneManager.TYPE_NOTIFICATION);
+ RingtoneManager.setActualDefaultRingtoneUri(mMockContext, RingtoneManager.TYPE_NOTIFICATION,
+ Settings.System.DEFAULT_NOTIFICATION_URI);
+ defaultChannel.enableVibration(true);
+ defaultChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, CUSTOM_ATTRIBUTES);
+ StatusBarNotification sbn = getNotification(
+ /* channelVibrationPattern= */ null,
+ /* channelVibrationEffect= */ null,
+ /* insistent= */ false);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+ try {
+ assertEquals(
+ new VibratorHelper(mMockContext).createDefaultVibration(false),
+ record.getVibration());
+ } finally {
+ // restore the data
+ RingtoneManager.setActualDefaultRingtoneUri(mMockContext,
+ RingtoneManager.TYPE_NOTIFICATION,
+ backupDefaultUri);
+ }
+ }
+
+ @Test
+ @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI)
+ public void testVibration_customVibrationForSound_withVibrationUri() throws IOException {
+ defaultChannel.enableVibration(true);
+ VibrationInfo vibration = getTestingVibration(mVibrator);
+ Uri uriWithVibration = getVibrationUriAppended(
+ Settings.System.DEFAULT_NOTIFICATION_URI, vibration.mUri);
+ defaultChannel.setSound(uriWithVibration, CUSTOM_ATTRIBUTES);
+ StatusBarNotification sbn = getNotification(
+ /* channelVibrationPattern= */ null,
+ /* channelVibrationEffect= */ null,
+ /* insistent= */ false);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+ assertEquals(vibration.mVibrationEffect, record.getVibration());
+ }
+
+ @Test
public void testImportance_preUpgrade() {
StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
@@ -1594,4 +1646,39 @@
assertThat(record.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM);
}
+
+ static class VibrationInfo {
+ public VibrationEffect mVibrationEffect;
+ public Uri mUri;
+ VibrationInfo(VibrationEffect vibrationEffect, Uri uri) {
+ mVibrationEffect = vibrationEffect;
+ mUri = uri;
+ }
+ }
+
+ private static VibrationInfo getTestingVibration(Vibrator vibrator) throws IOException {
+ File tempVibrationFile = File.createTempFile("test_vibration_file", ".xml");
+ FileWriter writer = new FileWriter(tempVibrationFile);
+ writer.write("<vibration-effect>\n"
+ + " <waveform-effect>\n"
+ + " <!-- PRIMING -->\n"
+ + " <waveform-entry durationMs=\"0\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"250\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"500\" amplitude=\"0\"/>\n"
+ + " </waveform-effect>\n"
+ + "</vibration-effect>"); // Your test XML content
+ writer.close();
+ Uri vibrationUri = Uri.parse(tempVibrationFile.toURI().toString());
+
+ VibrationEffect vibrationEffect = Utils.parseVibrationEffect(vibrator, vibrationUri);
+ return new VibrationInfo(vibrationEffect, vibrationUri);
+ }
+
+ private static Uri getVibrationUriAppended(Uri audioUri, Uri vibrationUri) {
+ Uri.Builder builder = audioUri.buildUpon();
+ builder.appendQueryParameter(Utils.VIBRATION_URI_PARAM, vibrationUri.toString());
+ return builder.build();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
index 0993bec..4d2396c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VibratorHelperTest.java
@@ -22,8 +22,12 @@
import static org.mockito.Mockito.when;
+import android.media.Utils;
+import android.net.Uri;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
+import android.provider.Settings;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -36,6 +40,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VibratorHelperTest extends UiServiceTestCase {
@@ -86,7 +94,34 @@
}
@Test
+ public void createVibrationEffectFromSoundUri_nullInput() {
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(null));
+ }
+
+ @Test
+ public void createVibrationEffectFromSoundUri_emptyUri() {
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(Uri.EMPTY));
+ }
+
+ @Test
+ public void createVibrationEffectFromSoundUri_uriWithoutRequiredQueryParameter() {
+ Uri uri = Settings.System.DEFAULT_NOTIFICATION_URI;
+ assertNull(mVibratorHelper.createVibrationEffectFromSoundUri(uri));
+ }
+
+ @Test
+ public void createVibrationEffectFromSoundUri_uriWithVibrationUri() throws IOException {
+ // prepare the uri with vibration
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
+ Uri validUri = getVibrationUriAppended(Settings.System.DEFAULT_NOTIFICATION_URI);
+
+ assertSingleVibration(mVibratorHelper.createVibrationEffectFromSoundUri(validUri));
+ }
+
+ @Test
public void createVibration_insistent_createsRepeatingVibration() {
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
+
when(mVibrator.hasFrequencyControl()).thenReturn(false);
assertRepeatingVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ true));
assertRepeatingVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ true));
@@ -98,6 +133,8 @@
@Test
public void createVibration_nonInsistent_createsSingleShotVibration() {
+ when(mVibrator.getInfo()).thenReturn(VibratorInfo.EMPTY_VIBRATOR_INFO);
+
when(mVibrator.hasFrequencyControl()).thenReturn(false);
assertSingleVibration(mVibratorHelper.createDefaultVibration(/* insistent= */ false));
assertSingleVibration(mVibratorHelper.createFallbackVibration(/* insistent= */ false));
@@ -120,4 +157,26 @@
effect instanceof VibrationEffect.Composed);
return ((VibrationEffect.Composed) effect).getRepeatIndex();
}
+
+ private static Uri getVibrationUriAppended(Uri baseUri) throws IOException {
+ File tempVibrationFile = File.createTempFile("test_vibration_file", ".xml");
+ FileWriter writer = new FileWriter(tempVibrationFile);
+ writer.write("<vibration-effect>\n"
+ + " <waveform-effect>\n"
+ + " <!-- PRIMING -->\n"
+ + " <waveform-entry durationMs=\"0\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"250\" amplitude=\"0\"/>\n"
+ + " <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
+ + " <waveform-entry durationMs=\"500\" amplitude=\"0\"/>\n"
+ + " </waveform-effect>\n"
+ + "</vibration-effect>"); // Your test XML content
+ writer.close();
+
+ Uri.Builder builder = baseUri.buildUpon();
+ builder.appendQueryParameter(
+ Utils.VIBRATION_URI_PARAM,
+ tempVibrationFile.toURI().toString());
+ return builder.build();
+ }
}