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.power; 18 19 import static com.android.server.power.ShutdownThread.DEFAULT_SHUTDOWN_VIBRATE_MS; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.content.Context; 29 import android.os.VibrationAttributes; 30 import android.os.VibrationEffect; 31 import android.os.Vibrator; 32 import android.os.VibratorInfo; 33 import android.util.AtomicFile; 34 35 import androidx.test.InstrumentationRegistry; 36 37 import org.junit.Before; 38 import org.junit.Test; 39 import org.mockito.Mock; 40 import org.mockito.MockitoAnnotations; 41 42 import java.io.File; 43 import java.io.FileOutputStream; 44 45 /** 46 * Tests for {@link com.android.server.power.ShutdownThread} 47 */ 48 public class ShutdownThreadTest { 49 50 private static final String WAVEFORM_VIB_10MS_SERIALIZATION = 51 """ 52 <vibration-effect> 53 <waveform-effect> 54 <waveform-entry durationMs="10" amplitude="100"/> 55 </waveform-effect> 56 </vibration-effect> 57 """; 58 59 private static final VibrationEffect WAVEFORM_VIB_10MS = VibrationEffect.createOneShot(10, 100); 60 61 private static final String REPEATING_VIB_SERIALIZATION = 62 """ 63 <vibration-effect> 64 <waveform-effect> 65 <repeating> 66 <waveform-entry durationMs="10" amplitude="100"/> 67 </repeating> 68 </waveform-effect> 69 </vibration-effect> 70 """; 71 72 private static final String CLICK_VIB_SERIALIZATION = 73 """ 74 <vibration-effect> 75 <predefined-effect name="click"/> 76 </vibration-effect> 77 """; 78 79 private static final VibrationEffect CLILCK_VIB = 80 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); 81 82 private static final String BAD_VIB_SERIALIZATION = "BAD SERIALIZATION"; 83 84 @Mock private Context mContextMock; 85 @Mock private Vibrator mVibratorMock; 86 @Mock private VibratorInfo mVibratorInfoMock; 87 88 private String mDefaultShutdownVibrationFilePath; 89 private boolean mShutdownVibrationDisabled; 90 private long mLastSleepDurationMs; 91 92 private ShutdownThread mShutdownThread; 93 94 @Before setUp()95 public void setUp() { 96 MockitoAnnotations.initMocks(this); 97 when(mVibratorMock.hasVibrator()).thenReturn(true); 98 when(mVibratorMock.getInfo()).thenReturn(mVibratorInfoMock); 99 100 when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(true); 101 102 mShutdownThread = new ShutdownThread(new TestInjector()); 103 } 104 105 @Test testSuccessfulShutdownVibrationFromFile()106 public void testSuccessfulShutdownVibrationFromFile() throws Exception { 107 setShutdownVibrationFileContent(WAVEFORM_VIB_10MS_SERIALIZATION); 108 109 mShutdownThread.playShutdownVibration(mContextMock); 110 111 assertShutdownVibration(WAVEFORM_VIB_10MS, /* vibrationSleepDuration= */ 10); 112 } 113 114 @Test testIOExceptionWhenParsingShutdownVibration()115 public void testIOExceptionWhenParsingShutdownVibration() throws Exception { 116 mDefaultShutdownVibrationFilePath = "non/existent/file_path"; 117 118 mShutdownThread.playShutdownVibration(mContextMock); 119 120 assertDefaultShutdownVibration(); 121 } 122 123 @Test testMalformedShutdownVibrationFileContent()124 public void testMalformedShutdownVibrationFileContent() throws Exception { 125 setShutdownVibrationFileContent(BAD_VIB_SERIALIZATION); 126 127 mShutdownThread.playShutdownVibration(mContextMock); 128 129 assertDefaultShutdownVibration(); 130 } 131 132 @Test testVibratorUnsupportedShutdownVibrationEffect()133 public void testVibratorUnsupportedShutdownVibrationEffect() throws Exception { 134 setShutdownVibrationFileContent(WAVEFORM_VIB_10MS_SERIALIZATION); 135 when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(false); 136 137 mShutdownThread.playShutdownVibration(mContextMock); 138 139 assertDefaultShutdownVibration(); 140 } 141 142 @Test testRepeatinghutdownVibrationEffect()143 public void testRepeatinghutdownVibrationEffect() throws Exception { 144 setShutdownVibrationFileContent(REPEATING_VIB_SERIALIZATION); 145 146 mShutdownThread.playShutdownVibration(mContextMock); 147 148 assertDefaultShutdownVibration(); 149 } 150 151 @Test testVibrationEffectWithUnknownDuration()152 public void testVibrationEffectWithUnknownDuration() throws Exception { 153 setShutdownVibrationFileContent(CLICK_VIB_SERIALIZATION); 154 155 mShutdownThread.playShutdownVibration(mContextMock); 156 157 assertShutdownVibration(CLILCK_VIB, DEFAULT_SHUTDOWN_VIBRATE_MS); 158 } 159 160 @Test testNoVibrator()161 public void testNoVibrator() { 162 when(mVibratorMock.hasVibrator()).thenReturn(false); 163 164 mShutdownThread.playShutdownVibration(mContextMock); 165 166 verify(mVibratorMock, never()) 167 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class)); 168 } 169 170 @Test testVibrationDisabled()171 public void testVibrationDisabled() throws Exception { 172 setShutdownVibrationFileContent(CLICK_VIB_SERIALIZATION); 173 mShutdownVibrationDisabled = true; 174 175 mShutdownThread.playShutdownVibration(mContextMock); 176 177 verify(mVibratorMock, never()) 178 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class)); 179 } 180 assertShutdownVibration(VibrationEffect effect, long vibrationSleepDuration)181 private void assertShutdownVibration(VibrationEffect effect, long vibrationSleepDuration) 182 throws Exception { 183 verify(mVibratorMock).vibrate( 184 eq(effect), 185 eq(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH))); 186 assertEquals(vibrationSleepDuration, mLastSleepDurationMs); 187 } 188 assertDefaultShutdownVibration()189 private void assertDefaultShutdownVibration() throws Exception { 190 assertShutdownVibration( 191 VibrationEffect.createOneShot( 192 DEFAULT_SHUTDOWN_VIBRATE_MS, VibrationEffect.DEFAULT_AMPLITUDE), 193 DEFAULT_SHUTDOWN_VIBRATE_MS); 194 } 195 setShutdownVibrationFileContent(String content)196 private void setShutdownVibrationFileContent(String content) throws Exception { 197 mDefaultShutdownVibrationFilePath = createFileForContent(content).getAbsolutePath(); 198 } 199 createFileForContent(String content)200 private static File createFileForContent(String content) throws Exception { 201 File file = new File(InstrumentationRegistry.getContext().getCacheDir(), "test.xml"); 202 file.createNewFile(); 203 204 AtomicFile atomicFile = new AtomicFile(file); 205 FileOutputStream fos = atomicFile.startWrite(); 206 fos.write(content.getBytes()); 207 atomicFile.finishWrite(fos); 208 209 return file; 210 } 211 212 private class TestInjector extends ShutdownThread.Injector { 213 @Override getVibrator(Context context)214 public Vibrator getVibrator(Context context) { 215 return mVibratorMock; 216 } 217 218 @Override getDefaultShutdownVibrationEffectFilePath(Context context)219 public String getDefaultShutdownVibrationEffectFilePath(Context context) { 220 return mDefaultShutdownVibrationFilePath; 221 } 222 223 @Override sleep(long durationMs)224 public void sleep(long durationMs) { 225 mLastSleepDurationMs = durationMs; 226 } 227 228 @Override isShutdownVibrationDisabled(Context context)229 public boolean isShutdownVibrationDisabled(Context context) { 230 return mShutdownVibrationDisabled; 231 } 232 } 233 } 234