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