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.am;
18 
19 import static android.os.Process.myPid;
20 import static android.os.Process.myUid;
21 
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.ArgumentMatchers.anyBoolean;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.ArgumentMatchers.anyLong;
26 import static org.mockito.Mockito.doAnswer;
27 import static org.mockito.Mockito.doNothing;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.verify;
33 
34 import android.app.ActivityManagerInternal;
35 import android.app.IApplicationThread;
36 import android.app.usage.UsageStatsManagerInternal;
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.pm.ApplicationInfo;
40 import android.content.pm.PackageManagerInternal;
41 import android.os.Binder;
42 import android.os.Handler;
43 import android.os.HandlerThread;
44 import android.os.IBinder;
45 import android.os.SystemClock;
46 import android.util.Log;
47 
48 import androidx.test.filters.MediumTest;
49 import androidx.test.platform.app.InstrumentationRegistry;
50 
51 import com.android.server.DropBoxManagerInternal;
52 import com.android.server.LocalServices;
53 import com.android.server.am.ActivityManagerService.Injector;
54 import com.android.server.appop.AppOpsService;
55 import com.android.server.wm.ActivityTaskManagerInternal;
56 import com.android.server.wm.ActivityTaskManagerService;
57 
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Rule;
61 import org.junit.Test;
62 import org.mockito.Mock;
63 import org.mockito.MockitoAnnotations;
64 
65 import java.io.File;
66 import java.util.Arrays;
67 
68 
69 /**
70  * Tests to verify process starts are completed or timeout correctly
71  */
72 @MediumTest
73 @SuppressWarnings("GuardedBy")
74 public class AsyncProcessStartTest {
75     private static final String TAG = "AsyncProcessStartTest";
76 
77     private static final String PACKAGE = "com.foo";
78 
79     @Rule
80     public final ApplicationExitInfoTest.ServiceThreadRule
81             mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
82 
83     private Context mContext;
84     private HandlerThread mHandlerThread;
85 
86     @Mock
87     private AppOpsService mAppOpsService;
88     @Mock
89     private DropBoxManagerInternal mDropBoxManagerInt;
90     @Mock
91     private PackageManagerInternal mPackageManagerInt;
92     @Mock
93     private UsageStatsManagerInternal mUsageStatsManagerInt;
94     @Mock
95     private ActivityManagerInternal mActivityManagerInt;
96     @Mock
97     private ActivityTaskManagerInternal mActivityTaskManagerInt;
98     @Mock
99     private BatteryStatsService mBatteryStatsService;
100 
101     private ActivityManagerService mRealAms;
102     private ActivityManagerService mAms;
103 
104     private ProcessList mRealProcessList = new ProcessList();
105     private ProcessList mProcessList;
106 
107     @Before
setUp()108     public void setUp() throws Exception {
109         MockitoAnnotations.initMocks(this);
110 
111         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
112 
113         mHandlerThread = new HandlerThread(TAG);
114         mHandlerThread.start();
115 
116         LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
117         LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
118 
119         LocalServices.removeServiceForTest(PackageManagerInternal.class);
120         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
121 
122         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
123         LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInt);
124 
125         LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
126         LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInt);
127 
128         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
129         doReturn(true).when(mActivityTaskManagerInt).attachApplication(any());
130         doNothing().when(mActivityTaskManagerInt).onProcessMapped(anyInt(), any());
131 
132         mRealAms = new ActivityManagerService(
133                 new TestInjector(mContext), mServiceThreadRule.getThread());
134         mRealAms.mConstants.loadDeviceConfigConstants();
135         mRealAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
136         mRealAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
137         mRealAms.mAtmInternal = mActivityTaskManagerInt;
138         mRealAms.mPackageManagerInt = mPackageManagerInt;
139         mRealAms.mUsageStatsService = mUsageStatsManagerInt;
140         mRealAms.mProcessesReady = true;
141         mAms = spy(mRealAms);
142         mRealProcessList.mService = mAms;
143         mProcessList = spy(mRealProcessList);
144 
145         doAnswer((invocation) -> {
146             Log.v(TAG, "Intercepting isProcStartValidLocked() for "
147                     + Arrays.toString(invocation.getArguments()));
148             return null;
149         }).when(mProcessList).isProcStartValidLocked(any(), anyLong());
150     }
151 
152     @After
tearDown()153     public void tearDown() throws Exception {
154         mHandlerThread.quit();
155     }
156 
157     private class TestInjector extends Injector {
TestInjector(Context context)158         TestInjector(Context context) {
159             super(context);
160         }
161 
162         @Override
getAppOpsService(File recentAccessesFile, File storageFile, Handler handler)163         public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
164                 Handler handler) {
165             return mAppOpsService;
166         }
167 
168         @Override
getUiHandler(ActivityManagerService service)169         public Handler getUiHandler(ActivityManagerService service) {
170             return mHandlerThread.getThreadHandler();
171         }
172 
173         @Override
getProcessList(ActivityManagerService service)174         public ProcessList getProcessList(ActivityManagerService service) {
175             return mRealProcessList;
176         }
177 
178         @Override
getBatteryStatsService()179         public BatteryStatsService getBatteryStatsService() {
180             return mBatteryStatsService;
181         }
182     }
183 
makeActiveProcessRecord(String packageName, boolean wedge)184     private ProcessRecord makeActiveProcessRecord(String packageName, boolean wedge)
185             throws Exception {
186         final ApplicationInfo ai = makeApplicationInfo(packageName);
187         return makeActiveProcessRecord(ai, wedge);
188     }
189 
makeActiveProcessRecord(ApplicationInfo ai, boolean wedge)190     private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, boolean wedge)
191             throws Exception {
192         final IApplicationThread thread = mock(IApplicationThread.class);
193         final IBinder threadBinder = new Binder();
194         doReturn(threadBinder).when(thread).asBinder();
195         doAnswer((invocation) -> {
196             Log.v(TAG, "Intercepting bindApplication() for "
197                     + Arrays.toString(invocation.getArguments()));
198             if (!wedge) {
199                 if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
200                     mRealAms.finishAttachApplication(0);
201                 }
202             }
203             return null;
204         }).when(thread).bindApplication(
205                 any(), any(),
206                 any(), any(),
207                 any(), any(),
208                 any(), any(),
209                 any(),
210                 any(), anyInt(),
211                 anyBoolean(), anyBoolean(),
212                 anyBoolean(), anyBoolean(), any(),
213                 any(), any(), any(),
214                 any(), any(),
215                 any(), any(),
216                 any(),
217                 anyLong(), anyLong());
218 
219         final ProcessRecord r = spy(new ProcessRecord(mAms, ai, ai.processName, ai.uid));
220         r.setPid(myPid());
221         r.setStartUid(myUid());
222         r.setHostingRecord(new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST));
223         r.makeActive(thread, mAms.mProcessStats);
224         doNothing().when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean(),
225                 anyBoolean());
226 
227         return r;
228     }
229 
makeApplicationInfo(String packageName)230     static ApplicationInfo makeApplicationInfo(String packageName) {
231         final ApplicationInfo ai = new ApplicationInfo();
232         ai.packageName = packageName;
233         ai.processName = packageName;
234         ai.uid = myUid();
235         return ai;
236     }
237 
238     /**
239      * Verify that we don't kill a normal process
240      */
241     @Test
testNormal()242     public void testNormal() throws Exception {
243         if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
244             ProcessRecord app = startProcessAndWait(false);
245             verify(app, never()).killLocked(any(), anyInt(), anyBoolean());
246         }
247     }
248 
249     /**
250      * Verify that we kill a wedged process after the process start timeout
251      */
252     @Test
testWedged()253     public void testWedged() throws Exception {
254         ProcessRecord app = startProcessAndWait(true);
255 
256         verify(app).killLocked(any(), anyInt(), anyBoolean());
257     }
258 
startProcessAndWait(boolean wedge)259     private ProcessRecord startProcessAndWait(boolean wedge) throws Exception {
260         final ProcessRecord app = makeActiveProcessRecord(PACKAGE, wedge);
261         final ApplicationInfo appInfo = makeApplicationInfo(PACKAGE);
262 
263         mProcessList.handleProcessStartedLocked(app, app.getPid(), /* usingWrapper */ false,
264                 /* expectedStartSeq */ 0, /* procAttached */ false);
265 
266         app.getThread().bindApplication(PACKAGE, appInfo,
267                 null, null,
268                 null,
269                 null,
270                 null, null,
271                 null,
272                 null, 0,
273                 false, false,
274                 true, false,
275                 null,
276                 null, null,
277                 null,
278                 null, null, null,
279                 null, null,
280                 0, 0);
281 
282         // Sleep until timeout should have triggered
283         SystemClock.sleep(ActivityManagerService.PROC_START_TIMEOUT + 1000);
284 
285         return app;
286     }
287 }
288