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