|
|
|
|
@@ -16,6 +16,7 @@
|
|
|
|
|
|
|
|
|
|
package com.android.server;
|
|
|
|
|
|
|
|
|
|
import android.annotation.Nullable;
|
|
|
|
|
import android.app.ActivityManager;
|
|
|
|
|
import android.app.AppOpsManager;
|
|
|
|
|
import android.app.IUidObserver;
|
|
|
|
|
@@ -193,7 +194,7 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
// with other system events, any duration calculations should be done use startTime so as
|
|
|
|
|
// not to be affected by discontinuities created by RTC adjustments.
|
|
|
|
|
public final long startTimeDebug;
|
|
|
|
|
public final int usageHint;
|
|
|
|
|
public final AudioAttributes attrs;
|
|
|
|
|
public final int uid;
|
|
|
|
|
public final String opPkg;
|
|
|
|
|
public final String reason;
|
|
|
|
|
@@ -206,12 +207,12 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
public VibrationEffect originalEffect;
|
|
|
|
|
|
|
|
|
|
private Vibration(IBinder token, VibrationEffect effect,
|
|
|
|
|
int usageHint, int uid, String opPkg, String reason) {
|
|
|
|
|
AudioAttributes attrs, int uid, String opPkg, String reason) {
|
|
|
|
|
this.token = token;
|
|
|
|
|
this.effect = effect;
|
|
|
|
|
this.startTime = SystemClock.elapsedRealtime();
|
|
|
|
|
this.startTimeDebug = System.currentTimeMillis();
|
|
|
|
|
this.usageHint = usageHint;
|
|
|
|
|
this.attrs = attrs;
|
|
|
|
|
this.uid = uid;
|
|
|
|
|
this.opPkg = opPkg;
|
|
|
|
|
this.reason = reason;
|
|
|
|
|
@@ -231,7 +232,7 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isHapticFeedback() {
|
|
|
|
|
if (VibratorService.this.isHapticFeedback(usageHint)) {
|
|
|
|
|
if (VibratorService.this.isHapticFeedback(attrs.getUsage())) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (effect instanceof VibrationEffect.Prebaked) {
|
|
|
|
|
@@ -256,15 +257,15 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isNotification() {
|
|
|
|
|
return VibratorService.this.isNotification(usageHint);
|
|
|
|
|
return VibratorService.this.isNotification(attrs.getUsage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isRingtone() {
|
|
|
|
|
return VibratorService.this.isRingtone(usageHint);
|
|
|
|
|
return VibratorService.this.isRingtone(attrs.getUsage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isAlarm() {
|
|
|
|
|
return VibratorService.this.isAlarm(usageHint);
|
|
|
|
|
return VibratorService.this.isAlarm(attrs.getUsage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isFromSystem() {
|
|
|
|
|
@@ -273,7 +274,7 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
|
|
|
|
|
public VibrationInfo toInfo() {
|
|
|
|
|
return new VibrationInfo(
|
|
|
|
|
startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
|
|
|
|
|
startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -281,18 +282,18 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
private final long mStartTimeDebug;
|
|
|
|
|
private final VibrationEffect mEffect;
|
|
|
|
|
private final VibrationEffect mOriginalEffect;
|
|
|
|
|
private final int mUsageHint;
|
|
|
|
|
private final AudioAttributes mAttrs;
|
|
|
|
|
private final int mUid;
|
|
|
|
|
private final String mOpPkg;
|
|
|
|
|
private final String mReason;
|
|
|
|
|
|
|
|
|
|
public VibrationInfo(long startTimeDebug, VibrationEffect effect,
|
|
|
|
|
VibrationEffect originalEffect, int usageHint, int uid,
|
|
|
|
|
VibrationEffect originalEffect, AudioAttributes attrs, int uid,
|
|
|
|
|
String opPkg, String reason) {
|
|
|
|
|
mStartTimeDebug = startTimeDebug;
|
|
|
|
|
mEffect = effect;
|
|
|
|
|
mOriginalEffect = originalEffect;
|
|
|
|
|
mUsageHint = usageHint;
|
|
|
|
|
mAttrs = attrs;
|
|
|
|
|
mUid = uid;
|
|
|
|
|
mOpPkg = opPkg;
|
|
|
|
|
mReason = reason;
|
|
|
|
|
@@ -307,8 +308,8 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
.append(mEffect)
|
|
|
|
|
.append(", originalEffect: ")
|
|
|
|
|
.append(mOriginalEffect)
|
|
|
|
|
.append(", usageHint: ")
|
|
|
|
|
.append(mUsageHint)
|
|
|
|
|
.append(", attrs: ")
|
|
|
|
|
.append(mAttrs)
|
|
|
|
|
.append(", uid: ")
|
|
|
|
|
.append(mUid)
|
|
|
|
|
.append(", opPkg: ")
|
|
|
|
|
@@ -549,12 +550,11 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override // Binder call
|
|
|
|
|
public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
|
|
|
|
|
IBinder token) {
|
|
|
|
|
public void vibrate(int uid, String opPkg, VibrationEffect effect,
|
|
|
|
|
@Nullable AudioAttributes attrs, String reason, IBinder token) {
|
|
|
|
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
|
|
|
|
|
try {
|
|
|
|
|
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
|
|
|
|
|
!= PackageManager.PERMISSION_GRANTED) {
|
|
|
|
|
if (!hasPermission(android.Manifest.permission.VIBRATE)) {
|
|
|
|
|
throw new SecurityException("Requires VIBRATE permission");
|
|
|
|
|
}
|
|
|
|
|
if (token == null) {
|
|
|
|
|
@@ -566,6 +566,22 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (attrs == null) {
|
|
|
|
|
attrs = new AudioAttributes.Builder()
|
|
|
|
|
.setUsage(AudioAttributes.USAGE_UNKNOWN)
|
|
|
|
|
.build();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldBypassDnd(attrs)) {
|
|
|
|
|
if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
|
|
|
|
|
|| hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
|
|
|
|
|
|| hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
|
|
|
|
|
final int flags = attrs.getAllFlags()
|
|
|
|
|
& ~AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
|
|
|
|
|
attrs = new AudioAttributes.Builder(attrs).replaceFlags(flags).build();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If our current vibration is longer than the new vibration and is the same amplitude,
|
|
|
|
|
// then just let the current one finish.
|
|
|
|
|
synchronized (mLock) {
|
|
|
|
|
@@ -608,13 +624,13 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
|
|
|
|
|
Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
|
|
|
|
|
if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
|
|
|
|
|
> ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
|
|
|
|
|
&& !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
|
|
|
|
|
Slog.e(TAG, "Ignoring incoming vibration as process with"
|
|
|
|
|
+ " uid = " + uid + " is background,"
|
|
|
|
|
+ " usage = " + AudioAttributes.usageToString(vib.usageHint));
|
|
|
|
|
+ " uid= " + uid + " is background,"
|
|
|
|
|
+ " attrs= " + vib.attrs);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
linkVibration(vib);
|
|
|
|
|
@@ -632,6 +648,11 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean hasPermission(String permission) {
|
|
|
|
|
return mContext.checkCallingOrSelfPermission(permission)
|
|
|
|
|
== PackageManager.PERMISSION_GRANTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean isRepeatingVibration(VibrationEffect effect) {
|
|
|
|
|
return effect.getDuration() == Long.MAX_VALUE;
|
|
|
|
|
}
|
|
|
|
|
@@ -760,14 +781,14 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
if (vib.effect instanceof VibrationEffect.OneShot) {
|
|
|
|
|
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
|
|
|
|
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
|
|
|
|
|
doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
|
|
|
|
|
doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.attrs);
|
|
|
|
|
mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
|
|
|
|
|
} else if (vib.effect instanceof VibrationEffect.Waveform) {
|
|
|
|
|
// mThread better be null here. doCancelVibrate should always be
|
|
|
|
|
// called before startNextVibrationLocked or startVibrationLocked.
|
|
|
|
|
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
|
|
|
|
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
|
|
|
|
|
mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
|
|
|
|
|
mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
|
|
|
|
|
mThread.start();
|
|
|
|
|
} else if (vib.effect instanceof VibrationEffect.Prebaked) {
|
|
|
|
|
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
|
|
|
|
@@ -788,13 +809,14 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
|
|
|
|
|
if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
|
|
|
|
|
vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
|
|
|
|
|
vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
|
|
|
|
|
if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM
|
|
|
|
|
|| vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
|
|
|
|
|
|| vib.attrs.getUsage()
|
|
|
|
|
== AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -887,12 +909,24 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean shouldBypassDnd(AudioAttributes attrs) {
|
|
|
|
|
return (attrs.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int getAppOpMode(Vibration vib) {
|
|
|
|
|
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
|
|
|
|
|
vib.usageHint, vib.uid, vib.opPkg);
|
|
|
|
|
vib.attrs.getUsage(), vib.uid, vib.opPkg);
|
|
|
|
|
if (mode == AppOpsManager.MODE_ALLOWED) {
|
|
|
|
|
mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(vib.attrs)) {
|
|
|
|
|
// If we're just ignoring the vibration op then this is set by DND and we should ignore
|
|
|
|
|
// if we're asked to bypass. AppOps won't be able to record this operation, so make
|
|
|
|
|
// sure we at least note it in the logs for debugging.
|
|
|
|
|
Slog.d(TAG, "Bypassing DND for vibration: " + vib);
|
|
|
|
|
mode = AppOpsManager.MODE_ALLOWED;
|
|
|
|
|
}
|
|
|
|
|
return mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1032,7 +1066,7 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
return vibratorExists();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
|
|
|
|
|
private void doVibratorOn(long millis, int amplitude, int uid, AudioAttributes attrs) {
|
|
|
|
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
|
|
|
|
|
try {
|
|
|
|
|
synchronized (mInputDeviceVibrators) {
|
|
|
|
|
@@ -1046,10 +1080,8 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
noteVibratorOnLocked(uid, millis);
|
|
|
|
|
final int vibratorCount = mInputDeviceVibrators.size();
|
|
|
|
|
if (vibratorCount != 0) {
|
|
|
|
|
final AudioAttributes attributes =
|
|
|
|
|
new AudioAttributes.Builder().setUsage(usageHint).build();
|
|
|
|
|
for (int i = 0; i < vibratorCount; i++) {
|
|
|
|
|
mInputDeviceVibrators.get(i).vibrate(millis, attributes);
|
|
|
|
|
mInputDeviceVibrators.get(i).vibrate(millis, attrs);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Note: ordering is important here! Many haptic drivers will reset their
|
|
|
|
|
@@ -1118,7 +1150,7 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
Slog.w(TAG, "Failed to play prebaked effect, no fallback");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
|
|
|
|
|
Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
|
|
|
|
|
vib.opPkg, vib.reason + " (fallback)");
|
|
|
|
|
final int intensity = getCurrentIntensityLocked(fallbackVib);
|
|
|
|
|
linkVibration(fallbackVib);
|
|
|
|
|
@@ -1213,14 +1245,14 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
private class VibrateThread extends Thread {
|
|
|
|
|
private final VibrationEffect.Waveform mWaveform;
|
|
|
|
|
private final int mUid;
|
|
|
|
|
private final int mUsageHint;
|
|
|
|
|
private final AudioAttributes mAttrs;
|
|
|
|
|
|
|
|
|
|
private boolean mForceStop;
|
|
|
|
|
|
|
|
|
|
VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
|
|
|
|
|
VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) {
|
|
|
|
|
mWaveform = waveform;
|
|
|
|
|
mUid = uid;
|
|
|
|
|
mUsageHint = usageHint;
|
|
|
|
|
mAttrs = attrs;
|
|
|
|
|
mTmpWorkSource.set(uid);
|
|
|
|
|
mWakeLock.setWorkSource(mTmpWorkSource);
|
|
|
|
|
}
|
|
|
|
|
@@ -1295,7 +1327,7 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
// appropriate intervals.
|
|
|
|
|
onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
|
|
|
|
|
repeat);
|
|
|
|
|
doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
|
|
|
|
|
doVibratorOn(onDuration, amplitude, mUid, mAttrs);
|
|
|
|
|
} else {
|
|
|
|
|
doVibratorSetAmplitude(amplitude);
|
|
|
|
|
}
|
|
|
|
|
@@ -1612,8 +1644,9 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
|
|
|
|
|
VibrationEffect effect =
|
|
|
|
|
VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
|
|
|
|
|
vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
|
|
|
|
|
"Shell Command", mToken);
|
|
|
|
|
AudioAttributes attrs = createAudioAttributes(commonOptions);
|
|
|
|
|
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
|
|
|
|
|
mToken);
|
|
|
|
|
return 0;
|
|
|
|
|
} finally {
|
|
|
|
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
|
|
|
|
@@ -1672,8 +1705,9 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
amplitudesList.stream().mapToInt(Integer::intValue).toArray();
|
|
|
|
|
effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
|
|
|
|
|
}
|
|
|
|
|
vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
|
|
|
|
|
"Shell Command", mToken);
|
|
|
|
|
AudioAttributes attrs = createAudioAttributes(commonOptions);
|
|
|
|
|
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
|
|
|
|
|
mToken);
|
|
|
|
|
return 0;
|
|
|
|
|
} finally {
|
|
|
|
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
|
|
|
|
@@ -1703,14 +1737,25 @@ public class VibratorService extends IVibratorService.Stub
|
|
|
|
|
|
|
|
|
|
VibrationEffect effect =
|
|
|
|
|
VibrationEffect.get(id, false);
|
|
|
|
|
vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
|
|
|
|
|
"Shell Command", mToken);
|
|
|
|
|
AudioAttributes attrs = createAudioAttributes(commonOptions);
|
|
|
|
|
vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
|
|
|
|
|
mToken);
|
|
|
|
|
return 0;
|
|
|
|
|
} finally {
|
|
|
|
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private AudioAttributes createAudioAttributes(CommonOptions commonOptions) {
|
|
|
|
|
final int flags = commonOptions.force
|
|
|
|
|
? AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
|
|
|
|
|
: 0;
|
|
|
|
|
return new AudioAttributes.Builder()
|
|
|
|
|
.setUsage(AudioAttributes.USAGE_UNKNOWN)
|
|
|
|
|
.setFlags(flags)
|
|
|
|
|
.build();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onHelp() {
|
|
|
|
|
try (PrintWriter pw = getOutPrintWriter();) {
|
|
|
|
|
|