Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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.server.cts;
     18 
     19 import com.android.tradefed.device.CollectingOutputReceiver;
     20 import com.android.tradefed.device.DeviceNotAvailableException;
     21 import com.android.tradefed.device.ITestDevice;
     22 
     23 import java.awt.Rectangle;
     24 import java.lang.Integer;
     25 import java.lang.String;
     26 import java.util.ArrayList;
     27 import java.util.Collections;
     28 import java.util.LinkedList;
     29 import java.util.List;
     30 
     31 import java.util.regex.Pattern;
     32 import java.util.regex.Matcher;
     33 
     34 import static android.server.cts.ActivityManagerTestBase.HOME_STACK_ID;
     35 import static android.server.cts.StateLogger.log;
     36 import static android.server.cts.StateLogger.logE;
     37 
     38 class ActivityManagerState {
     39     private static final String DUMPSYS_ACTIVITY_ACTIVITIES = "dumpsys activity activities";
     40 
     41     // Copied from ActivityRecord.java
     42     private static final int APPLICATION_ACTIVITY_TYPE = 0;
     43     private static final int HOME_ACTIVITY_TYPE = 1;
     44     private static final int RECENTS_ACTIVITY_TYPE = 2;
     45 
     46     private final Pattern mDisplayIdPattern = Pattern.compile("Display #(\\d+)");
     47     private final Pattern mStackIdPattern = Pattern.compile("Stack #(\\d+)\\:");
     48     private final Pattern mFocusedActivityPattern =
     49             Pattern.compile("mFocusedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
     50     private final Pattern mFocusedStackPattern =
     51             Pattern.compile("mFocusedStack=ActivityStack\\{(.+) stackId=(\\d+), (.+)\\}(.+)");
     52 
     53     private final Pattern[] mExtractStackExitPatterns =
     54             { mStackIdPattern, mFocusedActivityPattern, mFocusedStackPattern};
     55 
     56     // Stacks in z-order with the top most at the front of the list.
     57     private final List<ActivityStack> mStacks = new ArrayList();
     58     private int mFocusedStackId = -1;
     59     private String mFocusedActivityRecord = null;
     60     private final List<String> mResumedActivities = new ArrayList();
     61     private final LinkedList<String> mSysDump = new LinkedList();
     62 
     63     void computeState(ITestDevice device) throws DeviceNotAvailableException {
     64         // It is possible the system is in the middle of transition to the right state when we get
     65         // the dump. We try a few times to get the information we need before giving up.
     66         int retriesLeft = 3;
     67         boolean retry = false;
     68         String dump = null;
     69 
     70         log("==============================");
     71         log("     ActivityManagerState     ");
     72         log("==============================");
     73 
     74         do {
     75             if (retry) {
     76                 log("***Incomplete AM state. Retrying...");
     77                 // Wait half a second between retries for activity manager to finish transitioning.
     78                 try {
     79                     Thread.sleep(500);
     80                 } catch (InterruptedException e) {
     81                     log(e.toString());
     82                     // Well I guess we are not waiting...
     83                 }
     84             }
     85 
     86             final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
     87             device.executeShellCommand(DUMPSYS_ACTIVITY_ACTIVITIES, outputReceiver);
     88             dump = outputReceiver.getOutput();
     89             parseSysDump(dump);
     90 
     91             retry = mStacks.isEmpty() || mFocusedStackId == -1 || mFocusedActivityRecord == null
     92                     || mResumedActivities.isEmpty();
     93         } while (retry && retriesLeft-- > 0);
     94 
     95         if (retry) {
     96             log(dump);
     97         }
     98 
     99         if (mStacks.isEmpty()) {
    100             logE("No stacks found...");
    101         }
    102         if (mFocusedStackId == -1) {
    103             logE("No focused stack found...");
    104         }
    105         if (mFocusedActivityRecord == null) {
    106             logE("No focused activity found...");
    107         }
    108         if (mResumedActivities.isEmpty()) {
    109             logE("No resumed activities found...");
    110         }
    111     }
    112 
    113     private void parseSysDump(String sysDump) {
    114         reset();
    115 
    116         Collections.addAll(mSysDump, sysDump.split("\\n"));
    117 
    118         int currentDisplayId = 0;
    119         while (!mSysDump.isEmpty()) {
    120             final ActivityStack stack = ActivityStack.create(mSysDump, mStackIdPattern,
    121                     mExtractStackExitPatterns, currentDisplayId);
    122 
    123             if (stack != null) {
    124                 mStacks.add(stack);
    125                 if (stack.mResumedActivity != null) {
    126                     mResumedActivities.add(stack.mResumedActivity);
    127                 }
    128                 continue;
    129             }
    130 
    131             final String line = mSysDump.pop().trim();
    132 
    133             Matcher matcher = mFocusedStackPattern.matcher(line);
    134             if (matcher.matches()) {
    135                 log(line);
    136                 final String stackId = matcher.group(2);
    137                 log(stackId);
    138                 mFocusedStackId = Integer.parseInt(stackId);
    139                 continue;
    140             }
    141 
    142             matcher = mFocusedActivityPattern.matcher(line);
    143             if (matcher.matches()) {
    144                 log(line);
    145                 mFocusedActivityRecord = matcher.group(3);
    146                 log(mFocusedActivityRecord);
    147                 continue;
    148             }
    149 
    150             matcher = mDisplayIdPattern.matcher(line);
    151             if (matcher.matches()) {
    152                 log(line);
    153                 final String displayId = matcher.group(2);
    154                 log(displayId);
    155                 currentDisplayId = Integer.parseInt(displayId);
    156             }
    157         }
    158     }
    159 
    160     private void reset() {
    161         mStacks.clear();
    162         mFocusedStackId = -1;
    163         mFocusedActivityRecord = null;
    164         mResumedActivities.clear();
    165         mSysDump.clear();
    166     }
    167 
    168     int getFrontStackId() {
    169         return mStacks.get(0).mStackId;
    170     }
    171 
    172     int getFocusedStackId() {
    173         return mFocusedStackId;
    174     }
    175 
    176     String getFocusedActivity() {
    177         return mFocusedActivityRecord;
    178     }
    179 
    180     String getResumedActivity() {
    181         return mResumedActivities.get(0);
    182     }
    183 
    184     int getResumedActivitiesCount() {
    185         return mResumedActivities.size();
    186     }
    187 
    188     boolean containsStack(int stackId) {
    189         return getStackById(stackId) != null;
    190     }
    191 
    192     ActivityStack getStackById(int stackId) {
    193         for (ActivityStack stack : mStacks) {
    194             if (stackId == stack.mStackId) {
    195                 return stack;
    196             }
    197         }
    198         return null;
    199     }
    200 
    201     List<ActivityStack> getStacks() {
    202         return new ArrayList(mStacks);
    203     }
    204 
    205     int getStackCount() {
    206         return mStacks.size();
    207     }
    208 
    209     boolean isActivityVisible(String activityName) {
    210         for (ActivityStack stack : mStacks) {
    211             for (ActivityTask task : stack.mTasks) {
    212                for (Activity activity : task.mActivities) {
    213                    if (activity.name.equals(activityName)) {
    214                        return activity.visible;
    215                    }
    216                }
    217             }
    218         }
    219         return false;
    220     }
    221 
    222     boolean isHomeActivityVisible() {
    223         final Activity homeActivity = getHomeActivity();
    224         return homeActivity != null && homeActivity.visible;
    225     }
    226 
    227     private Activity getHomeActivity() {
    228         for (ActivityStack stack : mStacks) {
    229             if (stack.mStackId != HOME_STACK_ID) {
    230                 continue;
    231             }
    232 
    233             for (ActivityTask task : stack.mTasks) {
    234                 if (task.mTaskType != HOME_ACTIVITY_TYPE) {
    235                     continue;
    236                 }
    237                 return task.mActivities.get(task.mActivities.size() - 1);
    238             }
    239 
    240             return null;
    241         }
    242         return null;
    243     }
    244 
    245     ActivityTask getTaskByActivityName(String activityName) {
    246         return getTaskByActivityName(activityName, -1);
    247     }
    248 
    249     ActivityTask getTaskByActivityName(String activityName, int stackId) {
    250         String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
    251         for (ActivityStack stack : mStacks) {
    252             if (stackId == -1 || stackId == stack.mStackId) {
    253                 for (ActivityTask task : stack.mTasks) {
    254                     for (Activity activity : task.mActivities) {
    255                         if (activity.name.equals(fullName)) {
    256                             return task;
    257                         }
    258                     }
    259                 }
    260             }
    261         }
    262         return null;
    263     }
    264 
    265     static class ActivityStack extends ActivityContainer {
    266 
    267         private static final Pattern TASK_ID_PATTERN = Pattern.compile("Task id #(\\d+)");
    268         private static final Pattern RESUMED_ACTIVITY_PATTERN = Pattern.compile(
    269                 "mResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
    270 
    271         int mDisplayId;
    272         int mStackId;
    273         String mResumedActivity;
    274         ArrayList<ActivityTask> mTasks = new ArrayList();
    275 
    276         private ActivityStack() {
    277         }
    278 
    279         static ActivityStack create(LinkedList<String> dump, Pattern stackIdPattern,
    280                                     Pattern[] exitPatterns, int displayId) {
    281             final String line = dump.peek().trim();
    282 
    283             final Matcher matcher = stackIdPattern.matcher(line);
    284             if (!matcher.matches()) {
    285                 // Not a stack.
    286                 return null;
    287             }
    288             // For the stack Id line we just read.
    289             dump.pop();
    290 
    291             final ActivityStack stack = new ActivityStack();
    292             stack.mDisplayId = displayId;
    293             log(line);
    294             final String stackId = matcher.group(1);
    295             log(stackId);
    296             stack.mStackId = Integer.parseInt(stackId);
    297             stack.extract(dump, exitPatterns);
    298             return stack;
    299         }
    300 
    301         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
    302 
    303             final List<Pattern> taskExitPatterns = new ArrayList();
    304             Collections.addAll(taskExitPatterns, exitPatterns);
    305             taskExitPatterns.add(TASK_ID_PATTERN);
    306             taskExitPatterns.add(RESUMED_ACTIVITY_PATTERN);
    307             final Pattern[] taskExitPatternsArray =
    308                     taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
    309 
    310             while (!doneExtracting(dump, exitPatterns)) {
    311                 final ActivityTask task =
    312                         ActivityTask.create(dump, TASK_ID_PATTERN, taskExitPatternsArray);
    313 
    314                 if (task != null) {
    315                     mTasks.add(task);
    316                     continue;
    317                 }
    318 
    319                 final String line = dump.pop().trim();
    320 
    321                 if (extractFullscreen(line)) {
    322                     continue;
    323                 }
    324 
    325                 if (extractBounds(line)) {
    326                     continue;
    327                 }
    328 
    329                 Matcher matcher = RESUMED_ACTIVITY_PATTERN.matcher(line);
    330                 if (matcher.matches()) {
    331                     log(line);
    332                     mResumedActivity = matcher.group(3);
    333                     log(mResumedActivity);
    334                     continue;
    335                 }
    336             }
    337         }
    338 
    339         List<ActivityTask> getTasks() {
    340             return new ArrayList(mTasks);
    341         }
    342 
    343         ActivityTask getTask(int taskId) {
    344             for (ActivityTask task : mTasks) {
    345                 if (taskId == task.mTaskId) {
    346                     return task;
    347                 }
    348             }
    349             return null;
    350         }
    351     }
    352 
    353     static class ActivityTask extends ActivityContainer {
    354         private static final Pattern TASK_RECORD_PATTERN = Pattern.compile("\\* TaskRecord\\"
    355                 + "{(\\S+) #(\\d+) (\\S+)=(\\S+) U=(\\d+) StackId=(\\d+) sz=(\\d+)\\}");
    356 
    357         private static final Pattern LAST_NON_FULLSCREEN_BOUNDS_PATTERN = Pattern.compile(
    358                 "mLastNonFullscreenBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)");
    359 
    360         private static final Pattern ORIG_ACTIVITY_PATTERN = Pattern.compile("origActivity=(\\S+)");
    361         private static final Pattern REAL_ACTIVITY_PATTERN = Pattern.compile("realActivity=(\\S+)");
    362 
    363         private static final Pattern ACTIVITY_NAME_PATTERN = Pattern.compile(
    364                 "\\* Hist #(\\d+)\\: ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}");
    365 
    366         private static final Pattern TASK_TYPE_PATTERN = Pattern.compile("autoRemoveRecents=(\\S+) "
    367                 + "isPersistable=(\\S+) numFullscreen=(\\d+) taskType=(\\d+) "
    368                 + "mTaskToReturnTo=(\\d+)");
    369 
    370         int mTaskId;
    371         int mStackId;
    372         Rectangle mLastNonFullscreenBounds;
    373         String mRealActivity;
    374         String mOrigActivity;
    375         ArrayList<Activity> mActivities = new ArrayList();
    376         int mTaskType = -1;
    377         int mReturnToType = -1;
    378 
    379         private ActivityTask() {
    380         }
    381 
    382         static ActivityTask create(
    383                 LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
    384             final String line = dump.peek().trim();
    385 
    386             final Matcher matcher = taskIdPattern.matcher(line);
    387             if (!matcher.matches()) {
    388                 // Not a task.
    389                 return null;
    390             }
    391             // For the task Id line we just read.
    392             dump.pop();
    393 
    394             final ActivityTask task = new ActivityTask();
    395             log(line);
    396             final String taskId = matcher.group(1);
    397             log(taskId);
    398             task.mTaskId = Integer.parseInt(taskId);
    399             task.extract(dump, exitPatterns);
    400             return task;
    401         }
    402 
    403         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
    404             final List<Pattern> activityExitPatterns = new ArrayList();
    405             Collections.addAll(activityExitPatterns, exitPatterns);
    406             activityExitPatterns.add(ACTIVITY_NAME_PATTERN);
    407             final Pattern[] activityExitPatternsArray =
    408                     activityExitPatterns.toArray(new Pattern[activityExitPatterns.size()]);
    409 
    410             while (!doneExtracting(dump, exitPatterns)) {
    411                 final Activity activity =
    412                         Activity.create(dump, ACTIVITY_NAME_PATTERN, activityExitPatternsArray);
    413 
    414                 if (activity != null) {
    415                     mActivities.add(activity);
    416                     continue;
    417                 }
    418 
    419                 final String line = dump.pop().trim();
    420 
    421                 if (extractFullscreen(line)) {
    422                     continue;
    423                 }
    424 
    425                 if (extractBounds(line)) {
    426                     continue;
    427                 }
    428 
    429                 if (extractMinimalSize(line)) {
    430                     continue;
    431                 }
    432 
    433                 Matcher matcher = TASK_RECORD_PATTERN.matcher(line);
    434                 if (matcher.matches()) {
    435                     log(line);
    436                     final String stackId = matcher.group(6);
    437                     mStackId = Integer.valueOf(stackId);
    438                     log(stackId);
    439                     continue;
    440                 }
    441 
    442                 matcher = LAST_NON_FULLSCREEN_BOUNDS_PATTERN.matcher(line);
    443                 if (matcher.matches()) {
    444                     log(line);
    445                     mLastNonFullscreenBounds = extractBounds(matcher);
    446                 }
    447 
    448                 matcher = REAL_ACTIVITY_PATTERN.matcher(line);
    449                 if (matcher.matches()) {
    450                     if (mRealActivity == null) {
    451                         log(line);
    452                         mRealActivity = matcher.group(1);
    453                         log(mRealActivity);
    454                     }
    455                     continue;
    456                 }
    457 
    458                 matcher = ORIG_ACTIVITY_PATTERN.matcher(line);
    459                 if (matcher.matches()) {
    460                     if (mOrigActivity == null) {
    461                         log(line);
    462                         mOrigActivity = matcher.group(1);
    463                         log(mOrigActivity);
    464                     }
    465                     continue;
    466                 }
    467 
    468                 matcher = TASK_TYPE_PATTERN.matcher(line);
    469                 if (matcher.matches()) {
    470                     log(line);
    471                     mTaskType = Integer.valueOf(matcher.group(4));
    472                     mReturnToType = Integer.valueOf(matcher.group(5));
    473                     continue;
    474                 }
    475             }
    476         }
    477     }
    478 
    479     static class Activity {
    480         private static final Pattern VISIBILITY_PATTERN = Pattern.compile("keysPaused=(\\S+) "
    481                 + "inHistory=(\\S+) visible=(\\S+) sleeping=(\\S+) idle=(\\S+) "
    482                 + "mStartingWindowState=(\\S+)");
    483         private static final Pattern FRONT_OF_TASK_PATTERN = Pattern.compile("frontOfTask=(\\S+) "
    484                 + "task=TaskRecord\\{(\\S+) #(\\d+) A=(\\S+) U=(\\d+) StackId=(\\d+) sz=(\\d+)\\}");
    485 
    486         String name;
    487         boolean visible;
    488         boolean frontOfTask;
    489 
    490         private Activity() {
    491         }
    492 
    493         static Activity create(
    494                 LinkedList<String> dump, Pattern activityNamePattern, Pattern[] exitPatterns) {
    495             final String line = dump.peek().trim();
    496 
    497             final Matcher matcher = activityNamePattern.matcher(line);
    498             if (!matcher.matches()) {
    499                 // Not an activity.
    500                 return null;
    501             }
    502             // For the activity name line we just read.
    503             dump.pop();
    504 
    505             final Activity activity = new Activity();
    506             log(line);
    507             activity.name = matcher.group(4);
    508             log(activity.name);
    509             activity.extract(dump, exitPatterns);
    510             return activity;
    511         }
    512 
    513         private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
    514 
    515             while (!doneExtracting(dump, exitPatterns)) {
    516                 final String line = dump.pop().trim();
    517 
    518                 Matcher matcher = VISIBILITY_PATTERN.matcher(line);
    519                 if (matcher.matches()) {
    520                     log(line);
    521                     final String visibleString = matcher.group(3);
    522                     visible = Boolean.valueOf(visibleString);
    523                     log(visibleString);
    524                     continue;
    525                 }
    526 
    527                 matcher = FRONT_OF_TASK_PATTERN.matcher(line);
    528                 if (matcher.matches()) {
    529                     log(line);
    530                     final String frontOfTaskString = matcher.group(1);
    531                     frontOfTask = Boolean.valueOf(frontOfTaskString);
    532                     log(frontOfTaskString);
    533                     continue;
    534                 }
    535             }
    536         }
    537     }
    538 
    539     static abstract class ActivityContainer {
    540         protected static final Pattern FULLSCREEN_PATTERN = Pattern.compile("mFullscreen=(\\S+)");
    541         protected static final Pattern BOUNDS_PATTERN =
    542                 Pattern.compile("mBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)");
    543         protected static final Pattern MIN_WIDTH_PATTERN =
    544                 Pattern.compile("mMinWidth=(\\d+)");
    545         protected static final Pattern MIN_HEIGHT_PATTERN =
    546                 Pattern.compile("mMinHeight=(\\d+)");
    547 
    548         protected boolean mFullscreen;
    549         protected Rectangle mBounds;
    550         protected int mMinWidth = -1;
    551         protected int mMinHeight = -1;
    552 
    553         boolean extractFullscreen(String line) {
    554             final Matcher matcher = FULLSCREEN_PATTERN.matcher(line);
    555             if (!matcher.matches()) {
    556                 return false;
    557             }
    558             log(line);
    559             final String fullscreen = matcher.group(1);
    560             log(fullscreen);
    561             mFullscreen = Boolean.valueOf(fullscreen);
    562             return true;
    563         }
    564 
    565         boolean extractBounds(String line) {
    566             final Matcher matcher = BOUNDS_PATTERN.matcher(line);
    567             if (!matcher.matches()) {
    568                 return false;
    569             }
    570             log(line);
    571             mBounds = extractBounds(matcher);
    572             return true;
    573         }
    574 
    575         static Rectangle extractBounds(Matcher matcher) {
    576             final int left = Integer.valueOf(matcher.group(1));
    577             final int top = Integer.valueOf(matcher.group(2));
    578             final int right = Integer.valueOf(matcher.group(3));
    579             final int bottom = Integer.valueOf(matcher.group(4));
    580             final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
    581 
    582             log(rect.toString());
    583             return rect;
    584         }
    585 
    586         boolean extractMinimalSize(String line) {
    587             final Matcher minWidthMatcher = MIN_WIDTH_PATTERN.matcher(line);
    588             final Matcher minHeightMatcher = MIN_HEIGHT_PATTERN.matcher(line);
    589 
    590             if (minWidthMatcher.matches()) {
    591                 log(line);
    592                 mMinWidth = Integer.valueOf(minWidthMatcher.group(1));
    593             } else if (minHeightMatcher.matches()) {
    594                 log(line);
    595                 mMinHeight = Integer.valueOf(minHeightMatcher.group(1));
    596             } else {
    597                 return false;
    598             }
    599             return true;
    600         }
    601 
    602         Rectangle getBounds() {
    603             return mBounds;
    604         }
    605 
    606         boolean isFullscreen() {
    607             return mFullscreen;
    608         }
    609 
    610         int getMinWidth() {
    611             return mMinWidth;
    612         }
    613 
    614         int getMinHeight() {
    615             return mMinHeight;
    616         }
    617     }
    618 
    619     static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
    620         if (dump.isEmpty()) {
    621             return true;
    622         }
    623         final String line = dump.peek().trim();
    624 
    625         for (Pattern pattern : exitPatterns) {
    626             if (pattern.matcher(line).matches()) {
    627                 return true;
    628             }
    629         }
    630         return false;
    631     }
    632 }
    633