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