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 com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyBoolean; 25 import static org.mockito.ArgumentMatchers.anyInt; 26 27 import android.app.ActivityManager; 28 import android.app.ActivityTaskManager; 29 import android.app.AppOpsManager; 30 import android.app.AppOpsManagerInternal; 31 import android.content.Context; 32 import android.content.pm.UserInfo; 33 import android.os.Environment; 34 import android.telephony.TelephonyManager; 35 import android.testing.AndroidTestingRunner; 36 37 import androidx.test.platform.app.InstrumentationRegistry; 38 39 import com.android.dx.mockito.inline.extended.ExtendedMockito; 40 import com.android.server.LocalServices; 41 import com.android.server.SensorPrivacyService; 42 import com.android.server.SystemService; 43 import com.android.server.pm.UserManagerInternal; 44 45 import org.junit.Assert; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.mockito.Mock; 49 import org.mockito.MockitoSession; 50 import org.mockito.quality.Strictness; 51 52 import java.io.File; 53 import java.io.IOException; 54 import java.nio.file.Files; 55 import java.util.concurrent.CompletableFuture; 56 57 @RunWith(AndroidTestingRunner.class) 58 public class SensorPrivacyServiceMockingTest { 59 60 private static final String PERSISTENCE_FILE_PATHS_TEMPLATE = 61 "SensorPrivacyServiceMockingTest/persisted_file%d.xml"; 62 public static final String PERSISTENCE_FILE1 = 63 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 1); 64 public static final String PERSISTENCE_FILE2 = 65 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 2); 66 public static final String PERSISTENCE_FILE3 = 67 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 3); 68 public static final String PERSISTENCE_FILE4 = 69 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 4); 70 public static final String PERSISTENCE_FILE5 = 71 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5); 72 public static final String PERSISTENCE_FILE6 = 73 String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6); 74 75 public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE = 76 "SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml"; 77 public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE = 78 "SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml"; 79 public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE = 80 "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml"; 81 public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE = 82 "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml"; 83 84 private Context mContext; 85 @Mock 86 private AppOpsManager mMockedAppOpsManager; 87 @Mock 88 private AppOpsManagerInternal mMockedAppOpsManagerInternal; 89 @Mock 90 private UserManagerInternal mMockedUserManagerInternal; 91 @Mock 92 private ActivityManager mMockedActivityManager; 93 @Mock 94 private ActivityTaskManager mMockedActivityTaskManager; 95 @Mock 96 private TelephonyManager mMockedTelephonyManager; 97 98 @Test testServiceInit()99 public void testServiceInit() throws IOException { 100 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 101 .initMocks(this) 102 .strictness(Strictness.WARN) 103 .spyStatic(LocalServices.class) 104 .spyStatic(Environment.class) 105 .startMocking(); 106 107 try { 108 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 109 spyOn(mContext); 110 111 doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); 112 doReturn(mMockedUserManagerInternal) 113 .when(() -> LocalServices.getService(UserManagerInternal.class)); 114 doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class); 115 doReturn(mMockedActivityTaskManager) 116 .when(mContext).getSystemService(ActivityTaskManager.class); 117 doReturn(mMockedTelephonyManager).when(mContext).getSystemService( 118 TelephonyManager.class); 119 120 String dataDir = mContext.getApplicationInfo().dataDir; 121 doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory()); 122 123 File onDeviceFile = new File(dataDir, "sensor_privacy.xml"); 124 onDeviceFile.delete(); 125 126 // Try all files with one known user 127 doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds(); 128 doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal) 129 .getUserInfo(0); 130 initServiceWithPersistenceFile(onDeviceFile, null); 131 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1); 132 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2); 133 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3); 134 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4); 135 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5); 136 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6); 137 138 // Try all files with two known users 139 doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds(); 140 doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal) 141 .getUserInfo(0); 142 doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal) 143 .getUserInfo(10); 144 initServiceWithPersistenceFile(onDeviceFile, null); 145 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1); 146 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2); 147 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3); 148 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4); 149 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5); 150 initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6); 151 152 } finally { 153 mockitoSession.finishMocking(); 154 } 155 } 156 157 @Test testServiceInit_AppOpsRestricted_micMute_camMute()158 public void testServiceInit_AppOpsRestricted_micMute_camMute() throws IOException { 159 testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE, true, true); 160 } 161 162 @Test testServiceInit_AppOpsRestricted_micMute_camUnmute()163 public void testServiceInit_AppOpsRestricted_micMute_camUnmute() throws IOException { 164 testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE, true, false); 165 } 166 167 @Test testServiceInit_AppOpsRestricted_micUnmute_camMute()168 public void testServiceInit_AppOpsRestricted_micUnmute_camMute() throws IOException { 169 testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE, false, true); 170 } 171 172 @Test testServiceInit_AppOpsRestricted_micUnmute_camUnmute()173 public void testServiceInit_AppOpsRestricted_micUnmute_camUnmute() throws IOException { 174 testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE, false, false); 175 } 176 testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute, boolean expectedMicState, boolean expectedCamState)177 private void testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute, 178 boolean expectedMicState, boolean expectedCamState) 179 throws IOException { 180 MockitoSession mockitoSession = ExtendedMockito.mockitoSession() 181 .initMocks(this) 182 .strictness(Strictness.WARN) 183 .spyStatic(LocalServices.class) 184 .spyStatic(Environment.class) 185 .startMocking(); 186 187 try { 188 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 189 spyOn(mContext); 190 191 doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); 192 doReturn(mMockedAppOpsManagerInternal) 193 .when(() -> LocalServices.getService(AppOpsManagerInternal.class)); 194 doReturn(mMockedUserManagerInternal) 195 .when(() -> LocalServices.getService(UserManagerInternal.class)); 196 doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class); 197 doReturn(mMockedActivityTaskManager) 198 .when(mContext).getSystemService(ActivityTaskManager.class); 199 doReturn(mMockedTelephonyManager).when(mContext).getSystemService( 200 TelephonyManager.class); 201 202 String dataDir = mContext.getApplicationInfo().dataDir; 203 doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory()); 204 205 File onDeviceFile = new File(dataDir, "sensor_privacy.xml"); 206 onDeviceFile.delete(); 207 208 doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds(); 209 doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal) 210 .getUserInfo(0); 211 212 CompletableFuture<Boolean> micState = new CompletableFuture<>(); 213 CompletableFuture<Boolean> camState = new CompletableFuture<>(); 214 doAnswer(invocation -> { 215 int code = invocation.getArgument(0); 216 boolean restricted = invocation.getArgument(1); 217 if (code == AppOpsManager.OP_RECORD_AUDIO) { 218 micState.complete(restricted); 219 } else if (code == AppOpsManager.OP_CAMERA) { 220 camState.complete(restricted); 221 } 222 return null; 223 }).when(mMockedAppOpsManagerInternal).setGlobalRestriction(anyInt(), anyBoolean(), 224 any()); 225 226 initServiceWithPersistenceFile(onDeviceFile, persistenceFileMicMuteCamMute, 0); 227 228 Assert.assertTrue(micState.join() == expectedMicState); 229 Assert.assertTrue(camState.join() == expectedCamState); 230 231 } finally { 232 mockitoSession.finishMocking(); 233 } 234 } 235 initServiceWithPersistenceFile(File onDeviceFile, String persistenceFilePath)236 private void initServiceWithPersistenceFile(File onDeviceFile, 237 String persistenceFilePath) throws IOException { 238 initServiceWithPersistenceFile(onDeviceFile, persistenceFilePath, -1); 239 } 240 initServiceWithPersistenceFile(File onDeviceFile, String persistenceFilePath, int startingUserId)241 private void initServiceWithPersistenceFile(File onDeviceFile, 242 String persistenceFilePath, int startingUserId) throws IOException { 243 if (persistenceFilePath != null) { 244 Files.copy(mContext.getAssets().open(persistenceFilePath), 245 onDeviceFile.toPath()); 246 } 247 SensorPrivacyService service = new SensorPrivacyService(mContext); 248 if (startingUserId != -1) { 249 SystemService.TargetUser mockedTargetUser = 250 ExtendedMockito.mock(SystemService.TargetUser.class); 251 doReturn(startingUserId).when(mockedTargetUser).getUserIdentifier(); 252 service.onUserStarting(mockedTargetUser); 253 } 254 onDeviceFile.delete(); 255 } 256 } 257