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 mInstrumentation = instrumentation; 103 mUid = uid; 104 mUidStr = Integer.toString(uid); 105 mDefaultWaitTime = defaultWaitTime; 106 mSpaceSplitter = Pattern.compile("\\s+"); 107 ParcelFileDescriptor[] pfds = instrumentation.getUiAutomation().executeShellCommandRw( 108 "am watch-uids --oom " + uid); 109 mReadFd = pfds[0]; 110 mReadStream = new ParcelFileDescriptor.AutoCloseInputStream(mReadFd); 111 mReadReader = new BufferedReader(new InputStreamReader(mReadStream)); 112 mWriteFd = pfds[1]; 113 mWriteStream = new ParcelFileDescriptor.AutoCloseOutputStream(mWriteFd); 114 mWritePrinter = new PrintWriter(new BufferedOutputStream(mWriteStream)); 115 // Executing a shell command is asynchronous but we can't proceed further with the test 116 // until the 'watch-uids' cmd is executed. 117 waitUntilUidObserverReady(); 118 mReaderThread = new ReaderThread(); 119 mReaderThread.start(); 120 } 121 waitUntilUidObserverReady()122 private void waitUntilUidObserverReady() { 123 try { 124 final String line = mReadReader.readLine(); 125 assertTrue("Unexpected output: " + line, line.startsWith("Watching uid states")); 126 } catch (IOException e) { 127 fail("Error occurred " + e); 128 } 129 } 130 expect(int cmd, String procState)131 public void expect(int cmd, String procState) { 132 expect(cmd, procState, mDefaultWaitTime); 133 } 134 expect(int cmd, String procState, long timeout)135 public void expect(int cmd, String procState, long timeout) { 136 long waitUntil = SystemClock.uptimeMillis() + timeout; 137 String[] line = waitForNextLine(waitUntil, cmd, procState, 0); 138 if (!COMMAND_TO_STRING[cmd].equals(line[1])) { 139 String msg = "Expected cmd " + COMMAND_TO_STRING[cmd] 140 + " uid " + mUid + " but next report was " + Arrays.toString(line); 141 Log.d(TAG, msg); 142 logRemainingLines(); 143 throw new IllegalStateException(msg); 144 } 145 if (procState != null && (line.length < 3 || !procState.equals(line[2]))) { 146 String msg = "Expected procstate " + procState 147 + " uid " + mUid + " but next report was " + Arrays.toString(line); 148 Log.d(TAG, msg); 149 logRemainingLines(); 150 throw new IllegalStateException(msg); 151 } 152 Log.d(TAG, "Got expected: " + Arrays.toString(line)); 153 } 154 waitFor(int cmd)155 public void waitFor(int cmd) { 156 waitFor(cmd, null, null, mDefaultWaitTime); 157 } 158 waitFor(int cmd, long timeout)159 public void waitFor(int cmd, long timeout) { 160 waitFor(cmd, null, null, timeout); 161 } 162 waitFor(int cmd, String procState)163 public void waitFor(int cmd, String procState) { 164 waitFor(cmd, procState, null, mDefaultWaitTime); 165 } 166 waitFor(int cmd, String procState, Integer capability)167 public void waitFor(int cmd, String procState, Integer capability) { 168 waitFor(cmd, procState, capability, mDefaultWaitTime); 169 } 170 waitFor(int cmd, String procState, long timeout)171 public void waitFor(int cmd, String procState, long timeout) { 172 waitFor(cmd, procState, null, timeout); 173 } 174 waitFor(int cmd, String procState, Integer capability, long timeout)175 public void waitFor(int cmd, String procState, Integer capability, long timeout) { 176 long waitUntil = SystemClock.uptimeMillis() + timeout; 177 while (true) { 178 String[] line = waitForNextLine(waitUntil, cmd, procState, capability); 179 if (COMMAND_TO_STRING[cmd].equals(line[1])) { 180 if (procState == null && capability == null) { 181 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 182 return; 183 } 184 if (cmd == CMD_PROCSTATE) { 185 if (procState != null && capability != null) { 186 if (procState.equals(line[2]) && capability.toString().equals(line[6])) { 187 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 188 return; 189 } 190 } else if (procState != null) { 191 if (procState.equals(line[2])) { 192 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 193 return; 194 } 195 } else if (capability != null) { 196 if (capability.toString().equals(line[6])) { 197 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 198 return; 199 } 200 } 201 } else { 202 if (procState != null 203 && procState.equals(line[2])) { 204 Log.d(TAG, "Waited for: " + Arrays.toString(line)); 205 return; 206 } 207 } 208 Log.d(TAG, "Skipping because procstate not " + procState + ": " 209 + Arrays.toString(line)); 210 } else { 211 Log.d(TAG, "Skipping because not " + COMMAND_TO_STRING[cmd] + ": " 212 + Arrays.toString(line)); 213 } 214 } 215 } 216 logRemainingLines()217 void logRemainingLines() { 218 synchronized (mPendingLines) { 219 while (mPendingLines.size() > 0) { 220 String[] res = mPendingLines.remove(0); 221 if (res[0].startsWith("#")) { 222 Log.d(TAG, "Remaining: " + res[0]); 223 } else { 224 Log.d(TAG, "Remaining: " + Arrays.toString(res)); 225 } 226 } 227 } 228 } 229 waitForNextLine(long waitUntil, int cmd, String procState, Integer capability)230 String[] waitForNextLine(long waitUntil, int cmd, String procState, Integer capability) { 231 synchronized (mPendingLines) { 232 while (true) { 233 while (mPendingLines.size() == 0) { 234 long now = SystemClock.uptimeMillis(); 235 if (now >= waitUntil) { 236 String msg = "Timed out waiting for next line: uid=" + mUidStr 237 + " cmd=" + COMMAND_TO_STRING[cmd] + " procState=" + procState 238 + " capability=" + capability; 239 Log.d(TAG, msg); 240 throw new IllegalStateException(msg); 241 } 242 try { 243 mPendingLines.wait(waitUntil - now); 244 } catch (InterruptedException e) { 245 } 246 } 247 String[] res = mPendingLines.remove(0); 248 if (res[0].startsWith("#")) { 249 Log.d(TAG, "Note: " + res[0]); 250 } else { 251 Log.v(TAG, "LINE: " + Arrays.toString(res)); 252 return res; 253 } 254 } 255 } 256 } 257 finish()258 public void finish() { 259 synchronized (mPendingLines) { 260 mStopping = true; 261 } 262 mWritePrinter.println("q"); 263 try { 264 mWriteStream.close(); 265 } catch (IOException e) { 266 } 267 try { 268 mReadStream.close(); 269 } catch (IOException e) { 270 } 271 } 272 273 final class ReaderThread extends Thread { 274 String mLastReadLine; 275 276 @Override run()277 public void run() { 278 String[] line; 279 try { 280 while ((line = readNextLine()) != null) { 281 boolean comment = line.length == 1 && line[0].startsWith("#"); 282 if (!comment) { 283 if (line.length < 2) { 284 Log.d(TAG, "Skipping too short: " + mLastReadLine); 285 continue; 286 } 287 if (!line[0].equals(mUidStr)) { 288 Log.d(TAG, "Skipping ignored uid: " + mLastReadLine); 289 continue; 290 } 291 } 292 //Log.d(TAG, "Enqueueing: " + mLastReadLine); 293 synchronized (mPendingLines) { 294 if (mStopping) { 295 return; 296 } 297 mPendingLines.add(line); 298 mPendingLines.notifyAll(); 299 } 300 } 301 } catch (IOException e) { 302 Log.w(TAG, "Failed reading", e); 303 } 304 } 305 readNextLine()306 String[] readNextLine() throws IOException { 307 mLastReadLine = mReadReader.readLine(); 308 if (mLastReadLine == null) { 309 return null; 310 } 311 if (mLastReadLine.startsWith("#")) { 312 return new String[] { mLastReadLine }; 313 } 314 return mSpaceSplitter.split(mLastReadLine); 315 } 316 } 317 } 318