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 static org.junit.Assert.assertTrue; 20 import static org.junit.Assert.fail; 21 22 import android.app.Instrumentation; 23 import android.os.ParcelFileDescriptor; 24 import android.os.SystemClock; 25 import android.util.Log; 26 27 import java.io.BufferedOutputStream; 28 import java.io.BufferedReader; 29 import java.io.FileInputStream; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.InputStreamReader; 33 import java.io.PrintWriter; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.regex.Pattern; 37 38 /** 39 * bit CtsAppTestCases:ActivityManagerProcessStateTest 40 */ 41 public class WatchUidRunner { 42 static final String TAG = "WatchUidRunner"; 43 44 public static final int CMD_PROCSTATE = 0; 45 public static final int CMD_ACTIVE = 1; 46 public static final int CMD_IDLE = 2; 47 public static final int CMD_UNCACHED = 3; 48 public static final int CMD_CACHED = 4; 49 public static final int CMD_GONE = 5; 50 public static final int CMD_CAPABILITY = 6; 51 52 public static final String STATE_PERSISTENT = "PER"; 53 public static final String STATE_PERSISTENT_UI = "PERU"; 54 public static final String STATE_TOP = "TOP"; 55 public static final String STATE_BOUND_FG_SERVICE = "BFGS"; 56 public static final String STATE_BOUND_TOP = "BTOP"; 57 public static final String STATE_FG_SERVICE_LOCATION = "FGSL"; 58 public static final String STATE_FG_SERVICE = "FGS"; 59 public static final String STATE_TOP_SLEEPING = "TPSL"; 60 public static final String STATE_IMPORTANT_FG = "IMPF"; 61 public static final String STATE_IMPORTANT_BG = "IMPB"; 62 public static final String STATE_TRANSIENT_BG = "TRNB"; 63 public static final String STATE_BACKUP = "BKUP"; 64 public static final String STATE_HEAVY_WEIGHT = "HVY"; 65 public static final String STATE_SERVICE = "SVC"; 66 public static final String STATE_RECEIVER = "RCVR"; 67 public static final String STATE_HOME = "HOME"; 68 public static final String STATE_LAST = "LAST"; 69 public static final String STATE_CACHED_ACTIVITY = "CAC"; 70 public static final String STATE_CACHED_ACTIVITY_CLIENT = "CACC"; 71 public static final String STATE_CACHED_RECENT = "CRE"; 72 public static final String STATE_CACHED_EMPTY = "CEM"; 73 public static final String STATE_NONEXISTENT = "NONE"; 74 75 static final String[] COMMAND_TO_STRING = new String[] { 76 "procstate", "active", "idle", "uncached", "cached", "gone", "capability" 77 }; 78 79 final Instrumentation mInstrumentation; 80 final int mUid; 81 final String mUidStr; 82 final long mDefaultWaitTime; 83 final Pattern mSpaceSplitter; 84 final ParcelFileDescriptor mReadFd; 85 final FileInputStream mReadStream; 86 final BufferedReader mReadReader; 87 final ParcelFileDescriptor mWriteFd; 88 final FileOutputStream mWriteStream; 89 final PrintWriter mWritePrinter; 90 final Thread mReaderThread; 91 92 // Shared state is protected by this. 93 final ArrayList<String[]> mPendingLines = new ArrayList<>(); 94 95 boolean mStopping; 96 WatchUidRunner(Instrumentation instrumentation, int uid)97 public WatchUidRunner(Instrumentation instrumentation, int uid) { 98 this(instrumentation, uid, 5*1000); 99 } 100 WatchUidRunner(Instrumentation instrumentation, int uid, long defaultWaitTime)101 public WatchUidRunner(Instrumentation instrumentation, int uid, long defaultWaitTime) { 102 this(instrumentation, uid, defaultWaitTime, 0); 103 } 104 WatchUidRunner(Instrumentation instrumentation, int uid, long defaultWaitTime, int capabilityMask)105 public WatchUidRunner(Instrumentation instrumentation, int uid, long defaultWaitTime, 106 int capabilityMask) { 107 mInstrumentation = instrumentation; 108 mUid = uid; 109 mUidStr = Integer.toString(uid); 110 mDefaultWaitTime = defaultWaitTime; 111 mSpaceSplitter = Pattern.compile("\\s+"); 112 final String maskString = capabilityMask == 0 ? "" : " --mask " + capabilityMask; 113 ParcelFileDescriptor[] pfds = instrumentation.getUiAutomation().executeShellCommandRw( 114 "am watch-uids --oom " + uid + maskString); 115 mReadFd = pfds[0]; 116 mReadStream = new ParcelFileDescriptor.AutoCloseInputStream(mReadFd); 117 mReadReader = new BufferedReader(new InputStreamReader(mReadStream)); 118 mWriteFd = pfds[1]; 119 mWriteStream = new ParcelFileDescriptor.AutoCloseOutputStream(mWriteFd); 120 mWritePrinter = new PrintWriter(new BufferedOutputStream(mWriteStream)); 121 // Executing a shell command is asynchronous but we can't proceed further with the test 122 // until the 'watch-uids' cmd is executed. 123 waitUntilUidObserverReady(); 124 mReaderThread = new ReaderThread(); 125 mReaderThread.start(); 126 } 127 waitUntilUidObserverReady()128 private void waitUntilUidObserverReady() { 129 try { 130 final String line = mReadReader.readLine(); 131 assertTrue("Unexpected output: " + line, line.startsWith("Watching uid states")); 132 } catch (IOException e) { 133 fail("Error occurred " + e); 134 } 135 } 136 expect(int cmd, String procState)137 public void expect(int cmd, String procState) { 138 expect(cmd, procState, mDefaultWaitTime); 139 } 140 expect(int cmd, String procState, long timeout)141 public void expect(int cmd, String procState, long timeout) { 142 long waitUntil = SystemClock.uptimeMillis() + timeout; 143 String[] line = waitForNextLine(waitUntil, cmd, procState, 0); 144 if (!COMMAND_TO_STRING[cmd].equals(line[1])) { 145 String msg = "Expected cmd " + COMMAND_TO_STRING[cmd] 146 + " uid " + mUid + " but next report was " + Arrays.toString(line); 147 Log.d(TAG, msg); 148 logRemainingLines(); 149 throw new IllegalStateException(msg); 150 } 151 if (procState != null && (line.length < 3 || !procState.equals(line[2]))) { 152 String msg = "Expected procstate " + procState 153 + " uid " + mUid + " but next report was " + Arrays.toString(line); 154 Log.d(TAG, msg); 155 logRemainingLines(); 156 throw new IllegalStateException(msg); 157 } 158 Log.d(TAG, "Got expected: " + Arrays.toString(line)); 159 } 160 waitFor(int cmd)161 public void waitFor(int cmd) { 162 waitFor(cmd, null, null, mDefaultWaitTime); 163 } 164 waitFor(int cmd, long timeout)165 public void waitFor(int cmd, long timeout) { 166 waitFor(cmd, null, null, timeout); 167 } 168 waitFor(int cmd, String procState)169 public void waitFor(int cmd, String procState) { 170 waitFor(cmd, procState, null, mDefaultWaitTime); 171 } 172 waitFor(int cmd, String procState, Integer capability)173 public void waitFor(int cmd, String procState, Integer capability) { 174 waitFor(cmd, procState, capability, mDefaultWaitTime); 175 } 176 waitFor(int cmd, String procState, long timeout)177 public void waitFor(int cmd, String procState, long timeout) { 178 waitFor(cmd, procState, null, timeout); 179 } 180 waitFor(int cmd, String procState, Integer capability, long timeout)181 public void waitFor(int cmd, String procState, Integer capability, long timeout) { 182 Log.i(TAG, "waitFor(cmd=" + cmd + ", procState=" + procState + ", capability=" + capability 183 + ", timeout=" + timeout + ")"); 184 long waitUntil = SystemClock.uptimeMillis() + timeout; 185 while (true) { 186 String[] line = waitForNextLine(waitUntil, cmd, procState, capability); 187 if (COMMAND_TO_STRING[cmd].equals(line[1])) { 188 if (procState == null && capability == null) { 189 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 190 return; 191 } 192 if (cmd == CMD_PROCSTATE) { 193 if (procState != null && capability != null) { 194 if (procState.equals(line[2]) && capability.toString().equals(line[6])) { 195 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 196 return; 197 } 198 } else if (procState != null) { 199 if (procState.equals(line[2])) { 200 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 201 return; 202 } 203 } else if (capability != null) { 204 if (capability.toString().equals(line[6])) { 205 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 206 return; 207 } 208 } 209 } else { 210 if (procState != null 211 && procState.equals(line[2])) { 212 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 213 return; 214 } 215 } 216 Log.d(TAG, "Skipping because procstate not " + procState + ": " 217 + Arrays.toString(line)); 218 } else { 219 Log.d(TAG, "Skipping because not " + COMMAND_TO_STRING[cmd] + ": " 220 + Arrays.toString(line)); 221 } 222 } 223 } 224 logRemainingLines()225 void logRemainingLines() { 226 synchronized (mPendingLines) { 227 while (mPendingLines.size() > 0) { 228 String[] res = mPendingLines.remove(0); 229 if (res[0].startsWith("#")) { 230 Log.d(TAG, "Remaining: " + res[0]); 231 } else { 232 Log.d(TAG, "Remaining: " + Arrays.toString(res)); 233 } 234 } 235 } 236 } 237 clearHistory()238 public void clearHistory() { 239 synchronized (mPendingLines) { 240 mPendingLines.clear(); 241 } 242 } 243 waitForNextLine(long waitUntil, int cmd, String procState, Integer capability)244 String[] waitForNextLine(long waitUntil, int cmd, String procState, Integer capability) { 245 synchronized (mPendingLines) { 246 while (true) { 247 while (mPendingLines.size() == 0) { 248 long now = SystemClock.uptimeMillis(); 249 if (now >= waitUntil) { 250 String msg = "Timed out waiting for next line: uid=" + mUidStr 251 + " cmd=" + COMMAND_TO_STRING[cmd] + " procState=" + procState 252 + " capability=" + capability; 253 Log.d(TAG, msg); 254 throw new IllegalStateException(msg); 255 } 256 try { 257 mPendingLines.wait(waitUntil - now); 258 } catch (InterruptedException e) { 259 Thread.currentThread().interrupt(); 260 } 261 } 262 String[] res = mPendingLines.remove(0); 263 if (res[0].startsWith("#")) { 264 Log.d(TAG, "Note: " + res[0]); 265 } else { 266 Log.v(TAG, "LINE: " + Arrays.toString(res)); 267 return res; 268 } 269 } 270 } 271 } 272 finish()273 public void finish() { 274 synchronized (mPendingLines) { 275 mStopping = true; 276 } 277 mWritePrinter.println("q"); 278 try { 279 mWriteStream.close(); 280 } catch (IOException e) { 281 } 282 try { 283 mReadStream.close(); 284 } catch (IOException e) { 285 } 286 } 287 288 final class ReaderThread extends Thread { 289 String mLastReadLine; 290 291 @Override run()292 public void run() { 293 String[] line; 294 try { 295 while ((line = readNextLine()) != null) { 296 boolean comment = line.length == 1 && line[0].startsWith("#"); 297 if (!comment) { 298 if (line.length < 2) { 299 Log.d(TAG, "Skipping too short: " + mLastReadLine); 300 continue; 301 } 302 if (!line[0].equals(mUidStr)) { 303 Log.d(TAG, "Skipping ignored uid: " + mLastReadLine); 304 continue; 305 } 306 } 307 //Log.d(TAG, "Enqueueing: " + mLastReadLine); 308 synchronized (mPendingLines) { 309 if (mStopping) { 310 return; 311 } 312 mPendingLines.add(line); 313 mPendingLines.notifyAll(); 314 } 315 } 316 } catch (IOException e) { 317 Log.w(TAG, "Failed reading", e); 318 } 319 } 320 readNextLine()321 String[] readNextLine() throws IOException { 322 mLastReadLine = mReadReader.readLine(); 323 if (mLastReadLine == null) { 324 return null; 325 } 326 if (mLastReadLine.startsWith("#")) { 327 return new String[] { mLastReadLine }; 328 } 329 return mSpaceSplitter.split(mLastReadLine); 330 } 331 } 332 } 333