Home | History | Annotate | Download | only in tools
      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 
     96     public WatchUidRunner(Instrumentation instrumentation, int uid) {
     97         this(instrumentation, uid, 5*1000);
     98     }
     99 
    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 
    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 
    130     public void expect(int cmd, String procState) {
    131         expect(cmd, procState, mDefaultWaitTime);
    132     }
    133 
    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 
    154     public void waitFor(int cmd, String procState) {
    155         waitFor(cmd, procState, mDefaultWaitTime);
    156     }
    157 
    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 
    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 
    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                     return res;
    215                 }
    216             }
    217         }
    218     }
    219 
    220     public void finish() {
    221         synchronized (mPendingLines) {
    222             mStopping = true;
    223         }
    224         mWritePrinter.println("q");
    225         try {
    226             mWriteStream.close();
    227         } catch (IOException e) {
    228         }
    229         try {
    230             mReadStream.close();
    231         } catch (IOException e) {
    232         }
    233     }
    234 
    235     final class ReaderThread extends Thread {
    236         String mLastReadLine;
    237 
    238         @Override
    239         public void run() {
    240             String[] line;
    241             try {
    242                 while ((line = readNextLine()) != null) {
    243                     boolean comment = line.length == 1 && line[0].startsWith("#");
    244                     if (!comment) {
    245                         if (line.length < 2) {
    246                             Log.d(TAG, "Skipping too short: " + mLastReadLine);
    247                             continue;
    248                         }
    249                         if (!line[0].equals(mUidStr)) {
    250                             Log.d(TAG, "Skipping ignored uid: " + mLastReadLine);
    251                             continue;
    252                         }
    253                     }
    254                     //Log.d(TAG, "Enqueueing: " + mLastReadLine);
    255                     synchronized (mPendingLines) {
    256                         if (mStopping) {
    257                             return;
    258                         }
    259                         mPendingLines.add(line);
    260                         mPendingLines.notifyAll();
    261                     }
    262                 }
    263             } catch (IOException e) {
    264                 Log.w(TAG, "Failed reading", e);
    265             }
    266         }
    267 
    268         String[] readNextLine() throws IOException {
    269             mLastReadLine = mReadReader.readLine();
    270             if (mLastReadLine == null) {
    271                 return null;
    272             }
    273             if (mLastReadLine.startsWith("#")) {
    274                 return new String[] { mLastReadLine };
    275             }
    276             return mSpaceSplitter.split(mLastReadLine);
    277         }
    278     }
    279 }
    280