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.content.Context; 21 import android.hardware.input.InputManager; 22 import android.os.CombinedVibration; 23 import android.os.Handler; 24 import android.os.VibratorManager; 25 import android.util.SparseArray; 26 import android.view.InputDevice; 27 28 import com.android.internal.annotations.GuardedBy; 29 30 /** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */ 31 final class InputDeviceDelegate implements InputManager.InputDeviceListener { 32 private static final String TAG = "InputDeviceDelegate"; 33 34 private final Object mLock = new Object(); 35 private final Handler mHandler; 36 private final Context mContext; 37 38 @GuardedBy("mLock") 39 @Nullable 40 private InputManager mInputManager; 41 42 @GuardedBy("mLock") 43 private final SparseArray<VibratorManager> mInputDeviceVibrators = new SparseArray<>(); 44 45 /** 46 * Flag updated via {@link #updateInputDeviceVibrators(boolean)}, holding the value of {@link 47 * android.provider.Settings.System#VIBRATE_INPUT_DEVICES}. 48 */ 49 @GuardedBy("mLock") 50 private boolean mShouldVibrateInputDevices; 51 InputDeviceDelegate(Context context, Handler handler)52 InputDeviceDelegate(Context context, Handler handler) { 53 mHandler = handler; 54 mContext = context; 55 } 56 onSystemReady()57 public void onSystemReady() { 58 synchronized (mLock) { 59 mInputManager = mContext.getSystemService(InputManager.class); 60 } 61 } 62 63 @Override onInputDeviceAdded(int deviceId)64 public void onInputDeviceAdded(int deviceId) { 65 updateInputDevice(deviceId); 66 } 67 68 @Override onInputDeviceChanged(int deviceId)69 public void onInputDeviceChanged(int deviceId) { 70 updateInputDevice(deviceId); 71 } 72 73 @Override onInputDeviceRemoved(int deviceId)74 public void onInputDeviceRemoved(int deviceId) { 75 synchronized (mLock) { 76 mInputDeviceVibrators.remove(deviceId); 77 } 78 } 79 80 /** 81 * Return {@code true} is there are input devices with vibrators available and vibrations should 82 * be delegated to them. 83 */ isAvailable()84 public boolean isAvailable() { 85 synchronized (mLock) { 86 // mInputDeviceVibrators is cleared when settings are disabled, so this check is enough. 87 return mInputDeviceVibrators.size() > 0; 88 } 89 } 90 91 /** 92 * Vibrate all {@link InputDevice} with vibrators using given effect. 93 * 94 * @return {@link #isAvailable()} 95 */ vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect)96 public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) { 97 synchronized (mLock) { 98 for (int i = 0; i < mInputDeviceVibrators.size(); i++) { 99 mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect, 100 callerInfo.reason, callerInfo.attrs); 101 } 102 return mInputDeviceVibrators.size() > 0; 103 } 104 } 105 106 /** 107 * Cancel vibration on all {@link InputDevice} with vibrators. 108 * 109 * @return {@link #isAvailable()} 110 */ cancelVibrateIfAvailable()111 public boolean cancelVibrateIfAvailable() { 112 synchronized (mLock) { 113 for (int i = 0; i < mInputDeviceVibrators.size(); i++) { 114 mInputDeviceVibrators.valueAt(i).cancel(); 115 } 116 return mInputDeviceVibrators.size() > 0; 117 } 118 } 119 120 /** 121 * Updates the list of {@link InputDevice} vibrators based on the {@link 122 * VibrationSettings#shouldVibrateInputDevices()} setting current value and the 123 * devices currently available in {@link InputManager#getInputDeviceIds()}. 124 * 125 * @return true if there was any change in input devices available or related settings. 126 */ updateInputDeviceVibrators(boolean vibrateInputDevices)127 public boolean updateInputDeviceVibrators(boolean vibrateInputDevices) { 128 synchronized (mLock) { 129 if (mInputManager == null) { 130 // Ignore update, service not loaded yet so change cannot be applied. 131 return false; 132 } 133 if (vibrateInputDevices == mShouldVibrateInputDevices) { 134 // No need to update if settings haven't changed. 135 return false; 136 } 137 138 mShouldVibrateInputDevices = vibrateInputDevices; 139 mInputDeviceVibrators.clear(); 140 141 if (vibrateInputDevices) { 142 // Register the listener first so any device added/updated/removed after the call to 143 // getInputDeviceIds() will trigger the callbacks (which will wait on the lock for 144 // this loop to finish). 145 mInputManager.registerInputDeviceListener(this, mHandler); 146 147 for (int deviceId : mInputManager.getInputDeviceIds()) { 148 InputDevice device = mInputManager.getInputDevice(deviceId); 149 if (device == null) { 150 continue; 151 } 152 VibratorManager vibratorManager = device.getVibratorManager(); 153 if (vibratorManager.getVibratorIds().length > 0) { 154 mInputDeviceVibrators.put(device.getId(), vibratorManager); 155 } 156 } 157 } else { 158 mInputManager.unregisterInputDeviceListener(this); 159 } 160 } 161 162 return true; 163 } 164 updateInputDevice(int deviceId)165 private void updateInputDevice(int deviceId) { 166 synchronized (mLock) { 167 if (mInputManager == null) { 168 // Ignore update, service not loaded yet so change cannot be applied. 169 return; 170 } 171 if (!mShouldVibrateInputDevices) { 172 // No need to keep this device vibrator if setting is off. 173 return; 174 } 175 InputDevice device = mInputManager.getInputDevice(deviceId); 176 if (device == null) { 177 mInputDeviceVibrators.remove(deviceId); 178 return; 179 } 180 VibratorManager vibratorManager = device.getVibratorManager(); 181 if (vibratorManager.getVibratorIds().length > 0) { 182 mInputDeviceVibrators.put(device.getId(), vibratorManager); 183 } else { 184 mInputDeviceVibrators.remove(deviceId); 185 } 186 } 187 } 188 } 189