1 /*
2  * Copyright (C) 2017 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 android.app.cts.android.app.cts.tools;
18 
19 import android.app.ActivityManager;
20 import android.app.Instrumentation;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageManager;
25 import android.os.IBinder;
26 import android.os.Parcel;
27 import android.os.RemoteException;
28 import android.os.UserHandle;
29 
30 import androidx.test.InstrumentationRegistry;
31 
32 import com.android.compatibility.common.util.SystemUtil;
33 
34 import java.io.IOException;
35 
36 /**
37  * Helper for monitoring and controlling the state of a process under test.
38  * Primarily currently a convenience for cleanly killing a process and waiting
39  * for it to entirely disappear from the system.
40  */
41 public final class ServiceProcessController {
42     final Context mContext;
43     final Instrumentation mInstrumentation;
44     final String mMyPackageName;
45     final Intent[] mServiceIntents;
46     final String mServicePackage;
47     final long mDefaultWaitTime;
48 
49     final ActivityManager mAm;
50     final Parcel mData;
51     final ServiceConnectionHandler[] mConnections;
52     final int mUid;
53     final int mUserId;
54     final UidImportanceListener mUidForegroundListener;
55     final UidImportanceListener mUidGoneListener;
56     final WatchUidRunner mUidWatcher;
57 
ServiceProcessController(Context context, Instrumentation instrumentation, String myPackageName, Intent[] serviceIntents)58     public ServiceProcessController(Context context, Instrumentation instrumentation,
59             String myPackageName, Intent[] serviceIntents)
60             throws IOException, PackageManager.NameNotFoundException {
61         this(context, instrumentation, myPackageName, serviceIntents, 5*1000);
62     }
63 
ServiceProcessController(Context context, Instrumentation instrumentation, String myPackageName, Intent[] serviceIntents, long defaultWaitTime)64     public ServiceProcessController(Context context, Instrumentation instrumentation,
65             String myPackageName, Intent[] serviceIntents, long defaultWaitTime)
66             throws IOException, PackageManager.NameNotFoundException {
67         mContext = context;
68         mInstrumentation = instrumentation;
69         mMyPackageName = myPackageName;
70         mServiceIntents = serviceIntents;
71         mServicePackage = mServiceIntents[0].getComponent().getPackageName();
72         mDefaultWaitTime = defaultWaitTime;
73 
74         InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
75                 mMyPackageName, android.Manifest.permission.PACKAGE_USAGE_STATS);
76         /*
77         Log.d("XXXX", "Invoke: " + cmd);
78         Log.d("XXXX", "Result: " + result);
79         Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
80                 + STUB_PACKAGE_NAME));
81         */
82 
83         mAm = mContext.getSystemService(ActivityManager.class);
84         mData = Parcel.obtain();
85         mConnections = new ServiceConnectionHandler[serviceIntents.length];
86         for (int i=0; i<serviceIntents.length; i++) {
87             mConnections[i] = new ServiceConnectionHandler(mContext, serviceIntents[i],
88                     mDefaultWaitTime);
89         }
90 
91         ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
92                 mServicePackage, 0);
93         mUid = appInfo.uid;
94         mUserId = UserHandle.getUserId(mUid);
95 
96         mUidForegroundListener = new UidImportanceListener(mContext, appInfo.uid,
97                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE, mDefaultWaitTime);
98         mUidForegroundListener.register();
99         mUidGoneListener = new UidImportanceListener(mContext, appInfo.uid,
100                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY, mDefaultWaitTime);
101         mUidGoneListener.register();
102 
103         mUidWatcher = new WatchUidRunner(instrumentation, appInfo.uid, mDefaultWaitTime);
104     }
105 
denyBackgroundOp()106     public void denyBackgroundOp() throws IOException {
107         denyBackgroundOp(mDefaultWaitTime);
108     }
109 
denyBackgroundOp(long timeout)110     public void denyBackgroundOp(long timeout) throws IOException {
111         String cmd = "appops set --user " + mUserId + " "
112                 + mServicePackage + " RUN_IN_BACKGROUND deny";
113         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
114 
115         // This is a side-effect of the app op command.
116         mUidWatcher.expect(WatchUidRunner.CMD_IDLE, null, timeout);
117         mUidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "NONE", timeout);
118     }
119 
allowBackgroundOp()120     public void allowBackgroundOp() throws IOException {
121         String cmd = "appops set --user " + mUserId + " "
122                 + mServicePackage + " RUN_IN_BACKGROUND allow";
123         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
124     }
125 
126     /** The "battery restriction" forced app standby app-op */
denyAnyInBackgroundOp()127     public void denyAnyInBackgroundOp() throws IOException {
128         String cmd = "appops set --user " + mUserId + " "
129                 + mServicePackage + " RUN_ANY_IN_BACKGROUND deny";
130         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
131     }
132 
allowAnyInBackgroundOp()133     public void allowAnyInBackgroundOp() throws IOException {
134         String cmd = "appops set --user " + mUserId + " "
135                 + mServicePackage + " RUN_ANY_IN_BACKGROUND allow";
136         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
137     }
138 
makeUidIdle()139     public void makeUidIdle() throws IOException {
140         String cmd = "am make-uid-idle " + mServicePackage;
141         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
142     }
143 
removeFromWhitelist()144     public void removeFromWhitelist() throws IOException {
145         String cmd = "cmd deviceidle whitelist -" + mServicePackage;
146         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
147     }
148 
addToWhitelist()149     public void addToWhitelist() throws IOException {
150         String cmd = "cmd deviceidle whitelist +" + mServicePackage;
151         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
152     }
153 
tempWhitelist(long duration)154     public void tempWhitelist(long duration) throws IOException {
155         String cmd = "cmd deviceidle tempwhitelist -u " + mUserId
156                 + " -d " + duration + " " + mServicePackage;
157         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
158     }
159 
removeFromTempWhitelist()160     public void removeFromTempWhitelist() throws IOException {
161         String cmd = "cmd deviceidle tempwhitelist -u " + mUserId
162                 + " -r " + mServicePackage;
163         SystemUtil.runShellCommand(mInstrumentation, cmd);
164     }
165 
setAppOpMode(String opStr, String mode)166     public void setAppOpMode(String opStr, String mode) throws IOException {
167         String cmd = "cmd appops set --user " + mUserId + " "
168                 + mServicePackage + " " + opStr + "  " + mode;
169         SystemUtil.runShellCommand(mInstrumentation, cmd);
170     }
171 
cleanup()172     public void cleanup() throws IOException {
173         removeFromWhitelist();
174         allowBackgroundOp();
175         allowAnyInBackgroundOp();
176         mUidWatcher.finish();
177         mUidGoneListener.unregister();
178         mUidForegroundListener.unregister();
179         mData.recycle();
180     }
181 
getConnection(int index)182     public ServiceConnectionHandler getConnection(int index) {
183         return mConnections[index];
184     }
185 
getUid()186     public int getUid() {
187         return mUid;
188     }
189 
getUidForegroundListener()190     public UidImportanceListener getUidForegroundListener() {
191         return mUidForegroundListener;
192     }
193 
getUidGoneListener()194     public UidImportanceListener getUidGoneListener() {
195         return mUidGoneListener;
196     }
197 
getUidWatcher()198     public WatchUidRunner getUidWatcher() {
199         return mUidWatcher;
200     }
201 
ensureProcessGone()202     public void ensureProcessGone() {
203         ensureProcessGone(mDefaultWaitTime);
204     }
205 
ensureProcessGone(long timeout)206     public void ensureProcessGone(long timeout) {
207         for (int i=0; i<mConnections.length; i++) {
208             mConnections[i].bind(timeout);
209         }
210 
211         for (int i=0; i<mConnections.length; i++) {
212             IBinder serviceBinder = mConnections[i].getServiceIBinder();
213             mConnections[i].unbind(timeout);
214             try {
215                 serviceBinder.transact(IBinder.FIRST_CALL_TRANSACTION, mData, null, 0);
216             } catch (RemoteException e) {
217             }
218         }
219 
220         // Wait for uid's process to go away.
221         mUidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE,
222                 ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, timeout);
223         int importance = mAm.getPackageImportance(mServicePackage);
224         if (importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
225             throw new IllegalStateException("Unexpected importance after killing process: "
226                     + importance);
227         }
228         mUidWatcher.waitFor(WatchUidRunner.CMD_GONE, timeout);
229     }
230 }
231