1 /* 2 * Copyright (C) 2021 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 android.os.cts; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.junit.Assume.assumeTrue; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.clearInvocations; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.timeout; 27 import static org.mockito.Mockito.verify; 28 29 import android.content.Context; 30 import android.os.CombinedVibration; 31 import android.os.SystemClock; 32 import android.os.VibrationAttributes; 33 import android.os.VibrationEffect; 34 import android.os.Vibrator; 35 import android.os.Vibrator.OnVibratorStateChangedListener; 36 import android.os.VibratorManager; 37 import android.provider.Settings; 38 import android.util.SparseArray; 39 40 import androidx.test.filters.LargeTest; 41 import androidx.test.platform.app.InstrumentationRegistry; 42 import androidx.test.runner.AndroidJUnit4; 43 44 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 45 46 import org.junit.After; 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.junit.MockitoJUnit; 52 import org.mockito.junit.MockitoRule; 53 54 import java.util.Arrays; 55 56 @RunWith(AndroidJUnit4.class) 57 public class VibratorManagerTest { 58 59 @Rule 60 public final AdoptShellPermissionsRule mAdoptShellPermissionsRule = 61 new AdoptShellPermissionsRule( 62 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 63 android.Manifest.permission.ACCESS_VIBRATOR_STATE, 64 android.Manifest.permission.WRITE_SETTINGS); 65 66 @Rule 67 public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 68 69 private static final long CALLBACK_TIMEOUT_MILLIS = 5_000; 70 private static final VibrationAttributes VIBRATION_ATTRIBUTES = 71 new VibrationAttributes.Builder() 72 .setUsage(VibrationAttributes.USAGE_TOUCH) 73 .build(); 74 75 /** 76 * These listeners are used for test helper methods like asserting it starts/stops vibrating. 77 * It's not strongly required that the interactions with these mocks are validated by all tests. 78 */ 79 private final SparseArray<OnVibratorStateChangedListener> mStateListeners = new SparseArray<>(); 80 81 private VibratorManager mVibratorManager; 82 83 @Before setUp()84 public void setUp() { 85 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 86 Settings.System.putInt(context.getContentResolver(), Settings.System.VIBRATE_ON, 1); 87 88 mVibratorManager = context.getSystemService(VibratorManager.class); 89 for (int vibratorId : mVibratorManager.getVibratorIds()) { 90 OnVibratorStateChangedListener listener = mock(OnVibratorStateChangedListener.class); 91 mVibratorManager.getVibrator(vibratorId).addVibratorStateListener(listener); 92 mStateListeners.put(vibratorId, listener); 93 // Adding a listener to the Vibrator should trigger the callback once with the current 94 // vibrator state, so reset mocks to clear it for tests. 95 assertVibratorStateChangesTo(vibratorId, false); 96 clearInvocations(listener); 97 } 98 } 99 100 @After cleanUp()101 public void cleanUp() { 102 // Clearing invocations so we can use these listeners to wait for the vibrator to 103 // asynchronously cancel the ongoing vibration, if any was left pending by a test. 104 for (int i = 0; i < mStateListeners.size(); i++) { 105 clearInvocations(mStateListeners.valueAt(i)); 106 } 107 mVibratorManager.cancel(); 108 109 for (int i = 0; i < mStateListeners.size(); i++) { 110 int vibratorId = mStateListeners.keyAt(i); 111 112 // Wait for cancel to take effect, if device is still vibrating. 113 if (mVibratorManager.getVibrator(vibratorId).isVibrating()) { 114 assertStopsVibrating(vibratorId); 115 } 116 117 // Remove all listeners added by the tests. 118 mVibratorManager.getVibrator(vibratorId).removeVibratorStateListener( 119 mStateListeners.valueAt(i)); 120 } 121 } 122 123 @Test testGetVibratorIds()124 public void testGetVibratorIds() { 125 // Just make sure it doesn't crash or return null when this is called; we don't really have 126 // a way to test which vibrators will be returned. 127 assertThat(mVibratorManager.getVibratorIds()).isNotNull(); 128 assertThat(mVibratorManager.getVibratorIds()).asList().containsNoDuplicates(); 129 } 130 131 @Test testGetNonExistentVibratorId()132 public void testGetNonExistentVibratorId() { 133 int missingId = Arrays.stream(mVibratorManager.getVibratorIds()).max().orElse(0) + 1; 134 Vibrator vibrator = mVibratorManager.getVibrator(missingId); 135 assertThat(vibrator).isNotNull(); 136 assertThat(vibrator.hasVibrator()).isFalse(); 137 } 138 139 @Test testGetDefaultVibratorIsSameAsVibratorService()140 public void testGetDefaultVibratorIsSameAsVibratorService() { 141 // Note that VibratorTest parameterization relies on these two vibrators being identical. 142 // It only runs vibrator tests on the result of one of the APIs. 143 Vibrator systemVibrator = 144 InstrumentationRegistry.getInstrumentation().getContext().getSystemService( 145 Vibrator.class); 146 assertThat(mVibratorManager.getDefaultVibrator()).isSameInstanceAs(systemVibrator); 147 } 148 149 @Test testCancel()150 public void testCancel() { 151 mVibratorManager.vibrate(CombinedVibration.createParallel( 152 VibrationEffect.createOneShot(10_000, VibrationEffect.DEFAULT_AMPLITUDE))); 153 assertStartsVibrating(); 154 155 mVibratorManager.cancel(); 156 assertStopsVibrating(); 157 } 158 159 @LargeTest 160 @Test testCombinedVibrationOneShotStartsAndFinishesVibration()161 public void testCombinedVibrationOneShotStartsAndFinishesVibration() { 162 VibrationEffect oneShot = 163 VibrationEffect.createOneShot(300, VibrationEffect.DEFAULT_AMPLITUDE); 164 mVibratorManager.vibrate(CombinedVibration.createParallel(oneShot)); 165 assertStartsThenStopsVibrating(300); 166 } 167 168 @Test testCombinedVibrationOneShotMaxAmplitude()169 public void testCombinedVibrationOneShotMaxAmplitude() { 170 VibrationEffect oneShot = VibrationEffect.createOneShot(500, 255 /* Max amplitude */); 171 mVibratorManager.vibrate(CombinedVibration.createParallel(oneShot)); 172 assertStartsVibrating(); 173 174 mVibratorManager.cancel(); 175 assertStopsVibrating(); 176 } 177 178 @Test testCombinedVibrationOneShotMinAmplitude()179 public void testCombinedVibrationOneShotMinAmplitude() { 180 VibrationEffect oneShot = VibrationEffect.createOneShot(100, 1 /* Min amplitude */); 181 mVibratorManager.vibrate(CombinedVibration.createParallel(oneShot), 182 VIBRATION_ATTRIBUTES); 183 assertStartsVibrating(); 184 } 185 186 @LargeTest 187 @Test testCombinedVibrationWaveformStartsAndFinishesVibration()188 public void testCombinedVibrationWaveformStartsAndFinishesVibration() { 189 final long[] timings = new long[]{100, 200, 300, 400, 500}; 190 final int[] amplitudes = new int[]{64, 128, 255, 128, 64}; 191 VibrationEffect waveform = VibrationEffect.createWaveform(timings, amplitudes, -1); 192 mVibratorManager.vibrate(CombinedVibration.createParallel(waveform)); 193 assertStartsThenStopsVibrating(1500); 194 } 195 196 @LargeTest 197 @Test testCombinedVibrationWaveformRepeats()198 public void testCombinedVibrationWaveformRepeats() { 199 final long[] timings = new long[]{100, 200, 300, 400, 500}; 200 final int[] amplitudes = new int[]{64, 128, 255, 128, 64}; 201 VibrationEffect waveform = VibrationEffect.createWaveform(timings, amplitudes, 0); 202 mVibratorManager.vibrate(CombinedVibration.createParallel(waveform)); 203 assertStartsVibrating(); 204 205 SystemClock.sleep(2000); 206 int[] vibratorIds = mVibratorManager.getVibratorIds(); 207 for (int vibratorId : vibratorIds) { 208 assertWithMessage( 209 "Expected repeating parallel waveform to continue vibrating on vibrator %s" 210 + " after initial duration", vibratorId) 211 .that(mVibratorManager.getVibrator(vibratorId).isVibrating()).isTrue(); 212 } 213 214 mVibratorManager.cancel(); 215 assertStopsVibrating(); 216 } 217 218 @Test testCombinedVibrationTargetingSingleVibrator()219 public void testCombinedVibrationTargetingSingleVibrator() { 220 int[] vibratorIds = mVibratorManager.getVibratorIds(); 221 assumeTrue(vibratorIds.length >= 2); 222 223 VibrationEffect oneShot = 224 VibrationEffect.createOneShot(10_000, VibrationEffect.DEFAULT_AMPLITUDE); 225 226 // Vibrate each vibrator in turn, and assert that all the others are off. 227 for (int vibratorId : vibratorIds) { 228 Vibrator vibrator = mVibratorManager.getVibrator(vibratorId); 229 mVibratorManager.vibrate( 230 CombinedVibration.startParallel() 231 .addVibrator(vibratorId, oneShot) 232 .combine()); 233 assertStartsVibrating(vibratorId); 234 235 for (int otherVibratorId : vibratorIds) { 236 if (otherVibratorId != vibratorId) { 237 assertWithMessage( 238 "Expected vibrator %s not vibrating when combined vibration started" 239 + " on vibrator %s", otherVibratorId, vibratorId) 240 .that(mVibratorManager.getVibrator(otherVibratorId).isVibrating()) 241 .isFalse(); 242 } 243 } 244 245 vibrator.cancel(); 246 assertStopsVibrating(vibratorId); 247 } 248 } 249 assertStartsThenStopsVibrating(long duration)250 private void assertStartsThenStopsVibrating(long duration) { 251 for (int i = 0; i < mStateListeners.size(); i++) { 252 assertStartsVibrating(mStateListeners.keyAt(i)); 253 } 254 SystemClock.sleep(duration); 255 assertStopsVibrating(); 256 } 257 assertStartsVibrating()258 private void assertStartsVibrating() { 259 assertVibratorStateChangesTo(true); 260 } 261 assertStartsVibrating(int vibratorId)262 private void assertStartsVibrating(int vibratorId) { 263 assertVibratorStateChangesTo(vibratorId, true); 264 } 265 assertStopsVibrating()266 private void assertStopsVibrating() { 267 assertVibratorStateChangesTo(false); 268 } 269 assertStopsVibrating(int vibratorId)270 private void assertStopsVibrating(int vibratorId) { 271 assertVibratorStateChangesTo(vibratorId, false); 272 } 273 assertVibratorStateChangesTo(boolean expected)274 private void assertVibratorStateChangesTo(boolean expected) { 275 for (int i = 0; i < mStateListeners.size(); i++) { 276 assertVibratorStateChangesTo(mStateListeners.keyAt(i), expected); 277 } 278 } 279 assertVibratorStateChangesTo(int vibratorId, boolean expected)280 private void assertVibratorStateChangesTo(int vibratorId, boolean expected) { 281 OnVibratorStateChangedListener listener = mStateListeners.get(vibratorId); 282 verify(listener, 283 timeout(CALLBACK_TIMEOUT_MILLIS).atLeastOnce().description( 284 "Vibrator " + vibratorId + " expected to turn " 285 + (expected ? "on" : "off"))) 286 .onVibratorStateChanged(eq(expected)); 287 } 288 } 289