Loading core/java/android/os/IVibratorService.aidl +3 −2 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; package android.os; import android.media.AudioAttributes; import android.os.VibrationEffect; import android.os.VibrationEffect; /** {@hide} */ /** {@hide} */ Loading @@ -23,8 +24,8 @@ interface IVibratorService { { boolean hasVibrator(); boolean hasVibrator(); boolean hasAmplitudeControl(); boolean hasAmplitudeControl(); void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, String reason, void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes, IBinder token); String reason, IBinder token); void cancelVibrate(IBinder token); void cancelVibrate(IBinder token); } } core/java/android/os/SystemVibrator.java +1 −5 Original line number Original line Diff line number Diff line Loading @@ -77,16 +77,12 @@ public class SystemVibrator extends Vibrator { return; return; } } try { try { mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken); mService.vibrate(uid, opPkg, effect, attributes, reason, mToken); } catch (RemoteException e) { } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); Log.w(TAG, "Failed to vibrate.", e); } } } } private static int usageForAttributes(AudioAttributes attributes) { return attributes != null ? attributes.getUsage() : AudioAttributes.USAGE_UNKNOWN; } @Override @Override public void cancel() { public void cancel() { if (mService == null) { if (mService == null) { Loading services/core/java/com/android/server/VibratorService.java +87 −42 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server; package com.android.server; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.AppOpsManager; import android.app.IUidObserver; import android.app.IUidObserver; Loading Loading @@ -193,7 +194,7 @@ public class VibratorService extends IVibratorService.Stub // with other system events, any duration calculations should be done use startTime so as // with other system events, any duration calculations should be done use startTime so as // not to be affected by discontinuities created by RTC adjustments. // not to be affected by discontinuities created by RTC adjustments. public final long startTimeDebug; public final long startTimeDebug; public final int usageHint; public final AudioAttributes attrs; public final int uid; public final int uid; public final String opPkg; public final String opPkg; public final String reason; public final String reason; Loading @@ -206,12 +207,12 @@ public class VibratorService extends IVibratorService.Stub public VibrationEffect originalEffect; public VibrationEffect originalEffect; private Vibration(IBinder token, VibrationEffect effect, 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.token = token; this.effect = effect; this.effect = effect; this.startTime = SystemClock.elapsedRealtime(); this.startTime = SystemClock.elapsedRealtime(); this.startTimeDebug = System.currentTimeMillis(); this.startTimeDebug = System.currentTimeMillis(); this.usageHint = usageHint; this.attrs = attrs; this.uid = uid; this.uid = uid; this.opPkg = opPkg; this.opPkg = opPkg; this.reason = reason; this.reason = reason; Loading @@ -231,7 +232,7 @@ public class VibratorService extends IVibratorService.Stub } } public boolean isHapticFeedback() { public boolean isHapticFeedback() { if (VibratorService.this.isHapticFeedback(usageHint)) { if (VibratorService.this.isHapticFeedback(attrs.getUsage())) { return true; return true; } } if (effect instanceof VibrationEffect.Prebaked) { if (effect instanceof VibrationEffect.Prebaked) { Loading @@ -256,15 +257,15 @@ public class VibratorService extends IVibratorService.Stub } } public boolean isNotification() { public boolean isNotification() { return VibratorService.this.isNotification(usageHint); return VibratorService.this.isNotification(attrs.getUsage()); } } public boolean isRingtone() { public boolean isRingtone() { return VibratorService.this.isRingtone(usageHint); return VibratorService.this.isRingtone(attrs.getUsage()); } } public boolean isAlarm() { public boolean isAlarm() { return VibratorService.this.isAlarm(usageHint); return VibratorService.this.isAlarm(attrs.getUsage()); } } public boolean isFromSystem() { public boolean isFromSystem() { Loading @@ -273,7 +274,7 @@ public class VibratorService extends IVibratorService.Stub public VibrationInfo toInfo() { public VibrationInfo toInfo() { return new VibrationInfo( return new VibrationInfo( startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason); startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason); } } } } Loading @@ -281,18 +282,18 @@ public class VibratorService extends IVibratorService.Stub private final long mStartTimeDebug; private final long mStartTimeDebug; private final VibrationEffect mEffect; private final VibrationEffect mEffect; private final VibrationEffect mOriginalEffect; private final VibrationEffect mOriginalEffect; private final int mUsageHint; private final AudioAttributes mAttrs; private final int mUid; private final int mUid; private final String mOpPkg; private final String mOpPkg; private final String mReason; private final String mReason; public VibrationInfo(long startTimeDebug, VibrationEffect effect, public VibrationInfo(long startTimeDebug, VibrationEffect effect, VibrationEffect originalEffect, int usageHint, int uid, VibrationEffect originalEffect, AudioAttributes attrs, int uid, String opPkg, String reason) { String opPkg, String reason) { mStartTimeDebug = startTimeDebug; mStartTimeDebug = startTimeDebug; mEffect = effect; mEffect = effect; mOriginalEffect = originalEffect; mOriginalEffect = originalEffect; mUsageHint = usageHint; mAttrs = attrs; mUid = uid; mUid = uid; mOpPkg = opPkg; mOpPkg = opPkg; mReason = reason; mReason = reason; Loading @@ -307,8 +308,8 @@ public class VibratorService extends IVibratorService.Stub .append(mEffect) .append(mEffect) .append(", originalEffect: ") .append(", originalEffect: ") .append(mOriginalEffect) .append(mOriginalEffect) .append(", usageHint: ") .append(", attrs: ") .append(mUsageHint) .append(mAttrs) .append(", uid: ") .append(", uid: ") .append(mUid) .append(mUid) .append(", opPkg: ") .append(", opPkg: ") Loading Loading @@ -549,12 +550,11 @@ public class VibratorService extends IVibratorService.Stub } } @Override // Binder call @Override // Binder call public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason, public void vibrate(int uid, String opPkg, VibrationEffect effect, IBinder token) { @Nullable AudioAttributes attrs, String reason, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); try { try { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) if (!hasPermission(android.Manifest.permission.VIBRATE)) { != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); throw new SecurityException("Requires VIBRATE permission"); } } if (token == null) { if (token == null) { Loading @@ -566,6 +566,22 @@ public class VibratorService extends IVibratorService.Stub return; 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, // If our current vibration is longer than the new vibration and is the same amplitude, // then just let the current one finish. // then just let the current one finish. synchronized (mLock) { synchronized (mLock) { Loading Loading @@ -608,13 +624,13 @@ public class VibratorService extends IVibratorService.Stub return; 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) if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) { && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) { Slog.e(TAG, "Ignoring incoming vibration as process with" Slog.e(TAG, "Ignoring incoming vibration as process with" + " uid= " + uid + " is background," + " uid= " + uid + " is background," + " usage = " + AudioAttributes.usageToString(vib.usageHint)); + " attrs= " + vib.attrs); return; return; } } linkVibration(vib); linkVibration(vib); Loading @@ -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) { private static boolean isRepeatingVibration(VibrationEffect effect) { return effect.getDuration() == Long.MAX_VALUE; return effect.getDuration() == Long.MAX_VALUE; } } Loading Loading @@ -760,14 +781,14 @@ public class VibratorService extends IVibratorService.Stub if (vib.effect instanceof VibrationEffect.OneShot) { if (vib.effect instanceof VibrationEffect.OneShot) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; 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()); mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration()); } else if (vib.effect instanceof VibrationEffect.Waveform) { } else if (vib.effect instanceof VibrationEffect.Waveform) { // mThread better be null here. doCancelVibrate should always be // mThread better be null here. doCancelVibrate should always be // called before startNextVibrationLocked or startVibrationLocked. // called before startNextVibrationLocked or startVibrationLocked. Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; 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(); mThread.start(); } else if (vib.effect instanceof VibrationEffect.Prebaked) { } else if (vib.effect instanceof VibrationEffect.Prebaked) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Loading @@ -788,13 +809,14 @@ public class VibratorService extends IVibratorService.Stub return true; return true; } } if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { return true; return true; } } if (vib.usageHint == AudioAttributes.USAGE_ALARM || if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY || || vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { || vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { return true; return true; } } Loading Loading @@ -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) { private int getAppOpMode(Vibration vib) { int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, 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) { if (mode == AppOpsManager.MODE_ALLOWED) { mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg); 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; return mode; } } Loading Loading @@ -1032,7 +1066,7 @@ public class VibratorService extends IVibratorService.Stub return vibratorExists(); 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"); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { try { synchronized (mInputDeviceVibrators) { synchronized (mInputDeviceVibrators) { Loading @@ -1046,10 +1080,8 @@ public class VibratorService extends IVibratorService.Stub noteVibratorOnLocked(uid, millis); noteVibratorOnLocked(uid, millis); final int vibratorCount = mInputDeviceVibrators.size(); final int vibratorCount = mInputDeviceVibrators.size(); if (vibratorCount != 0) { if (vibratorCount != 0) { final AudioAttributes attributes = new AudioAttributes.Builder().setUsage(usageHint).build(); for (int i = 0; i < vibratorCount; i++) { for (int i = 0; i < vibratorCount; i++) { mInputDeviceVibrators.get(i).vibrate(millis, attributes); mInputDeviceVibrators.get(i).vibrate(millis, attrs); } } } else { } else { // Note: ordering is important here! Many haptic drivers will reset their // Note: ordering is important here! Many haptic drivers will reset their Loading Loading @@ -1118,7 +1150,7 @@ public class VibratorService extends IVibratorService.Stub Slog.w(TAG, "Failed to play prebaked effect, no fallback"); Slog.w(TAG, "Failed to play prebaked effect, no fallback"); return 0; 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)"); vib.opPkg, vib.reason + " (fallback)"); final int intensity = getCurrentIntensityLocked(fallbackVib); final int intensity = getCurrentIntensityLocked(fallbackVib); linkVibration(fallbackVib); linkVibration(fallbackVib); Loading Loading @@ -1213,14 +1245,14 @@ public class VibratorService extends IVibratorService.Stub private class VibrateThread extends Thread { private class VibrateThread extends Thread { private final VibrationEffect.Waveform mWaveform; private final VibrationEffect.Waveform mWaveform; private final int mUid; private final int mUid; private final int mUsageHint; private final AudioAttributes mAttrs; private boolean mForceStop; private boolean mForceStop; VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) { VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) { mWaveform = waveform; mWaveform = waveform; mUid = uid; mUid = uid; mUsageHint = usageHint; mAttrs = attrs; mTmpWorkSource.set(uid); mTmpWorkSource.set(uid); mWakeLock.setWorkSource(mTmpWorkSource); mWakeLock.setWorkSource(mTmpWorkSource); } } Loading Loading @@ -1295,7 +1327,7 @@ public class VibratorService extends IVibratorService.Stub // appropriate intervals. // appropriate intervals. onDuration = getTotalOnDuration(timings, amplitudes, index - 1, onDuration = getTotalOnDuration(timings, amplitudes, index - 1, repeat); repeat); doVibratorOn(onDuration, amplitude, mUid, mUsageHint); doVibratorOn(onDuration, amplitude, mUid, mAttrs); } else { } else { doVibratorSetAmplitude(amplitude); doVibratorSetAmplitude(amplitude); } } Loading Loading @@ -1612,8 +1644,9 @@ public class VibratorService extends IVibratorService.Stub VibrationEffect effect = VibrationEffect effect = VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, AudioAttributes attrs = createAudioAttributes(commonOptions); "Shell Command", mToken); vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command", mToken); return 0; return 0; } finally { } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Loading Loading @@ -1672,8 +1705,9 @@ public class VibratorService extends IVibratorService.Stub amplitudesList.stream().mapToInt(Integer::intValue).toArray(); amplitudesList.stream().mapToInt(Integer::intValue).toArray(); effect = VibrationEffect.createWaveform(timings, amplitudes, repeat); effect = VibrationEffect.createWaveform(timings, amplitudes, repeat); } } vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, AudioAttributes attrs = createAudioAttributes(commonOptions); "Shell Command", mToken); vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command", mToken); return 0; return 0; } finally { } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Loading Loading @@ -1703,14 +1737,25 @@ public class VibratorService extends IVibratorService.Stub VibrationEffect effect = VibrationEffect effect = VibrationEffect.get(id, false); VibrationEffect.get(id, false); vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, AudioAttributes attrs = createAudioAttributes(commonOptions); "Shell Command", mToken); vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command", mToken); return 0; return 0; } finally { } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 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 @Override public void onHelp() { public void onHelp() { try (PrintWriter pw = getOutPrintWriter();) { try (PrintWriter pw = getOutPrintWriter();) { Loading tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java +8 −4 Original line number Original line Diff line number Diff line Loading @@ -16,9 +16,7 @@ package com.android.framework.permission.tests; package com.android.framework.permission.tests; import junit.framework.TestCase; import android.media.AudioAttributes; import android.media.AudioManager; import android.os.Binder; import android.os.Binder; import android.os.IVibratorService; import android.os.IVibratorService; import android.os.Process; import android.os.Process; Loading @@ -27,6 +25,9 @@ import android.os.ServiceManager; import android.os.VibrationEffect; import android.os.VibrationEffect; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; /** /** * Verify that Hardware apis cannot be called without required permissions. * Verify that Hardware apis cannot be called without required permissions. */ */ Loading @@ -51,7 +52,10 @@ public class VibratorServicePermissionTest extends TestCase { try { try { final VibrationEffect effect = final VibrationEffect effect = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE); VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE); mVibratorService.vibrate(Process.myUid(), null, effect, AudioManager.STREAM_ALARM, final AudioAttributes attrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_ALARM) .build(); mVibratorService.vibrate(Process.myUid(), null, effect, attrs, "testVibrate", new Binder()); "testVibrate", new Binder()); fail("vibrate did not throw SecurityException as expected"); fail("vibrate did not throw SecurityException as expected"); } catch (SecurityException e) { } catch (SecurityException e) { Loading Loading
core/java/android/os/IVibratorService.aidl +3 −2 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; package android.os; import android.media.AudioAttributes; import android.os.VibrationEffect; import android.os.VibrationEffect; /** {@hide} */ /** {@hide} */ Loading @@ -23,8 +24,8 @@ interface IVibratorService { { boolean hasVibrator(); boolean hasVibrator(); boolean hasAmplitudeControl(); boolean hasAmplitudeControl(); void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, String reason, void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes, IBinder token); String reason, IBinder token); void cancelVibrate(IBinder token); void cancelVibrate(IBinder token); } }
core/java/android/os/SystemVibrator.java +1 −5 Original line number Original line Diff line number Diff line Loading @@ -77,16 +77,12 @@ public class SystemVibrator extends Vibrator { return; return; } } try { try { mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken); mService.vibrate(uid, opPkg, effect, attributes, reason, mToken); } catch (RemoteException e) { } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); Log.w(TAG, "Failed to vibrate.", e); } } } } private static int usageForAttributes(AudioAttributes attributes) { return attributes != null ? attributes.getUsage() : AudioAttributes.USAGE_UNKNOWN; } @Override @Override public void cancel() { public void cancel() { if (mService == null) { if (mService == null) { Loading
services/core/java/com/android/server/VibratorService.java +87 −42 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server; package com.android.server; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.AppOpsManager; import android.app.IUidObserver; import android.app.IUidObserver; Loading Loading @@ -193,7 +194,7 @@ public class VibratorService extends IVibratorService.Stub // with other system events, any duration calculations should be done use startTime so as // with other system events, any duration calculations should be done use startTime so as // not to be affected by discontinuities created by RTC adjustments. // not to be affected by discontinuities created by RTC adjustments. public final long startTimeDebug; public final long startTimeDebug; public final int usageHint; public final AudioAttributes attrs; public final int uid; public final int uid; public final String opPkg; public final String opPkg; public final String reason; public final String reason; Loading @@ -206,12 +207,12 @@ public class VibratorService extends IVibratorService.Stub public VibrationEffect originalEffect; public VibrationEffect originalEffect; private Vibration(IBinder token, VibrationEffect effect, 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.token = token; this.effect = effect; this.effect = effect; this.startTime = SystemClock.elapsedRealtime(); this.startTime = SystemClock.elapsedRealtime(); this.startTimeDebug = System.currentTimeMillis(); this.startTimeDebug = System.currentTimeMillis(); this.usageHint = usageHint; this.attrs = attrs; this.uid = uid; this.uid = uid; this.opPkg = opPkg; this.opPkg = opPkg; this.reason = reason; this.reason = reason; Loading @@ -231,7 +232,7 @@ public class VibratorService extends IVibratorService.Stub } } public boolean isHapticFeedback() { public boolean isHapticFeedback() { if (VibratorService.this.isHapticFeedback(usageHint)) { if (VibratorService.this.isHapticFeedback(attrs.getUsage())) { return true; return true; } } if (effect instanceof VibrationEffect.Prebaked) { if (effect instanceof VibrationEffect.Prebaked) { Loading @@ -256,15 +257,15 @@ public class VibratorService extends IVibratorService.Stub } } public boolean isNotification() { public boolean isNotification() { return VibratorService.this.isNotification(usageHint); return VibratorService.this.isNotification(attrs.getUsage()); } } public boolean isRingtone() { public boolean isRingtone() { return VibratorService.this.isRingtone(usageHint); return VibratorService.this.isRingtone(attrs.getUsage()); } } public boolean isAlarm() { public boolean isAlarm() { return VibratorService.this.isAlarm(usageHint); return VibratorService.this.isAlarm(attrs.getUsage()); } } public boolean isFromSystem() { public boolean isFromSystem() { Loading @@ -273,7 +274,7 @@ public class VibratorService extends IVibratorService.Stub public VibrationInfo toInfo() { public VibrationInfo toInfo() { return new VibrationInfo( return new VibrationInfo( startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason); startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason); } } } } Loading @@ -281,18 +282,18 @@ public class VibratorService extends IVibratorService.Stub private final long mStartTimeDebug; private final long mStartTimeDebug; private final VibrationEffect mEffect; private final VibrationEffect mEffect; private final VibrationEffect mOriginalEffect; private final VibrationEffect mOriginalEffect; private final int mUsageHint; private final AudioAttributes mAttrs; private final int mUid; private final int mUid; private final String mOpPkg; private final String mOpPkg; private final String mReason; private final String mReason; public VibrationInfo(long startTimeDebug, VibrationEffect effect, public VibrationInfo(long startTimeDebug, VibrationEffect effect, VibrationEffect originalEffect, int usageHint, int uid, VibrationEffect originalEffect, AudioAttributes attrs, int uid, String opPkg, String reason) { String opPkg, String reason) { mStartTimeDebug = startTimeDebug; mStartTimeDebug = startTimeDebug; mEffect = effect; mEffect = effect; mOriginalEffect = originalEffect; mOriginalEffect = originalEffect; mUsageHint = usageHint; mAttrs = attrs; mUid = uid; mUid = uid; mOpPkg = opPkg; mOpPkg = opPkg; mReason = reason; mReason = reason; Loading @@ -307,8 +308,8 @@ public class VibratorService extends IVibratorService.Stub .append(mEffect) .append(mEffect) .append(", originalEffect: ") .append(", originalEffect: ") .append(mOriginalEffect) .append(mOriginalEffect) .append(", usageHint: ") .append(", attrs: ") .append(mUsageHint) .append(mAttrs) .append(", uid: ") .append(", uid: ") .append(mUid) .append(mUid) .append(", opPkg: ") .append(", opPkg: ") Loading Loading @@ -549,12 +550,11 @@ public class VibratorService extends IVibratorService.Stub } } @Override // Binder call @Override // Binder call public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason, public void vibrate(int uid, String opPkg, VibrationEffect effect, IBinder token) { @Nullable AudioAttributes attrs, String reason, IBinder token) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); try { try { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) if (!hasPermission(android.Manifest.permission.VIBRATE)) { != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); throw new SecurityException("Requires VIBRATE permission"); } } if (token == null) { if (token == null) { Loading @@ -566,6 +566,22 @@ public class VibratorService extends IVibratorService.Stub return; 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, // If our current vibration is longer than the new vibration and is the same amplitude, // then just let the current one finish. // then just let the current one finish. synchronized (mLock) { synchronized (mLock) { Loading Loading @@ -608,13 +624,13 @@ public class VibratorService extends IVibratorService.Stub return; 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) if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) { && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) { Slog.e(TAG, "Ignoring incoming vibration as process with" Slog.e(TAG, "Ignoring incoming vibration as process with" + " uid= " + uid + " is background," + " uid= " + uid + " is background," + " usage = " + AudioAttributes.usageToString(vib.usageHint)); + " attrs= " + vib.attrs); return; return; } } linkVibration(vib); linkVibration(vib); Loading @@ -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) { private static boolean isRepeatingVibration(VibrationEffect effect) { return effect.getDuration() == Long.MAX_VALUE; return effect.getDuration() == Long.MAX_VALUE; } } Loading Loading @@ -760,14 +781,14 @@ public class VibratorService extends IVibratorService.Stub if (vib.effect instanceof VibrationEffect.OneShot) { if (vib.effect instanceof VibrationEffect.OneShot) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; 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()); mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration()); } else if (vib.effect instanceof VibrationEffect.Waveform) { } else if (vib.effect instanceof VibrationEffect.Waveform) { // mThread better be null here. doCancelVibrate should always be // mThread better be null here. doCancelVibrate should always be // called before startNextVibrationLocked or startVibrationLocked. // called before startNextVibrationLocked or startVibrationLocked. Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; 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(); mThread.start(); } else if (vib.effect instanceof VibrationEffect.Prebaked) { } else if (vib.effect instanceof VibrationEffect.Prebaked) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Loading @@ -788,13 +809,14 @@ public class VibratorService extends IVibratorService.Stub return true; return true; } } if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { return true; return true; } } if (vib.usageHint == AudioAttributes.USAGE_ALARM || if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY || || vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { || vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { return true; return true; } } Loading Loading @@ -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) { private int getAppOpMode(Vibration vib) { int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, 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) { if (mode == AppOpsManager.MODE_ALLOWED) { mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg); 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; return mode; } } Loading Loading @@ -1032,7 +1066,7 @@ public class VibratorService extends IVibratorService.Stub return vibratorExists(); 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"); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { try { synchronized (mInputDeviceVibrators) { synchronized (mInputDeviceVibrators) { Loading @@ -1046,10 +1080,8 @@ public class VibratorService extends IVibratorService.Stub noteVibratorOnLocked(uid, millis); noteVibratorOnLocked(uid, millis); final int vibratorCount = mInputDeviceVibrators.size(); final int vibratorCount = mInputDeviceVibrators.size(); if (vibratorCount != 0) { if (vibratorCount != 0) { final AudioAttributes attributes = new AudioAttributes.Builder().setUsage(usageHint).build(); for (int i = 0; i < vibratorCount; i++) { for (int i = 0; i < vibratorCount; i++) { mInputDeviceVibrators.get(i).vibrate(millis, attributes); mInputDeviceVibrators.get(i).vibrate(millis, attrs); } } } else { } else { // Note: ordering is important here! Many haptic drivers will reset their // Note: ordering is important here! Many haptic drivers will reset their Loading Loading @@ -1118,7 +1150,7 @@ public class VibratorService extends IVibratorService.Stub Slog.w(TAG, "Failed to play prebaked effect, no fallback"); Slog.w(TAG, "Failed to play prebaked effect, no fallback"); return 0; 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)"); vib.opPkg, vib.reason + " (fallback)"); final int intensity = getCurrentIntensityLocked(fallbackVib); final int intensity = getCurrentIntensityLocked(fallbackVib); linkVibration(fallbackVib); linkVibration(fallbackVib); Loading Loading @@ -1213,14 +1245,14 @@ public class VibratorService extends IVibratorService.Stub private class VibrateThread extends Thread { private class VibrateThread extends Thread { private final VibrationEffect.Waveform mWaveform; private final VibrationEffect.Waveform mWaveform; private final int mUid; private final int mUid; private final int mUsageHint; private final AudioAttributes mAttrs; private boolean mForceStop; private boolean mForceStop; VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) { VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) { mWaveform = waveform; mWaveform = waveform; mUid = uid; mUid = uid; mUsageHint = usageHint; mAttrs = attrs; mTmpWorkSource.set(uid); mTmpWorkSource.set(uid); mWakeLock.setWorkSource(mTmpWorkSource); mWakeLock.setWorkSource(mTmpWorkSource); } } Loading Loading @@ -1295,7 +1327,7 @@ public class VibratorService extends IVibratorService.Stub // appropriate intervals. // appropriate intervals. onDuration = getTotalOnDuration(timings, amplitudes, index - 1, onDuration = getTotalOnDuration(timings, amplitudes, index - 1, repeat); repeat); doVibratorOn(onDuration, amplitude, mUid, mUsageHint); doVibratorOn(onDuration, amplitude, mUid, mAttrs); } else { } else { doVibratorSetAmplitude(amplitude); doVibratorSetAmplitude(amplitude); } } Loading Loading @@ -1612,8 +1644,9 @@ public class VibratorService extends IVibratorService.Stub VibrationEffect effect = VibrationEffect effect = VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, AudioAttributes attrs = createAudioAttributes(commonOptions); "Shell Command", mToken); vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command", mToken); return 0; return 0; } finally { } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Loading Loading @@ -1672,8 +1705,9 @@ public class VibratorService extends IVibratorService.Stub amplitudesList.stream().mapToInt(Integer::intValue).toArray(); amplitudesList.stream().mapToInt(Integer::intValue).toArray(); effect = VibrationEffect.createWaveform(timings, amplitudes, repeat); effect = VibrationEffect.createWaveform(timings, amplitudes, repeat); } } vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, AudioAttributes attrs = createAudioAttributes(commonOptions); "Shell Command", mToken); vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command", mToken); return 0; return 0; } finally { } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); Loading Loading @@ -1703,14 +1737,25 @@ public class VibratorService extends IVibratorService.Stub VibrationEffect effect = VibrationEffect effect = VibrationEffect.get(id, false); VibrationEffect.get(id, false); vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, AudioAttributes attrs = createAudioAttributes(commonOptions); "Shell Command", mToken); vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command", mToken); return 0; return 0; } finally { } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 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 @Override public void onHelp() { public void onHelp() { try (PrintWriter pw = getOutPrintWriter();) { try (PrintWriter pw = getOutPrintWriter();) { Loading
tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java +8 −4 Original line number Original line Diff line number Diff line Loading @@ -16,9 +16,7 @@ package com.android.framework.permission.tests; package com.android.framework.permission.tests; import junit.framework.TestCase; import android.media.AudioAttributes; import android.media.AudioManager; import android.os.Binder; import android.os.Binder; import android.os.IVibratorService; import android.os.IVibratorService; import android.os.Process; import android.os.Process; Loading @@ -27,6 +25,9 @@ import android.os.ServiceManager; import android.os.VibrationEffect; import android.os.VibrationEffect; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; /** /** * Verify that Hardware apis cannot be called without required permissions. * Verify that Hardware apis cannot be called without required permissions. */ */ Loading @@ -51,7 +52,10 @@ public class VibratorServicePermissionTest extends TestCase { try { try { final VibrationEffect effect = final VibrationEffect effect = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE); VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE); mVibratorService.vibrate(Process.myUid(), null, effect, AudioManager.STREAM_ALARM, final AudioAttributes attrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_ALARM) .build(); mVibratorService.vibrate(Process.myUid(), null, effect, attrs, "testVibrate", new Binder()); "testVibrate", new Binder()); fail("vibrate did not throw SecurityException as expected"); fail("vibrate did not throw SecurityException as expected"); } catch (SecurityException e) { } catch (SecurityException e) { Loading