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