1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vibrator; 18 19 import android.annotation.Nullable; 20 import android.app.ActivityManager; 21 import android.app.IUidObserver; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.res.Resources; 27 import android.database.ContentObserver; 28 import android.media.AudioManager; 29 import android.net.Uri; 30 import android.os.Handler; 31 import android.os.PowerManager; 32 import android.os.PowerManagerInternal; 33 import android.os.PowerSaveState; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.os.VibrationAttributes; 37 import android.os.VibrationEffect; 38 import android.os.Vibrator; 39 import android.provider.Settings; 40 import android.util.SparseArray; 41 import android.util.proto.ProtoOutputStream; 42 43 import com.android.internal.annotations.GuardedBy; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.server.LocalServices; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 50 /** Controls all the system settings related to vibration. */ 51 final class VibrationSettings { 52 private static final String TAG = "VibrationSettings"; 53 54 private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = {0, 30, 100, 30}; 55 56 /** Listener for changes on vibration settings. */ 57 interface OnVibratorSettingsChanged { 58 /** Callback triggered when any of the vibrator settings change. */ onChange()59 void onChange(); 60 } 61 62 private final Object mLock = new Object(); 63 private final Context mContext; 64 private final SettingsObserver mSettingObserver; 65 @VisibleForTesting 66 final UidObserver mUidObserver; 67 @VisibleForTesting 68 final UserObserver mUserReceiver; 69 70 @GuardedBy("mLock") 71 private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); 72 private final SparseArray<VibrationEffect> mFallbackEffects; 73 74 private final int mRampStepDuration; 75 private final int mRampDownDuration; 76 77 @GuardedBy("mLock") 78 @Nullable 79 private Vibrator mVibrator; 80 @GuardedBy("mLock") 81 @Nullable 82 private AudioManager mAudioManager; 83 84 @GuardedBy("mLock") 85 private boolean mVibrateInputDevices; 86 @GuardedBy("mLock") 87 private boolean mVibrateWhenRinging; 88 @GuardedBy("mLock") 89 private boolean mApplyRampingRinger; 90 @GuardedBy("mLock") 91 private int mZenMode; 92 @GuardedBy("mLock") 93 private int mHapticFeedbackIntensity; 94 @GuardedBy("mLock") 95 private int mNotificationIntensity; 96 @GuardedBy("mLock") 97 private int mRingIntensity; 98 @GuardedBy("mLock") 99 private boolean mLowPowerMode; 100 VibrationSettings(Context context, Handler handler)101 VibrationSettings(Context context, Handler handler) { 102 this(context, handler, 103 context.getResources().getInteger( 104 com.android.internal.R.integer.config_vibrationWaveformRampDownDuration), 105 context.getResources().getInteger( 106 com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)); 107 } 108 109 @VisibleForTesting VibrationSettings(Context context, Handler handler, int rampDownDuration, int rampStepDuration)110 VibrationSettings(Context context, Handler handler, int rampDownDuration, 111 int rampStepDuration) { 112 mContext = context; 113 mSettingObserver = new SettingsObserver(handler); 114 mUidObserver = new UidObserver(); 115 mUserReceiver = new UserObserver(); 116 117 // TODO(b/191150049): move these to vibrator static config file 118 mRampDownDuration = rampDownDuration; 119 mRampStepDuration = rampStepDuration; 120 121 VibrationEffect clickEffect = createEffectFromResource( 122 com.android.internal.R.array.config_virtualKeyVibePattern); 123 VibrationEffect doubleClickEffect = VibrationEffect.createWaveform( 124 DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/); 125 VibrationEffect heavyClickEffect = createEffectFromResource( 126 com.android.internal.R.array.config_longPressVibePattern); 127 VibrationEffect tickEffect = createEffectFromResource( 128 com.android.internal.R.array.config_clockTickVibePattern); 129 130 mFallbackEffects = new SparseArray<>(); 131 mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect); 132 mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect); 133 mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect); 134 mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect); 135 mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK, 136 VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)); 137 138 // Update with current values from settings. 139 updateSettings(); 140 } 141 onSystemReady()142 public void onSystemReady() { 143 synchronized (mLock) { 144 mVibrator = mContext.getSystemService(Vibrator.class); 145 mAudioManager = mContext.getSystemService(AudioManager.class); 146 } 147 try { 148 ActivityManager.getService().registerUidObserver(mUidObserver, 149 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, 150 ActivityManager.PROCESS_STATE_UNKNOWN, null); 151 } catch (RemoteException e) { 152 // ignored; both services live in system_server 153 } 154 155 PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); 156 pm.registerLowPowerModeObserver( 157 new PowerManagerInternal.LowPowerModeListener() { 158 @Override 159 public int getServiceType() { 160 return PowerManager.ServiceType.VIBRATION; 161 } 162 163 @Override 164 public void onLowPowerModeChanged(PowerSaveState result) { 165 boolean shouldNotifyListeners; 166 synchronized (mLock) { 167 shouldNotifyListeners = result.batterySaverEnabled != mLowPowerMode; 168 mLowPowerMode = result.batterySaverEnabled; 169 } 170 if (shouldNotifyListeners) { 171 notifyListeners(); 172 } 173 } 174 }); 175 176 mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); 177 registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); 178 registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); 179 registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER)); 180 registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE)); 181 registerSettingsObserver( 182 Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY)); 183 registerSettingsObserver( 184 Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY)); 185 registerSettingsObserver( 186 Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY)); 187 188 // Update with newly loaded services. 189 updateSettings(); 190 } 191 192 /** 193 * Add listener to vibrator settings changes. This will trigger the listener with current state 194 * immediately and every time one of the settings change. 195 */ addListener(OnVibratorSettingsChanged listener)196 public void addListener(OnVibratorSettingsChanged listener) { 197 synchronized (mLock) { 198 if (!mListeners.contains(listener)) { 199 mListeners.add(listener); 200 } 201 } 202 } 203 204 /** Remove listener to vibrator settings. */ removeListener(OnVibratorSettingsChanged listener)205 public void removeListener(OnVibratorSettingsChanged listener) { 206 synchronized (mLock) { 207 mListeners.remove(listener); 208 } 209 } 210 211 /** 212 * The duration, in milliseconds, that should be applied to convert vibration effect's 213 * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on 214 * devices without PWLE support. 215 */ getRampStepDuration()216 public int getRampStepDuration() { 217 return mRampStepDuration; 218 } 219 220 /** 221 * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator 222 * when a vibration is cancelled or finished at non-zero amplitude. 223 */ getRampDownDuration()224 public int getRampDownDuration() { 225 return mRampDownDuration; 226 } 227 228 /** 229 * Return default vibration intensity for given usage. 230 * 231 * @param usageHint one of VibrationAttributes.USAGE_* 232 * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_* 233 */ getDefaultIntensity(int usageHint)234 public int getDefaultIntensity(int usageHint) { 235 if (isAlarm(usageHint)) { 236 return Vibrator.VIBRATION_INTENSITY_HIGH; 237 } 238 synchronized (mLock) { 239 if (mVibrator != null) { 240 if (isRingtone(usageHint)) { 241 return mVibrator.getDefaultRingVibrationIntensity(); 242 } else if (isNotification(usageHint)) { 243 return mVibrator.getDefaultNotificationVibrationIntensity(); 244 } else if (isHapticFeedback(usageHint)) { 245 return mVibrator.getDefaultHapticFeedbackIntensity(); 246 } 247 } 248 } 249 return Vibrator.VIBRATION_INTENSITY_MEDIUM; 250 } 251 252 /** 253 * Return the current vibration intensity set for given usage at the user settings. 254 * 255 * @param usageHint one of VibrationAttributes.USAGE_* 256 * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_* 257 */ getCurrentIntensity(int usageHint)258 public int getCurrentIntensity(int usageHint) { 259 synchronized (mLock) { 260 if (isRingtone(usageHint)) { 261 return mRingIntensity; 262 } else if (isNotification(usageHint)) { 263 return mNotificationIntensity; 264 } else if (isHapticFeedback(usageHint)) { 265 return mHapticFeedbackIntensity; 266 } else if (isAlarm(usageHint)) { 267 return Vibrator.VIBRATION_INTENSITY_HIGH; 268 } else { 269 return Vibrator.VIBRATION_INTENSITY_MEDIUM; 270 } 271 } 272 } 273 274 /** 275 * Return a {@link VibrationEffect} that should be played if the device do not support given 276 * {@code effectId}. 277 * 278 * @param effectId one of VibrationEffect.EFFECT_* 279 * @return The effect to be played as a fallback 280 */ getFallbackEffect(int effectId)281 public VibrationEffect getFallbackEffect(int effectId) { 282 return mFallbackEffects.get(effectId); 283 } 284 285 /** 286 * Return {@code true} if the device should vibrate for current ringer mode. 287 * 288 * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings 289 * for ringtone usage only. All other usages are allowed independently of ringer mode. 290 */ shouldVibrateForRingerMode(int usageHint)291 public boolean shouldVibrateForRingerMode(int usageHint) { 292 if (!isRingtone(usageHint)) { 293 return true; 294 } 295 synchronized (mLock) { 296 if (mAudioManager == null) { 297 return false; 298 } 299 int ringerMode = mAudioManager.getRingerModeInternal(); 300 if (mVibrateWhenRinging) { 301 return ringerMode != AudioManager.RINGER_MODE_SILENT; 302 } else if (mApplyRampingRinger) { 303 return ringerMode != AudioManager.RINGER_MODE_SILENT; 304 } else { 305 return ringerMode == AudioManager.RINGER_MODE_VIBRATE; 306 } 307 } 308 } 309 310 /** 311 * Returns {@code true} if this vibration is allowed for given {@code uid}. 312 * 313 * <p>This checks if the user is aware of this foreground process, or if the vibration usage is 314 * allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration). 315 */ shouldVibrateForUid(int uid, int usageHint)316 public boolean shouldVibrateForUid(int uid, int usageHint) { 317 return mUidObserver.isUidForeground(uid) || isClassAlarm(usageHint); 318 } 319 320 /** 321 * Returns {@code true} if this vibration is allowed for current power mode state. 322 * 323 * <p>This checks if the device is in battery saver mode, in which case only alarm, ringtone and 324 * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate. 325 */ shouldVibrateForPowerMode(int usageHint)326 public boolean shouldVibrateForPowerMode(int usageHint) { 327 return !mLowPowerMode || isRingtone(usageHint) || isAlarm(usageHint) 328 || usageHint == VibrationAttributes.USAGE_COMMUNICATION_REQUEST; 329 } 330 331 /** Return {@code true} if input devices should vibrate instead of this device. */ shouldVibrateInputDevices()332 public boolean shouldVibrateInputDevices() { 333 return mVibrateInputDevices; 334 } 335 336 /** Return {@code true} if setting for {@link Settings.Global#ZEN_MODE} is not OFF. */ isInZenMode()337 public boolean isInZenMode() { 338 return mZenMode != Settings.Global.ZEN_MODE_OFF; 339 } 340 isNotification(int usageHint)341 private static boolean isNotification(int usageHint) { 342 return usageHint == VibrationAttributes.USAGE_NOTIFICATION; 343 } 344 isRingtone(int usageHint)345 private static boolean isRingtone(int usageHint) { 346 return usageHint == VibrationAttributes.USAGE_RINGTONE; 347 } 348 isHapticFeedback(int usageHint)349 private static boolean isHapticFeedback(int usageHint) { 350 return usageHint == VibrationAttributes.USAGE_TOUCH; 351 } 352 isAlarm(int usageHint)353 private static boolean isAlarm(int usageHint) { 354 return usageHint == VibrationAttributes.USAGE_ALARM; 355 } 356 isClassAlarm(int usageHint)357 private static boolean isClassAlarm(int usageHint) { 358 return (usageHint & VibrationAttributes.USAGE_CLASS_MASK) 359 == VibrationAttributes.USAGE_CLASS_ALARM; 360 } 361 362 /** Updates all vibration settings and triggers registered listeners. */ updateSettings()363 public void updateSettings() { 364 synchronized (mLock) { 365 mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0; 366 mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0; 367 mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, 368 getDefaultIntensity(VibrationAttributes.USAGE_TOUCH)); 369 mNotificationIntensity = getSystemSetting( 370 Settings.System.NOTIFICATION_VIBRATION_INTENSITY, 371 getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION)); 372 mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, 373 getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE)); 374 mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0; 375 mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); 376 } 377 notifyListeners(); 378 } 379 380 @Override toString()381 public String toString() { 382 return "VibrationSettings{" 383 + "mVibrateInputDevices=" + mVibrateInputDevices 384 + ", mVibrateWhenRinging=" + mVibrateWhenRinging 385 + ", mApplyRampingRinger=" + mApplyRampingRinger 386 + ", mLowPowerMode=" + mLowPowerMode 387 + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode) 388 + ", mProcStatesCache=" + mUidObserver.mProcStatesCache 389 + ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude() 390 + ", mRampStepDuration=" + mRampStepDuration 391 + ", mRampDownDuration=" + mRampDownDuration 392 + ", mHapticFeedbackIntensity=" 393 + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)) 394 + ", mHapticFeedbackDefaultIntensity=" 395 + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_TOUCH)) 396 + ", mNotificationIntensity=" 397 + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION)) 398 + ", mNotificationDefaultIntensity=" 399 + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION)) 400 + ", mRingIntensity=" 401 + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE)) 402 + ", mRingDefaultIntensity=" 403 + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE)) 404 + '}'; 405 } 406 407 /** Write current settings into given {@link ProtoOutputStream}. */ dumpProto(ProtoOutputStream proto)408 public void dumpProto(ProtoOutputStream proto) { 409 synchronized (mLock) { 410 proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY, 411 mHapticFeedbackIntensity); 412 proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY, 413 getDefaultIntensity(VibrationAttributes.USAGE_TOUCH)); 414 proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY, 415 mNotificationIntensity); 416 proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY, 417 getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION)); 418 proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY, 419 mRingIntensity); 420 proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY, 421 getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE)); 422 } 423 } 424 notifyListeners()425 private void notifyListeners() { 426 List<OnVibratorSettingsChanged> currentListeners; 427 synchronized (mLock) { 428 currentListeners = new ArrayList<>(mListeners); 429 } 430 for (OnVibratorSettingsChanged listener : currentListeners) { 431 listener.onChange(); 432 } 433 } 434 intensityToString(int intensity)435 private static String intensityToString(int intensity) { 436 switch (intensity) { 437 case Vibrator.VIBRATION_INTENSITY_OFF: 438 return "OFF"; 439 case Vibrator.VIBRATION_INTENSITY_LOW: 440 return "LOW"; 441 case Vibrator.VIBRATION_INTENSITY_MEDIUM: 442 return "MEDIUM"; 443 case Vibrator.VIBRATION_INTENSITY_HIGH: 444 return "HIGH"; 445 default: 446 return "UNKNOWN INTENSITY " + intensity; 447 } 448 } 449 getHapticChannelMaxVibrationAmplitude()450 private float getHapticChannelMaxVibrationAmplitude() { 451 synchronized (mLock) { 452 return mVibrator == null ? Float.NaN : mVibrator.getHapticChannelMaximumAmplitude(); 453 } 454 } 455 getSystemSetting(String settingName, int defaultValue)456 private int getSystemSetting(String settingName, int defaultValue) { 457 return Settings.System.getIntForUser(mContext.getContentResolver(), 458 settingName, defaultValue, UserHandle.USER_CURRENT); 459 } 460 getGlobalSetting(String settingName, int defaultValue)461 private int getGlobalSetting(String settingName, int defaultValue) { 462 return Settings.Global.getInt(mContext.getContentResolver(), settingName, defaultValue); 463 } 464 registerSettingsObserver(Uri settingUri)465 private void registerSettingsObserver(Uri settingUri) { 466 mContext.getContentResolver().registerContentObserver( 467 settingUri, /* notifyForDescendants= */ true, mSettingObserver, 468 UserHandle.USER_ALL); 469 } 470 createEffectFromResource(int resId)471 private VibrationEffect createEffectFromResource(int resId) { 472 long[] timings = getLongIntArray(mContext.getResources(), resId); 473 return createEffectFromTimings(timings); 474 } 475 createEffectFromTimings(long[] timings)476 private static VibrationEffect createEffectFromTimings(long[] timings) { 477 if (timings == null || timings.length == 0) { 478 return null; 479 } else if (timings.length == 1) { 480 return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE); 481 } else { 482 return VibrationEffect.createWaveform(timings, -1); 483 } 484 } 485 getLongIntArray(Resources r, int resid)486 private static long[] getLongIntArray(Resources r, int resid) { 487 int[] ar = r.getIntArray(resid); 488 if (ar == null) { 489 return null; 490 } 491 long[] out = new long[ar.length]; 492 for (int i = 0; i < ar.length; i++) { 493 out[i] = ar[i]; 494 } 495 return out; 496 } 497 498 /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ 499 private final class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler)500 SettingsObserver(Handler handler) { 501 super(handler); 502 } 503 504 @Override onChange(boolean selfChange)505 public void onChange(boolean selfChange) { 506 updateSettings(); 507 } 508 } 509 510 /** Implementation of {@link BroadcastReceiver} to update settings on current user change. */ 511 @VisibleForTesting 512 final class UserObserver extends BroadcastReceiver { 513 @Override onReceive(Context context, Intent intent)514 public void onReceive(Context context, Intent intent) { 515 if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 516 updateSettings(); 517 } 518 } 519 } 520 521 /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ 522 @VisibleForTesting 523 final class UidObserver extends IUidObserver.Stub { 524 private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); 525 isUidForeground(int uid)526 public boolean isUidForeground(int uid) { 527 return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) 528 <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; 529 } 530 531 @Override onUidGone(int uid, boolean disabled)532 public void onUidGone(int uid, boolean disabled) { 533 mProcStatesCache.delete(uid); 534 } 535 536 @Override onUidActive(int uid)537 public void onUidActive(int uid) { 538 } 539 540 @Override onUidIdle(int uid, boolean disabled)541 public void onUidIdle(int uid, boolean disabled) { 542 } 543 544 @Override onUidStateChanged(int uid, int procState, long procStateSeq, int capability)545 public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { 546 mProcStatesCache.put(uid, procState); 547 } 548 549 @Override onUidCachedChanged(int uid, boolean cached)550 public void onUidCachedChanged(int uid, boolean cached) { 551 } 552 } 553 } 554