1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.launcher3.tapl;
18 
19 import static androidx.test.InstrumentationRegistry.getInstrumentation;
20 import static androidx.test.InstrumentationRegistry.getTargetContext;
21 
22 import android.app.Instrumentation;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ActivityInfo;
27 import android.content.pm.ResolveInfo;
28 import android.content.res.Resources;
29 import android.os.DropBoxManager;
30 import android.os.SystemClock;
31 import android.util.Log;
32 
33 import androidx.test.uiautomator.SearchCondition;
34 import androidx.test.uiautomator.UiDevice;
35 
36 import java.util.Date;
37 import java.util.List;
38 import java.util.Objects;
39 
40 public class TestHelpers {
41 
42     private static final String TAG = "Tapl";
43     private static Boolean sIsInLauncherProcess;
44 
isInLauncherProcess()45     public static boolean isInLauncherProcess() {
46         if (sIsInLauncherProcess == null) {
47             sIsInLauncherProcess = initIsInLauncherProcess();
48         }
49         return sIsInLauncherProcess;
50     }
51 
initIsInLauncherProcess()52     private static boolean initIsInLauncherProcess() {
53         ActivityInfo info = getLauncherInMyProcess();
54 
55         // If we are in the same process, we can instantiate the class name.
56         try {
57             Class launcherClazz = Class.forName("com.android.launcher3.Launcher");
58             return launcherClazz.isAssignableFrom(Class.forName(info.name));
59         } catch (Exception e) {
60             return false;
61         }
62     }
63 
getHomeIntentInPackage(Context context)64     public static Intent getHomeIntentInPackage(Context context) {
65         return new Intent(Intent.ACTION_MAIN)
66                 .addCategory(Intent.CATEGORY_HOME)
67                 .setPackage(context.getPackageName())
68                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
69     }
70 
getLauncherInMyProcess()71     public static ActivityInfo getLauncherInMyProcess() {
72         Instrumentation instrumentation = getInstrumentation();
73         if (instrumentation.getTargetContext() == null) {
74             return null;
75         }
76 
77         List<ResolveInfo> launchers = getTargetContext().getPackageManager()
78                 .queryIntentActivities(getHomeIntentInPackage(getTargetContext()), 0);
79         if (launchers.size() != 1) {
80             return null;
81         }
82         return launchers.get(0).activityInfo;
83     }
84 
getOverviewComponentName()85     public static ComponentName getOverviewComponentName() {
86         Resources res = Resources.getSystem();
87         int id = res.getIdentifier("config_recentsComponentName", "string", "android");
88         if (id != 0) {
89             return ComponentName.unflattenFromString(res.getString(id));
90         }
91         return new ComponentName("com.android.systemui",
92                 "com.android.systemui.recents.RecentsActivity");
93     }
94 
getOverviewPackageName()95     public static String getOverviewPackageName() {
96         return getOverviewComponentName().getPackageName();
97     }
98 
truncateCrash(String text, int maxLines)99     private static String truncateCrash(String text, int maxLines) {
100         String[] lines = text.split("\\r?\\n");
101         StringBuilder ret = new StringBuilder();
102         for (int i = 0; i < maxLines && i < lines.length; i++) {
103             ret.append(lines[i]);
104             ret.append('\n');
105         }
106         if (lines.length > maxLines) {
107             ret.append("... ");
108             ret.append(lines.length - maxLines);
109             ret.append(" more lines truncated ...\n");
110         }
111         return ret.toString();
112     }
113 
checkCrash(Context context, String label, long startTime)114     private static String checkCrash(Context context, String label, long startTime) {
115         DropBoxManager dropbox = Objects.requireNonNull(
116                 context.getSystemService(DropBoxManager.class));
117 
118         long timestamp = startTime;
119         DropBoxManager.Entry entry;
120         StringBuilder errorDetails = new StringBuilder();
121         while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
122             errorDetails.append("------------------------------\n");
123             timestamp = entry.getTimeMillis();
124             errorDetails.append(new Date(timestamp));
125             errorDetails.append(": ");
126             errorDetails.append(entry.getTag());
127             errorDetails.append(": ");
128             final String dropboxSnippet = entry.getText(4096);
129             if (dropboxSnippet != null) errorDetails.append(truncateCrash(dropboxSnippet, 40));
130             errorDetails.append("    ...\n");
131             entry.close();
132         }
133         return errorDetails.length() != 0 ? errorDetails.toString() : null;
134     }
135 
getSystemHealthMessage(Context context, long startTime)136     public static String getSystemHealthMessage(Context context, long startTime) {
137         try {
138             StringBuilder errors = new StringBuilder();
139 
140             final String[] labels = {
141                     "system_app_anr",
142                     "system_app_crash",
143                     "system_app_native_crash",
144                     "system_server_anr",
145                     "system_server_crash",
146                     "system_server_native_crash",
147                     "system_server_watchdog",
148             };
149 
150             for (String label : labels) {
151                 final String crash = checkCrash(context, label, startTime);
152                 if (crash != null) errors.append(crash);
153             }
154 
155             return errors.length() != 0
156                     ? "Current time: " + new Date(System.currentTimeMillis()) + "\n" + errors
157                     : null;
158         } catch (Exception e) {
159             return null;
160         }
161     }
162 
wait(SearchCondition<R> condition, long timeout)163     public static <R> R wait(SearchCondition<R> condition, long timeout) {
164         Log.d(TAG,
165                 "TestHelpers.wait, condition=" + timeout + ", time=" + SystemClock.uptimeMillis());
166         final R result = UiDevice.getInstance(getInstrumentation()).wait(condition, timeout);
167         Log.d(TAG, "TestHelpers.wait, result=" + result + ", time=" + SystemClock.uptimeMillis());
168         return result;
169     }
170 }
171