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 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 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 103 public void denyBackgroundOp() throws IOException { 104 denyBackgroundOp(mDefaultWaitTime); 105 } 106 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 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 public void makeUidIdle() throws IOException { 122 String cmd = "am make-uid-idle " + mServicePackage; 123 String result = SystemUtil.runShellCommand(mInstrumentation, cmd); 124 } 125 126 public void removeFromWhitelist() throws IOException { 127 String cmd = "cmd deviceidle whitelist -" + mServicePackage; 128 String result = SystemUtil.runShellCommand(mInstrumentation, cmd); 129 } 130 131 public void addToWhitelist() throws IOException { 132 String cmd = "cmd deviceidle whitelist +" + mServicePackage; 133 String result = SystemUtil.runShellCommand(mInstrumentation, cmd); 134 } 135 136 public void tempWhitelist(long duration) throws IOException { 137 String cmd = "cmd deviceidle tempwhitelist -d " + duration + " " + mServicePackage; 138 String result = SystemUtil.runShellCommand(mInstrumentation, cmd); 139 } 140 141 public void removeFromTempWhitelist() throws IOException { 142 String cmd = "cmd deviceidle tempwhitelist -r " + mServicePackage; 143 SystemUtil.runShellCommand(mInstrumentation, cmd); 144 } 145 146 public void setAppOpMode(String opStr, String mode) throws IOException { 147 String cmd = "cmd appops set " + mServicePackage + " " + opStr + " " + mode; 148 SystemUtil.runShellCommand(mInstrumentation, cmd); 149 } 150 151 public void cleanup() throws IOException { 152 removeFromWhitelist(); 153 allowBackgroundOp(); 154 mUidWatcher.finish(); 155 mUidGoneListener.unregister(); 156 mUidForegroundListener.unregister(); 157 mData.recycle(); 158 } 159 160 public ServiceConnectionHandler getConnection(int index) { 161 return mConnections[index]; 162 } 163 164 public int getUid() { 165 return mUid; 166 } 167 168 public UidImportanceListener getUidForegroundListener() { 169 return mUidForegroundListener; 170 } 171 172 public UidImportanceListener getUidGoneListener() { 173 return mUidGoneListener; 174 } 175 176 public WatchUidRunner getUidWatcher() { 177 return mUidWatcher; 178 } 179 180 public void ensureProcessGone() { 181 ensureProcessGone(mDefaultWaitTime); 182 } 183 184 public void ensureProcessGone(long timeout) { 185 for (int i=0; i<mConnections.length; i++) { 186 mConnections[i].bind(timeout); 187 } 188 189 for (int i=0; i<mConnections.length; i++) { 190 IBinder serviceBinder = mConnections[i].getServiceIBinder(); 191 mConnections[i].unbind(timeout); 192 try { 193 serviceBinder.transact(IBinder.FIRST_CALL_TRANSACTION, mData, null, 0); 194 } catch (RemoteException e) { 195 } 196 } 197 198 // Wait for uid's process to go away. 199 mUidGoneListener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, 200 ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, timeout); 201 int importance = mAm.getPackageImportance(mServicePackage); 202 if (importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) { 203 throw new IllegalStateException("Unexpected importance after killing process: " 204 + importance); 205 } 206 mUidWatcher.waitFor(WatchUidRunner.CMD_GONE, null, timeout); 207 } 208 } 209