diff options
| -rw-r--r-- | Android.bp | 5 | ||||
| -rw-r--r-- | core/java/android/os/VibrationEffect.java | 103 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 8 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 2 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/VibrationEffectTest.java | 90 | ||||
| -rw-r--r-- | services/core/java/com/android/server/VibratorService.java | 2 | ||||
| -rw-r--r-- | services/core/jni/Android.bp | 1 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_VibratorService.cpp | 69 |
8 files changed, 244 insertions, 36 deletions
diff --git a/Android.bp b/Android.bp index c5d64fb99f8b..05fb3c083937 100644 --- a/Android.bp +++ b/Android.bp @@ -670,8 +670,9 @@ java_library { "android.hardware.tv.input-V1.0-java-constants", "android.hardware.usb-V1.0-java-constants", "android.hardware.usb-V1.1-java-constants", - "android.hardware.vibrator-V1.0-java-constants", - "android.hardware.vibrator-V1.1-java-constants", + "android.hardware.vibrator-V1.0-java", + "android.hardware.vibrator-V1.1-java", + "android.hardware.vibrator-V1.2-java", "android.hardware.wifi-V1.0-java-constants", "android.hardware.radio-V1.0-java", "android.hardware.usb.gadget-V1.0-java", diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index b6f16a7b9ff8..e9b48535a34e 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -16,10 +16,15 @@ package android.os; -import android.hardware.vibrator.V1_0.Constants.EffectStrength; -import android.hardware.vibrator.V1_1.Constants.Effect_1_1; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.vibrator.V1_0.EffectStrength; +import android.hardware.vibrator.V1_2.Effect; +import android.net.Uri; import android.util.MathUtils; +import com.android.internal.annotations.VisibleForTesting; + import java.util.Arrays; /** @@ -49,7 +54,7 @@ public abstract class VibrationEffect implements Parcelable { * @see #get(int) * @hide */ - public static final int EFFECT_CLICK = Effect_1_1.CLICK; + public static final int EFFECT_CLICK = Effect.CLICK; /** * A double click effect. @@ -57,14 +62,62 @@ public abstract class VibrationEffect implements Parcelable { * @see #get(int) * @hide */ - public static final int EFFECT_DOUBLE_CLICK = Effect_1_1.DOUBLE_CLICK; + public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK; /** * A tick effect. * @see #get(int) * @hide */ - public static final int EFFECT_TICK = Effect_1_1.TICK; + public static final int EFFECT_TICK = Effect.TICK; + + /** + * A thud effect. + * @see #get(int) + * @hide + */ + public static final int EFFECT_THUD = Effect.THUD; + + /** + * A pop effect. + * @see #get(int) + * @hide + */ + public static final int EFFECT_POP = Effect.POP; + + /** + * A heavy click effect. + * @see #get(int) + * @hide + */ + public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK; + + + /** + * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a + * pattern that can be played as a ringtone with any audio, depending on the device. + * + * @see #get(Uri, Context) + * @hide + */ + @VisibleForTesting + public static final int[] RINGTONES = { + Effect.RINGTONE_1, + Effect.RINGTONE_2, + Effect.RINGTONE_3, + Effect.RINGTONE_4, + Effect.RINGTONE_5, + Effect.RINGTONE_6, + Effect.RINGTONE_7, + Effect.RINGTONE_8, + Effect.RINGTONE_9, + Effect.RINGTONE_10, + Effect.RINGTONE_11, + Effect.RINGTONE_12, + Effect.RINGTONE_13, + Effect.RINGTONE_14, + Effect.RINGTONE_15 + }; /** @hide to prevent subclassing from outside of the framework */ public VibrationEffect() { } @@ -198,6 +251,37 @@ public abstract class VibrationEffect implements Parcelable { return effect; } + /** + * Get a predefined vibration effect associated with a given URI. + * + * Predefined effects are a set of common vibration effects that should be identical, regardless + * of the app they come from, in order to provide a cohesive experience for users across + * the entire device. They also may be custom tailored to the device hardware in order to + * provide a better experience than you could otherwise build using the generic building + * blocks. + * + * @param uri The URI associated with the haptic effect. + * @param context The context used to get the URI to haptic effect association. + * + * @return The desired effect, or {@code null} if there's no associated effect. + * + * @hide + */ + @Nullable + public static VibrationEffect get(Uri uri, Context context) { + String[] uris = context.getResources().getStringArray( + com.android.internal.R.array.config_ringtoneEffectUris); + for (int i = 0; i < uris.length && i < RINGTONES.length; i++) { + if (uris[i] == null) { + continue; + } + if (Uri.parse(uris[i]).equals(uri)) { + return get(RINGTONES[i]); + } + } + return null; + } + @Override public int describeContents() { return 0; @@ -548,10 +632,15 @@ public abstract class VibrationEffect implements Parcelable { case EFFECT_CLICK: case EFFECT_DOUBLE_CLICK: case EFFECT_TICK: + case EFFECT_THUD: + case EFFECT_POP: + case EFFECT_HEAVY_CLICK: break; default: - throw new IllegalArgumentException( - "Unknown prebaked effect type (value=" + mEffectId + ")"); + if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) { + throw new IllegalArgumentException( + "Unknown prebaked effect type (value=" + mEffectId + ")"); + } } if (!isValidEffectStrength(mEffectStrength)) { throw new IllegalArgumentException( diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9dffe6906a04..d2194ba25029 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1075,6 +1075,14 @@ <item>10</item> </integer-array> + <!-- The URI to associate with each ringtone effect constant, intended to be used with the + android.os.VibrationEffect#get(Uri, Context) API. + The position of the string in the string-array determines which ringtone effect is chosen. + For example, if the URI passed into get match the third string in the string-array, then + RINGTONE_3 will be the returned effect --> + <string-array translatable="false" name="config_ringtoneEffectUris"> + </string-array> + <bool name="config_use_strict_phone_number_comparation">false</bool> <!-- Display low battery warning when battery level dips to this value. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f7b6f06a6806..ca698ef2b55b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3252,4 +3252,6 @@ <java-symbol type="string" name="screenshot_edit" /> <java-symbol type="bool" name="config_keepRestrictedProfilesInBackground" /> + + <java-symbol type="array" name="config_ringtoneEffectUris" /> </resources> diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java new file mode 100644 index 000000000000..c7fdf0fa7d32 --- /dev/null +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.net.Uri; + +import com.android.internal.R; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class VibrationEffectTest { + private static final String RINGTONE_URI_1 = "content://test/system/ringtone_1"; + private static final String RINGTONE_URI_2 = "content://test/system/ringtone_2"; + private static final String RINGTONE_URI_3 = "content://test/system/ringtone_3"; + private static final String UNKNOWN_URI = "content://test/system/other_audio"; + + @Test + public void getRingtones_noPrebakedRingtones() { + Resources r = mockRingtoneResources(new String[0]); + Context context = mockContext(r); + VibrationEffect effect = VibrationEffect.get(Uri.parse(RINGTONE_URI_1), context); + assertNull(effect); + } + + @Test + public void getRingtones_noPrebakedRingtoneForUri() { + Resources r = mockRingtoneResources(); + Context context = mockContext(r); + VibrationEffect effect = VibrationEffect.get(Uri.parse(UNKNOWN_URI), context); + assertNull(effect); + } + + @Test + public void getRingtones_getPrebakedRingtone() { + Resources r = mockRingtoneResources(); + Context context = mockContext(r); + VibrationEffect effect = VibrationEffect.get(Uri.parse(RINGTONE_URI_2), context); + VibrationEffect expectedEffect = VibrationEffect.get(VibrationEffect.RINGTONES[1]); + assertNotNull(expectedEffect); + assertEquals(expectedEffect, effect); + } + + + private Resources mockRingtoneResources() { + return mockRingtoneResources(new String[] { + RINGTONE_URI_1, + RINGTONE_URI_2, + RINGTONE_URI_3 + }); + } + + private Resources mockRingtoneResources(String[] ringtoneUris) { + Resources mockResources = mock(Resources.class); + when(mockResources.getStringArray(R.array.config_ringtoneEffectUris)) + .thenReturn(ringtoneUris); + return mockResources; + } + + private Context mockContext(Resources r) { + Context ctx = mock(Context.class); + when(ctx.getResources()).thenReturn(r); + return ctx; + } +} diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 8d22eca656cd..0baa3d03ba60 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -25,7 +25,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.input.InputManager; -import android.hardware.vibrator.V1_0.Constants.EffectStrength; +import android.hardware.vibrator.V1_0.EffectStrength; import android.icu.text.DateFormat; import android.media.AudioManager; import android.os.PowerManager.ServiceType; diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 5e003ff775fe..4045b72939d8 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -124,6 +124,7 @@ cc_defaults { "android.hardware.tv.input@1.0", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index d2f374dd9e08..016de14f1582 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -18,7 +18,10 @@ #include <android/hardware/vibrator/1.0/IVibrator.h> #include <android/hardware/vibrator/1.0/types.h> -#include <android/hardware/vibrator/1.1/IVibrator.h> +#include <android/hardware/vibrator/1.0/IVibrator.h> +#include <android/hardware/vibrator/1.1/types.h> +#include <android/hardware/vibrator/1.2/IVibrator.h> +#include <android/hardware/vibrator/1.2/types.h> #include "jni.h" #include <nativehelper/JNIHelp.h> @@ -32,15 +35,15 @@ #include <stdio.h> using android::hardware::Return; -using android::hardware::vibrator::V1_0::Effect; using android::hardware::vibrator::V1_0::EffectStrength; -using android::hardware::vibrator::V1_0::IVibrator; using android::hardware::vibrator::V1_0::Status; using android::hardware::vibrator::V1_1::Effect_1_1; -using IVibrator_1_1 = android::hardware::vibrator::V1_1::IVibrator; -namespace android -{ +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; + +namespace android { static constexpr int NUM_TRIES = 2; @@ -84,19 +87,29 @@ Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) { return ret; } +template<class R> +bool isValidEffect(jlong effect) { + if (effect < 0) { + return false; + } + R val = static_cast<R>(effect); + auto iter = hardware::hidl_enum_iterator<R>(); + return val >= *iter.begin() && val < *std::prev(iter.end()); +} + static void vibratorInit(JNIEnv /* env */, jobject /* clazz */) { - halCall(&IVibrator::ping).isOk(); + halCall(&V1_0::IVibrator::ping).isOk(); } static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */) { - return halCall(&IVibrator::ping).isOk() ? JNI_TRUE : JNI_FALSE; + return halCall(&V1_0::IVibrator::ping).isOk() ? JNI_TRUE : JNI_FALSE; } static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms) { - Status retStatus = halCall(&IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR); + Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR); if (retStatus != Status::OK) { ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); } @@ -104,18 +117,18 @@ static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms) static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */) { - Status retStatus = halCall(&IVibrator::off).withDefault(Status::UNKNOWN_ERROR); + Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR); if (retStatus != Status::OK) { ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); } } static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jobject) { - return halCall(&IVibrator::supportsAmplitudeControl).withDefault(false); + return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false); } static void vibratorSetAmplitude(JNIEnv*, jobject, jint amplitude) { - Status status = halCall(&IVibrator::setAmplitude, static_cast<uint32_t>(amplitude)) + Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude)) .withDefault(Status::UNKNOWN_ERROR); if (status != Status::OK) { ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").", @@ -132,22 +145,25 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength }; EffectStrength effectStrength(static_cast<EffectStrength>(strength)); - if (effect < 0 || effect > static_cast<uint32_t>(Effect_1_1::TICK)) { - ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")", - static_cast<int32_t>(effect)); - } else if (effect == static_cast<uint32_t>(Effect_1_1::TICK)) { - auto ret = halCall(&IVibrator_1_1::perform_1_1, static_cast<Effect_1_1>(effect), + Return<void> ret; + if (isValidEffect<V1_0::Effect>(effect)) { + ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect), + effectStrength, callback); + } else if (isValidEffect<Effect_1_1>(effect)) { + ret = halCall(&V1_1::IVibrator::perform_1_1, static_cast<Effect_1_1>(effect), + effectStrength, callback); + } else if (isValidEffect<V1_2::Effect>(effect)) { + ret = halCall(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(effect), effectStrength, callback); - if (!ret.isOk()) { - ALOGW("Failed to perform effect (%" PRId32 "), insufficient HAL version", - static_cast<int32_t>(effect)); - } } else { - auto ret = halCall(&IVibrator::perform, static_cast<Effect>(effect), effectStrength, - callback); - if (!ret.isOk()) { - ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect)); - } + ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")", + static_cast<int32_t>(effect)); + return -1; + } + + if (!ret.isOk()) { + ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect)); + return -1; } if (status == Status::OK) { @@ -160,6 +176,7 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength ", error=%" PRIu32 ").", static_cast<int64_t>(effect), static_cast<int32_t>(strength), static_cast<uint32_t>(status)); } + return -1; } |