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