1 /* 2 * Copyright (C) 2018 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.systemui.statusbar; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.media.AudioAttributes; 22 import android.os.Process; 23 import android.os.VibrationAttributes; 24 import android.os.VibrationEffect; 25 import android.os.Vibrator; 26 27 import androidx.annotation.VisibleForTesting; 28 29 import com.android.systemui.dagger.SysUISingleton; 30 31 import org.jetbrains.annotations.NotNull; 32 33 import java.util.concurrent.Executor; 34 import java.util.concurrent.Executors; 35 36 import javax.inject.Inject; 37 38 /** 39 * A Helper class that offloads {@link Vibrator} calls to a different thread. 40 * {@link Vibrator} makes blocking calls that may cause SysUI to ANR. 41 * TODO(b/245528624): Use regular Vibrator instance once new APIs are available. 42 */ 43 @SysUISingleton 44 public class VibratorHelper { 45 46 private final Vibrator mVibrator; 47 public static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = 48 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); 49 50 private static final VibrationEffect BIOMETRIC_SUCCESS_VIBRATION_EFFECT = 51 VibrationEffect.get(VibrationEffect.EFFECT_CLICK); 52 private static final VibrationEffect BIOMETRIC_ERROR_VIBRATION_EFFECT = 53 VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); 54 private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = 55 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); 56 57 private final Executor mExecutor; 58 59 /** 60 * Creates a vibrator helper on a new single threaded {@link Executor}. 61 */ 62 @Inject VibratorHelper(@ullable Vibrator vibrator)63 public VibratorHelper(@Nullable Vibrator vibrator) { 64 this(vibrator, Executors.newSingleThreadExecutor()); 65 } 66 67 /** 68 * Creates new vibrator helper on a specific {@link Executor}. 69 */ 70 @VisibleForTesting VibratorHelper(@ullable Vibrator vibrator, Executor executor)71 public VibratorHelper(@Nullable Vibrator vibrator, Executor executor) { 72 mExecutor = executor; 73 mVibrator = vibrator; 74 } 75 76 /** 77 * @see Vibrator#vibrate(long) 78 */ vibrate(final int effectId)79 public void vibrate(final int effectId) { 80 if (!hasVibrator()) { 81 return; 82 } 83 mExecutor.execute(() -> 84 mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */), 85 TOUCH_VIBRATION_ATTRIBUTES)); 86 } 87 88 /** 89 * @see Vibrator#vibrate(int, String, VibrationEffect, String, VibrationAttributes) 90 */ vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason, @NonNull VibrationAttributes attributes)91 public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, 92 String reason, @NonNull VibrationAttributes attributes) { 93 if (!hasVibrator()) { 94 return; 95 } 96 mExecutor.execute(() -> mVibrator.vibrate(uid, opPkg, vibe, reason, attributes)); 97 } 98 99 /** 100 * @see Vibrator#vibrate(VibrationEffect, AudioAttributes) 101 */ vibrate(@onNull VibrationEffect effect, @NonNull AudioAttributes attributes)102 public void vibrate(@NonNull VibrationEffect effect, @NonNull AudioAttributes attributes) { 103 if (!hasVibrator()) { 104 return; 105 } 106 mExecutor.execute(() -> mVibrator.vibrate(effect, attributes)); 107 } 108 109 /** 110 * @see Vibrator#vibrate(VibrationEffect) 111 */ vibrate(@otNull VibrationEffect effect)112 public void vibrate(@NotNull VibrationEffect effect) { 113 if (!hasVibrator()) { 114 return; 115 } 116 mExecutor.execute(() -> mVibrator.vibrate(effect)); 117 } 118 119 /** 120 * @see Vibrator#hasVibrator() 121 */ hasVibrator()122 public boolean hasVibrator() { 123 return mVibrator != null && mVibrator.hasVibrator(); 124 } 125 126 /** 127 * @see Vibrator#cancel() 128 */ cancel()129 public void cancel() { 130 if (!hasVibrator()) { 131 return; 132 } 133 mExecutor.execute(mVibrator::cancel); 134 } 135 136 /** 137 * Perform vibration when biometric authentication success 138 */ vibrateAuthSuccess(String reason)139 public void vibrateAuthSuccess(String reason) { 140 vibrate(Process.myUid(), 141 "com.android.systemui", 142 BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason, 143 HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); 144 } 145 146 /** 147 * Perform vibration when biometric authentication error 148 */ vibrateAuthError(String reason)149 public void vibrateAuthError(String reason) { 150 vibrate(Process.myUid(), "com.android.systemui", 151 BIOMETRIC_ERROR_VIBRATION_EFFECT, reason, 152 HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); 153 } 154 } 155