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