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 com.android.server.sensorprivacy; 18 19 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; 20 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; 21 import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE; 22 import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE; 23 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertFalse; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assert.assertTrue; 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.anyInt; 33 import static org.mockito.ArgumentMatchers.eq; 34 import static org.mockito.Mockito.mock; 35 import static org.mockito.Mockito.times; 36 37 import android.content.Context; 38 import android.hardware.SensorPrivacyManager; 39 import android.os.Environment; 40 import android.os.Handler; 41 import android.testing.AndroidTestingRunner; 42 43 import androidx.test.platform.app.InstrumentationRegistry; 44 45 import com.android.dx.mockito.inline.extended.ExtendedMockito; 46 import com.android.server.LocalServices; 47 import com.android.server.pm.UserManagerInternal; 48 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.ArgumentCaptor; 53 import org.mockito.MockitoSession; 54 import org.mockito.quality.Strictness; 55 56 import java.io.File; 57 import java.io.IOException; 58 import java.nio.file.Files; 59 import java.nio.file.StandardCopyOption; 60 61 @RunWith(AndroidTestingRunner.class) 62 public class SensorPrivacyServiceMockingTest { 63 64 private static final String PERSISTENCE_FILE_PATHS_TEMPLATE = 65 "SensorPrivacyServiceMockingTest/persisted_file%d.xml"; 66 public static final String PERSISTENCE_FILE1 = 67 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 1); 68 public static final String PERSISTENCE_FILE2 = 69 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 2); 70 public static final String PERSISTENCE_FILE3 = 71 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 3); 72 public static final String PERSISTENCE_FILE4 = 73 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 4); 74 public static final String PERSISTENCE_FILE5 = 75 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5); 76 public static final String PERSISTENCE_FILE6 = 77 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6); 78 public static final String PERSISTENCE_FILE7 = 79 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 7); 80 public static final String PERSISTENCE_FILE8 = 81 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 8); 82 83 public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE = 84 "SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml"; 85 public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE = 86 "SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml"; 87 public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE = 88 "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml"; 89 public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE = 90 "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml"; 91 92 Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); 93 String mDataDir = mContext.getApplicationInfo().dataDir; 94 95 @Before setUp()96 public void setUp() { 97 new File(mDataDir, "sensor_privacy.xml").delete(); 98 new File(mDataDir, "sensor_privacy_impl.xml").delete(); 99 } 100 101 @Test testMigration1()102 public void testMigration1() throws IOException { 103 PersistedState ps = migrateFromFile(PERSISTENCE_FILE1); 104 105 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled()); 106 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled()); 107 108 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE)); 109 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA)); 110 111 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 112 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 113 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 114 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 115 } 116 117 @Test testMigration2()118 public void testMigration2() throws IOException { 119 PersistedState ps = migrateFromFile(PERSISTENCE_FILE2); 120 121 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled()); 122 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled()); 123 124 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled()); 125 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled()); 126 127 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, MICROPHONE)); 128 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, CAMERA)); 129 130 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, MICROPHONE).isEnabled()); 131 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, CAMERA)); 132 133 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 134 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 135 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 136 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 137 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, MICROPHONE)); 138 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, CAMERA)); 139 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, MICROPHONE)); 140 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, CAMERA)); 141 } 142 143 @Test testMigration3()144 public void testMigration3() throws IOException { 145 PersistedState ps = migrateFromFile(PERSISTENCE_FILE3); 146 147 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled()); 148 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled()); 149 150 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE)); 151 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA)); 152 153 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 154 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 155 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 156 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 157 } 158 159 @Test testMigration4()160 public void testMigration4() throws IOException { 161 PersistedState ps = migrateFromFile(PERSISTENCE_FILE4); 162 163 assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled()); 164 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled()); 165 166 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled()); 167 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA)); 168 169 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 170 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 171 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 172 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 173 } 174 175 @Test testMigration5()176 public void testMigration5() throws IOException { 177 PersistedState ps = migrateFromFile(PERSISTENCE_FILE5); 178 179 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE)); 180 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled()); 181 182 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE)); 183 assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled()); 184 185 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 186 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 187 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 188 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 189 } 190 191 @Test testMigration6()192 public void testMigration6() throws IOException { 193 PersistedState ps = migrateFromFile(PERSISTENCE_FILE6); 194 195 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE)); 196 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA)); 197 198 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE)); 199 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA)); 200 201 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 202 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 203 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 204 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 205 } 206 migrateFromFile(String fileName)207 private PersistedState migrateFromFile(String fileName) throws IOException { 208 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 209 .initMocks(this) 210 .strictness(Strictness.WARN) 211 .spyStatic(LocalServices.class) 212 .spyStatic(Environment.class) 213 .startMocking(); 214 try { 215 doReturn(new File(mDataDir)).when(() -> Environment.getDataSystemDirectory()); 216 217 UserManagerInternal umi = mock(UserManagerInternal.class); 218 doReturn(umi).when(() -> LocalServices.getService(UserManagerInternal.class)); 219 doReturn(new int[] {0}).when(umi).getUserIds(); 220 221 Files.copy( 222 mContext.getAssets().open(fileName), 223 new File(mDataDir, "sensor_privacy.xml").toPath(), 224 StandardCopyOption.REPLACE_EXISTING); 225 226 return PersistedState.fromFile("sensor_privacy_impl.xml"); 227 } finally { 228 mockitoSession.finishMocking(); 229 } 230 } 231 232 @Test testPersistence1Version2()233 public void testPersistence1Version2() throws IOException { 234 PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE7); 235 236 assertEquals(1, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getState()); 237 assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getLastChange()); 238 assertEquals(2, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getState()); 239 assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getLastChange()); 240 241 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE)); 242 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA)); 243 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE)); 244 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA)); 245 } 246 247 @Test testPersistence2Version2()248 public void testPersistence2Version2() throws IOException { 249 PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE8); 250 251 assertEquals(1, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getState()); 252 assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getLastChange()); 253 assertEquals(2, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getState()); 254 assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getLastChange()); 255 256 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE)); 257 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA)); 258 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE)); 259 assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA)); 260 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE)); 261 assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA)); 262 } 263 getPersistedStateV2(String version2FilePath)264 private PersistedState getPersistedStateV2(String version2FilePath) throws IOException { 265 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 266 .initMocks(this) 267 .strictness(Strictness.WARN) 268 .spyStatic(LocalServices.class) 269 .spyStatic(Environment.class) 270 .startMocking(); 271 try { 272 doReturn(new File(mDataDir)).when(() -> Environment.getDataSystemDirectory()); 273 Files.copy( 274 mContext.getAssets().open(version2FilePath), 275 new File(mDataDir, "sensor_privacy_impl.xml").toPath(), 276 StandardCopyOption.REPLACE_EXISTING); 277 278 return PersistedState.fromFile("sensor_privacy_impl.xml"); 279 } finally { 280 mockitoSession.finishMocking(); 281 } 282 } 283 284 @Test testGetDefaultState()285 public void testGetDefaultState() { 286 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 287 .initMocks(this) 288 .strictness(Strictness.WARN) 289 .spyStatic(PersistedState.class) 290 .startMocking(); 291 try { 292 PersistedState persistedState = mock(PersistedState.class); 293 doReturn(persistedState).when(() -> PersistedState.fromFile(any())); 294 doReturn(null).when(persistedState).getState(anyInt(), anyInt(), anyInt()); 295 296 SensorPrivacyStateController sensorPrivacyStateController = 297 getSensorPrivacyStateControllerImpl(); 298 299 SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0, 300 MICROPHONE); 301 SensorState camState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0, 302 CAMERA); 303 304 assertEquals(SensorPrivacyManager.StateTypes.DISABLED, micState.getState()); 305 assertEquals(SensorPrivacyManager.StateTypes.DISABLED, camState.getState()); 306 verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE); 307 verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA); 308 } finally { 309 mockitoSession.finishMocking(); 310 } 311 } 312 313 @Test testGetSetState()314 public void testGetSetState() { 315 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 316 .initMocks(this) 317 .strictness(Strictness.WARN) 318 .spyStatic(PersistedState.class) 319 .startMocking(); 320 try { 321 PersistedState persistedState = mock(PersistedState.class); 322 SensorState sensorState = mock(SensorState.class); 323 doReturn(persistedState).when(() -> PersistedState.fromFile(any())); 324 doReturn(sensorState).when(persistedState).getState(TOGGLE_TYPE_SOFTWARE, 0, 325 MICROPHONE); 326 doReturn(SensorPrivacyManager.StateTypes.ENABLED).when(sensorState).getState(); 327 doReturn(0L).when(sensorState).getLastChange(); 328 329 SensorPrivacyStateController sensorPrivacyStateController = 330 getSensorPrivacyStateControllerImpl(); 331 332 SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0, 333 MICROPHONE); 334 335 assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState()); 336 assertEquals(0L, micState.getLastChange()); 337 } finally { 338 mockitoSession.finishMocking(); 339 } 340 } 341 342 @Test testSetState()343 public void testSetState() { 344 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 345 .initMocks(this) 346 .strictness(Strictness.WARN) 347 .spyStatic(PersistedState.class) 348 .startMocking(); 349 try { 350 PersistedState persistedState = mock(PersistedState.class); 351 doReturn(persistedState).when(() -> PersistedState.fromFile(any())); 352 353 SensorPrivacyStateController sensorPrivacyStateController = 354 getSensorPrivacyStateControllerImpl(); 355 356 sensorPrivacyStateController.setState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE, true, 357 mock(Handler.class), changed -> {}); 358 359 ArgumentCaptor<SensorState> captor = ArgumentCaptor.forClass(SensorState.class); 360 361 verify(persistedState, times(1)).setState(eq(TOGGLE_TYPE_SOFTWARE), eq(0), 362 eq(MICROPHONE), captor.capture()); 363 assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState()); 364 } finally { 365 mockitoSession.finishMocking(); 366 } 367 } 368 getSensorPrivacyStateControllerImpl()369 private SensorPrivacyStateController getSensorPrivacyStateControllerImpl() { 370 SensorPrivacyStateControllerImpl.getInstance().resetForTestingImpl(); 371 return SensorPrivacyStateControllerImpl.getInstance(); 372 } 373 } 374