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