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