1 /* 2 * Copyright (C) 2022 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.logcat; 18 19 import static android.os.Process.INVALID_UID; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.app.ActivityManager; 30 import android.app.ActivityManagerInternal; 31 import android.content.Context; 32 import android.content.pm.PackageManager; 33 import android.os.ILogd; 34 import android.os.Looper; 35 import android.os.UserHandle; 36 import android.os.test.TestLooper; 37 38 import com.android.server.LocalServices; 39 import com.android.server.logcat.LogcatManagerService.Injector; 40 import com.android.server.testutils.OffsettableClock; 41 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.mockito.Mock; 46 import org.mockito.MockitoAnnotations; 47 48 import java.util.function.Supplier; 49 50 /** 51 * Tests for {@link com.android.server.logcat.LogcatManagerService}. 52 * 53 * Build/Install/Run: 54 * atest FrameworksServicesTests:LogcatManagerServiceTest 55 */ 56 @SuppressWarnings("GuardedBy") 57 public class LogcatManagerServiceTest { 58 private static final String APP1_PACKAGE_NAME = "app1"; 59 private static final int APP1_UID = 10001; 60 private static final int APP1_GID = 10001; 61 private static final int APP1_PID = 10001; 62 private static final String APP2_PACKAGE_NAME = "app2"; 63 private static final int APP2_UID = 10002; 64 private static final int APP2_GID = 10002; 65 private static final int APP2_PID = 10002; 66 private static final int FD1 = 10; 67 private static final int FD2 = 11; 68 69 @Mock 70 private Context mContextSpy; 71 @Mock 72 private ActivityManagerInternal mActivityManagerInternalMock; 73 @Mock 74 private PackageManager mPackageManagerMock; 75 @Mock 76 private ILogd mLogdMock; 77 78 private LogcatManagerService mService; 79 private LogcatManagerService.LogAccessDialogCallback mDialogCallback; 80 private OffsettableClock mClock; 81 private TestLooper mTestLooper; 82 83 @Before setUp()84 public void setUp() throws Exception { 85 MockitoAnnotations.initMocks(this); 86 addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); 87 when(mActivityManagerInternalMock.getInstrumentationSourceUid(anyInt())) 88 .thenReturn(INVALID_UID); 89 90 mClock = new OffsettableClock.Stopped(); 91 mTestLooper = new TestLooper(mClock::now); 92 when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock); 93 when(mPackageManagerMock.getPackagesForUid(APP1_UID)).thenReturn( 94 new String[]{APP1_PACKAGE_NAME}); 95 when(mPackageManagerMock.getPackagesForUid(APP2_UID)).thenReturn( 96 new String[]{APP2_PACKAGE_NAME}); 97 when(mActivityManagerInternalMock.getPackageNameByPid(APP1_PID)).thenReturn( 98 APP1_PACKAGE_NAME); 99 when(mActivityManagerInternalMock.getPackageNameByPid(APP2_PID)).thenReturn( 100 APP2_PACKAGE_NAME); 101 102 mService = new LogcatManagerService(mContextSpy, new Injector() { 103 @Override 104 protected Supplier<Long> createClock() { 105 return mClock::now; 106 } 107 108 @Override 109 protected Looper getLooper() { 110 return mTestLooper.getLooper(); 111 } 112 113 @Override 114 protected ILogd getLogdService() { 115 return mLogdMock; 116 } 117 }); 118 mDialogCallback = mService.getDialogCallback(); 119 mService.onStart(); 120 } 121 122 @After tearDown()123 public void tearDown() throws Exception { 124 LocalServices.removeServiceForTest(ActivityManagerInternal.class); 125 } 126 127 /** 128 * Creates a mock and registers it to {@link LocalServices}. 129 */ addLocalServiceMock(Class<T> clazz, T mock)130 private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { 131 LocalServices.removeServiceForTest(clazz); 132 LocalServices.addService(clazz, mock); 133 } 134 135 @Test test_RequestFromBackground_DeclinedWithoutPrompt()136 public void test_RequestFromBackground_DeclinedWithoutPrompt() throws Exception { 137 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 138 ActivityManager.PROCESS_STATE_RECEIVER); 139 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 140 mTestLooper.dispatchAll(); 141 142 verify(mLogdMock).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 143 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 144 verify(mContextSpy, never()).startActivityAsUser(any(), any()); 145 } 146 147 @Test test_RequestFromBackground_ApprovedIfInstrumented()148 public void test_RequestFromBackground_ApprovedIfInstrumented() throws Exception { 149 when(mActivityManagerInternalMock.getInstrumentationSourceUid(APP1_UID)) 150 .thenReturn(APP1_UID); 151 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 152 ActivityManager.PROCESS_STATE_RECEIVER); 153 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 154 mTestLooper.dispatchAll(); 155 156 verify(mLogdMock).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 157 verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 158 verify(mContextSpy, never()).startActivityAsUser(any(), any()); 159 } 160 161 @Test test_RequestFromForegroundService_DeclinedWithoutPrompt()162 public void test_RequestFromForegroundService_DeclinedWithoutPrompt() throws Exception { 163 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 164 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); 165 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 166 mTestLooper.dispatchAll(); 167 168 verify(mLogdMock).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 169 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 170 verify(mContextSpy, never()).startActivityAsUser(any(), any()); 171 } 172 173 @Test test_RequestFromTop_ShowsPrompt()174 public void test_RequestFromTop_ShowsPrompt() throws Exception { 175 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 176 ActivityManager.PROCESS_STATE_TOP); 177 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 178 mTestLooper.dispatchAll(); 179 180 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 181 verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 182 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 183 } 184 185 @Test test_RequestFromTop_NoInteractionWithPrompt_DeclinesAfterTimeout()186 public void test_RequestFromTop_NoInteractionWithPrompt_DeclinesAfterTimeout() 187 throws Exception { 188 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 189 ActivityManager.PROCESS_STATE_TOP); 190 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 191 mTestLooper.dispatchAll(); 192 193 advanceTime(LogcatManagerService.PENDING_CONFIRMATION_TIMEOUT_MILLIS); 194 195 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 196 verify(mLogdMock).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 197 } 198 199 @Test test_RequestFromTop_Approved()200 public void test_RequestFromTop_Approved() throws Exception { 201 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 202 ActivityManager.PROCESS_STATE_TOP); 203 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 204 mTestLooper.dispatchAll(); 205 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 206 207 mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 208 mTestLooper.dispatchAll(); 209 210 verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 211 verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 212 } 213 214 @Test test_RequestFromTop_Declined()215 public void test_RequestFromTop_Declined() throws Exception { 216 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 217 ActivityManager.PROCESS_STATE_TOP); 218 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 219 mTestLooper.dispatchAll(); 220 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 221 222 mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 223 mTestLooper.dispatchAll(); 224 225 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 226 verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 227 } 228 229 @Test test_RequestFromTop_MultipleRequestsApprovedTogether()230 public void test_RequestFromTop_MultipleRequestsApprovedTogether() throws Exception { 231 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 232 ActivityManager.PROCESS_STATE_TOP); 233 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 234 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); 235 mTestLooper.dispatchAll(); 236 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 237 verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); 238 verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); 239 240 mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 241 mTestLooper.dispatchAll(); 242 243 verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 244 verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD2); 245 verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 246 verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD2); 247 } 248 249 @Test test_RequestFromTop_MultipleRequestsDeclinedTogether()250 public void test_RequestFromTop_MultipleRequestsDeclinedTogether() throws Exception { 251 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 252 ActivityManager.PROCESS_STATE_TOP); 253 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 254 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); 255 mTestLooper.dispatchAll(); 256 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 257 verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); 258 verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); 259 260 mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 261 mTestLooper.dispatchAll(); 262 263 verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 264 verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD2); 265 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 266 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD2); 267 } 268 269 @Test test_RequestFromTop_Approved_DoesNotShowPromptAgain()270 public void test_RequestFromTop_Approved_DoesNotShowPromptAgain() throws Exception { 271 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 272 ActivityManager.PROCESS_STATE_TOP); 273 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 274 mTestLooper.dispatchAll(); 275 mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 276 mTestLooper.dispatchAll(); 277 278 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); 279 mTestLooper.dispatchAll(); 280 281 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 282 verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); 283 verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD2); 284 verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD2); 285 } 286 287 @Test test_RequestFromTop_Declined_DoesNotShowPromptAgain()288 public void test_RequestFromTop_Declined_DoesNotShowPromptAgain() throws Exception { 289 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 290 ActivityManager.PROCESS_STATE_TOP); 291 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 292 mTestLooper.dispatchAll(); 293 mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 294 mTestLooper.dispatchAll(); 295 296 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); 297 mTestLooper.dispatchAll(); 298 299 verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 300 verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1); 301 verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD2); 302 verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD2); 303 } 304 305 @Test test_RequestFromTop_Approved_ShowsPromptForDifferentClient()306 public void test_RequestFromTop_Approved_ShowsPromptForDifferentClient() throws Exception { 307 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 308 ActivityManager.PROCESS_STATE_TOP); 309 when(mActivityManagerInternalMock.getUidProcessState(APP2_UID)).thenReturn( 310 ActivityManager.PROCESS_STATE_TOP); 311 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 312 mTestLooper.dispatchAll(); 313 mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 314 mTestLooper.dispatchAll(); 315 316 mService.getBinderService().startThread(APP2_UID, APP2_GID, APP2_PID, FD2); 317 mTestLooper.dispatchAll(); 318 319 verify(mContextSpy, times(2)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 320 verify(mLogdMock, never()).decline(APP2_UID, APP2_GID, APP2_PID, FD2); 321 verify(mLogdMock, never()).approve(APP2_UID, APP2_GID, APP2_PID, FD2); 322 } 323 324 @Test test_RequestFromTop_Approved_ShowPromptAgainAfterTimeout()325 public void test_RequestFromTop_Approved_ShowPromptAgainAfterTimeout() throws Exception { 326 when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( 327 ActivityManager.PROCESS_STATE_TOP); 328 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 329 mTestLooper.dispatchAll(); 330 mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); 331 mTestLooper.dispatchAll(); 332 333 advanceTime(LogcatManagerService.STATUS_EXPIRATION_TIMEOUT_MILLIS); 334 335 mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); 336 mTestLooper.dispatchAll(); 337 338 verify(mContextSpy, times(2)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); 339 } 340 advanceTime(long timeMs)341 private void advanceTime(long timeMs) { 342 mClock.fastForward(timeMs); 343 mTestLooper.dispatchAll(); 344 } 345 } 346