Merge "vibrator: Support Always-On Effects"
This commit is contained in:
@@ -24,6 +24,7 @@ interface IVibratorService
|
|||||||
{
|
{
|
||||||
boolean hasVibrator();
|
boolean hasVibrator();
|
||||||
boolean hasAmplitudeControl();
|
boolean hasAmplitudeControl();
|
||||||
|
boolean setAlwaysOnEffect(int id, in VibrationEffect effect, in AudioAttributes attributes);
|
||||||
void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes,
|
void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes,
|
||||||
String reason, IBinder token);
|
String reason, IBinder token);
|
||||||
void cancelVibrate(IBinder token);
|
void cancelVibrate(IBinder token);
|
||||||
|
|||||||
@@ -69,6 +69,20 @@ public class SystemVibrator extends Vibrator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attributes) {
|
||||||
|
if (mService == null) {
|
||||||
|
Log.w(TAG, "Failed to set always-on effect; no vibrator service.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return mService.setAlwaysOnEffect(id, effect, attributes);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, "Failed to set always-on effect.", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vibrate(int uid, String opPkg, VibrationEffect effect,
|
public void vibrate(int uid, String opPkg, VibrationEffect effect,
|
||||||
String reason, AudioAttributes attributes) {
|
String reason, AudioAttributes attributes) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package android.os;
|
package android.os;
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
import android.annotation.SystemService;
|
import android.annotation.SystemService;
|
||||||
import android.annotation.UnsupportedAppUsage;
|
import android.annotation.UnsupportedAppUsage;
|
||||||
@@ -151,6 +152,24 @@ public abstract class Vibrator {
|
|||||||
*/
|
*/
|
||||||
public abstract boolean hasAmplitudeControl();
|
public abstract boolean hasAmplitudeControl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure an always-on haptics effect.
|
||||||
|
*
|
||||||
|
* @param id The board-specific always-on ID to configure.
|
||||||
|
* @param effect Vibration effect to assign to always-on id. Passing null will disable it.
|
||||||
|
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
|
||||||
|
* specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
|
||||||
|
* {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
|
||||||
|
* vibrations associated with incoming calls. May only be null when effect is null.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
|
||||||
|
public boolean setAlwaysOnEffect(int id, @Nullable VibrationEffect effect,
|
||||||
|
@Nullable AudioAttributes attributes) {
|
||||||
|
Log.w(TAG, "Always-on effects aren't supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vibrate constantly for the specified period of time.
|
* Vibrate constantly for the specified period of time.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1868,6 +1868,13 @@
|
|||||||
android:description="@string/permdesc_vibrate"
|
android:description="@string/permdesc_vibrate"
|
||||||
android:protectionLevel="normal|instant" />
|
android:protectionLevel="normal|instant" />
|
||||||
|
|
||||||
|
<!-- Allows access to the vibrator always-on settings.
|
||||||
|
<p>Protection level: signature
|
||||||
|
@hide
|
||||||
|
-->
|
||||||
|
<permission android:name="android.permission.VIBRATE_ALWAYS_ON"
|
||||||
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
|
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
|
||||||
from dimming.
|
from dimming.
|
||||||
<p>Protection level: normal
|
<p>Protection level: normal
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import android.provider.DeviceConfig;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.provider.Settings.SettingNotFoundException;
|
import android.provider.Settings.SettingNotFoundException;
|
||||||
import android.util.DebugUtils;
|
import android.util.DebugUtils;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.StatsLog;
|
import android.util.StatsLog;
|
||||||
@@ -161,6 +162,8 @@ public class VibratorService extends IVibratorService.Stub
|
|||||||
private int mHapticFeedbackIntensity;
|
private int mHapticFeedbackIntensity;
|
||||||
private int mNotificationIntensity;
|
private int mNotificationIntensity;
|
||||||
private int mRingIntensity;
|
private int mRingIntensity;
|
||||||
|
private SparseArray<Pair<VibrationEffect, AudioAttributes>> mAlwaysOnEffects =
|
||||||
|
new SparseArray<>();
|
||||||
|
|
||||||
static native boolean vibratorExists();
|
static native boolean vibratorExists();
|
||||||
static native void vibratorInit();
|
static native void vibratorInit();
|
||||||
@@ -172,6 +175,8 @@ public class VibratorService extends IVibratorService.Stub
|
|||||||
static native boolean vibratorSupportsExternalControl();
|
static native boolean vibratorSupportsExternalControl();
|
||||||
static native void vibratorSetExternalControl(boolean enabled);
|
static native void vibratorSetExternalControl(boolean enabled);
|
||||||
static native long vibratorGetCapabilities();
|
static native long vibratorGetCapabilities();
|
||||||
|
static native void vibratorAlwaysOnEnable(long id, long effect, long strength);
|
||||||
|
static native void vibratorAlwaysOnDisable(long id);
|
||||||
|
|
||||||
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
|
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
|
||||||
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
|
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
|
||||||
@@ -518,6 +523,41 @@ public class VibratorService extends IVibratorService.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override // Binder call
|
||||||
|
public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attrs) {
|
||||||
|
if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
|
||||||
|
throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
|
||||||
|
}
|
||||||
|
if ((mCapabilities & IVibrator.CAP_ALWAYS_ON_CONTROL) == 0) {
|
||||||
|
Slog.e(TAG, "Always-on effects not supported.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (effect == null) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mAlwaysOnEffects.delete(id);
|
||||||
|
vibratorAlwaysOnDisable(id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!verifyVibrationEffect(effect)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(effect instanceof VibrationEffect.Prebaked)) {
|
||||||
|
Slog.e(TAG, "Only prebaked effects supported for always-on.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (attrs == null) {
|
||||||
|
attrs = new AudioAttributes.Builder()
|
||||||
|
.setUsage(AudioAttributes.USAGE_UNKNOWN)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mAlwaysOnEffects.put(id, Pair.create(effect, attrs));
|
||||||
|
updateAlwaysOnLocked(id, effect, attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyIncomingUid(int uid) {
|
private void verifyIncomingUid(int uid) {
|
||||||
if (uid == Binder.getCallingUid()) {
|
if (uid == Binder.getCallingUid()) {
|
||||||
return;
|
return;
|
||||||
@@ -988,6 +1028,8 @@ public class VibratorService extends IVibratorService.Stub
|
|||||||
// If the state changes out from under us then just reset.
|
// If the state changes out from under us then just reset.
|
||||||
doCancelVibrateLocked();
|
doCancelVibrateLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAlwaysOnLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1054,6 +1096,27 @@ public class VibratorService extends IVibratorService.Stub
|
|||||||
mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
|
mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateAlwaysOnLocked(int id, VibrationEffect effect, AudioAttributes attrs) {
|
||||||
|
// TODO: Check DND and LowPower settings
|
||||||
|
final Vibration vib = new Vibration(null, effect, attrs, 0, null, null);
|
||||||
|
final int intensity = getCurrentIntensityLocked(vib);
|
||||||
|
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
|
||||||
|
vibratorAlwaysOnDisable(id);
|
||||||
|
} else {
|
||||||
|
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
|
||||||
|
final int strength = intensityToEffectStrength(intensity);
|
||||||
|
vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAlwaysOnLocked() {
|
||||||
|
for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
|
||||||
|
int id = mAlwaysOnEffects.keyAt(i);
|
||||||
|
Pair<VibrationEffect, AudioAttributes> pair = mAlwaysOnEffects.valueAt(i);
|
||||||
|
updateAlwaysOnLocked(id, pair.first, pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInputDeviceAdded(int deviceId) {
|
public void onInputDeviceAdded(int deviceId) {
|
||||||
updateVibrators();
|
updateVibrators();
|
||||||
|
|||||||
@@ -410,6 +410,21 @@ static jlong vibratorGetCapabilities(JNIEnv*, jclass) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) {
|
||||||
|
auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id,
|
||||||
|
static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength));
|
||||||
|
if (!status.isOk()) {
|
||||||
|
ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) {
|
||||||
|
auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id);
|
||||||
|
if (!status.isOk()) {
|
||||||
|
ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const JNINativeMethod method_table[] = {
|
static const JNINativeMethod method_table[] = {
|
||||||
{ "vibratorExists", "()Z", (void*)vibratorExists },
|
{ "vibratorExists", "()Z", (void*)vibratorExists },
|
||||||
{ "vibratorInit", "()V", (void*)vibratorInit },
|
{ "vibratorInit", "()V", (void*)vibratorInit },
|
||||||
@@ -422,6 +437,8 @@ static const JNINativeMethod method_table[] = {
|
|||||||
{ "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
|
{ "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
|
||||||
{ "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
|
{ "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
|
||||||
{ "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
|
{ "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
|
||||||
|
{ "vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
|
||||||
|
{ "vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_server_VibratorService(JNIEnv *env)
|
int register_android_server_VibratorService(JNIEnv *env)
|
||||||
|
|||||||
Reference in New Issue
Block a user