1 /* 2 * Copyright (C) 2015 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 com.android.performanceapp.tests; 18 19 import java.io.File; 20 import java.io.FileInputStream; 21 import java.io.FileOutputStream; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.util.ArrayList; 25 import java.util.List; 26 27 import android.app.ActivityManager; 28 import android.app.ActivityManager.RunningAppProcessInfo; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.pm.ActivityInfo; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.os.Bundle; 36 import android.os.Environment; 37 import android.os.ParcelFileDescriptor; 38 import android.support.test.InstrumentationRegistry; 39 import android.test.InstrumentationTestCase; 40 import android.test.suitebuilder.annotation.MediumTest; 41 import android.util.Log; 42 43 /** 44 * To test the App launch performance on the given target package for the list of activities. It 45 * launches the activities present in the target package the number of times in launch count or it 46 * launches only the activities mentioned in custom activity list the launch count times and returns 47 * the path to the files where the atrace logs are stored corresponding to each activity launch 48 */ 49 public class AppLaunchTests extends InstrumentationTestCase { 50 51 private static final String TAG = "AppLaunchInstrumentation"; 52 private static final String TARGETPACKAGE = "targetpackage"; 53 private static final String SIMPLEPERF_BIN = "simpleperf_bin"; 54 private static final String SIMPLEPERF_EVT = "simpleperf_event"; 55 private static final String SIMPLEPERF_DATA = "simpleperf_data"; 56 private static final String DISPATCHER = "dispatcher"; 57 private static final String ACTIVITYLIST = "activitylist"; 58 private static final String LAUNCHCOUNT = "launchcount"; 59 private static final String RECORDTRACE = "recordtrace"; 60 private static final String ATRACE_START = "atrace --async_start am view gfx"; 61 private static final String ATRACE_DUMP = "atrace --async_dump"; 62 private static final String ATRACE_STOP = "atrace --async_stop"; 63 private static final String FORCE_STOP = "am force-stop "; 64 65 private Context mContext; 66 private Bundle mResult; 67 private String mTargetPackageName; 68 private String mSimpleperfBin; 69 private String mSimpleperfEvt; 70 private String mSimpleperfDir; 71 private String mDispatcher; 72 private int mLaunchCount; 73 private String mCustomActivityList; 74 private PackageInfo mPackageInfo; 75 private boolean mRecordTrace = true; 76 private List<String> mActivityList; 77 78 /** 79 * {@inheritDoc} 80 */ 81 @Override setUp()82 public void setUp() throws Exception { 83 super.setUp(); 84 mContext = getInstrumentation().getTargetContext(); 85 assertNotNull("Failed to get context", mContext); 86 Bundle args = InstrumentationRegistry.getArguments(); 87 assertNotNull("Unable to get the args", args); 88 mTargetPackageName = args.getString(TARGETPACKAGE); 89 assertNotNull("Target package name not set", mTargetPackageName); 90 mSimpleperfEvt = args.getString(SIMPLEPERF_EVT); 91 mDispatcher = args.getString(DISPATCHER); 92 if (mDispatcher != null && !mDispatcher.isEmpty()) { 93 mSimpleperfBin = args.getString(SIMPLEPERF_BIN); 94 mSimpleperfEvt = args.getString(SIMPLEPERF_EVT); 95 mSimpleperfDir = args.getString(SIMPLEPERF_DATA); 96 } 97 mCustomActivityList = args.getString(ACTIVITYLIST); 98 if (mCustomActivityList == null || mCustomActivityList.isEmpty()) { 99 // Get full list of activities from the target package 100 mActivityList = getActivityList(""); 101 } else { 102 // Get only the user defined list of activities from the target package 103 mActivityList = getActivityList(mCustomActivityList); 104 } 105 assertTrue("Activity List is empty", (mActivityList.size() > 0)); 106 mLaunchCount = Integer.parseInt(args.getString(LAUNCHCOUNT)); 107 assertTrue("Invalid Launch Count", mLaunchCount > 0); 108 if (args.getString(RECORDTRACE) != null 109 && args.getString(RECORDTRACE).equalsIgnoreCase("false")) { 110 mRecordTrace = false; 111 } 112 mResult = new Bundle(); 113 } 114 115 @MediumTest testAppLaunchPerformance()116 public void testAppLaunchPerformance() throws Exception { 117 assertTrue("Cannot write in External File", isExternalStorageWritable()); 118 File root = Environment.getExternalStorageDirectory(); 119 assertNotNull("Unable to get the root of the external storage", root); 120 File logsDir = new File(root, "atrace_logs"); 121 assertTrue("Unable to create the directory to store atrace logs", logsDir.mkdir()); 122 if (mDispatcher != null && !mDispatcher.isEmpty()) { 123 if (mSimpleperfDir == null) 124 mSimpleperfDir = "/sdcard/perf_simpleperf/"; 125 File simpleperfDir = new File(mSimpleperfDir); 126 assertTrue("Unable to create the directory to store simpleperf data", simpleperfDir.mkdir()); 127 } 128 for (int count = 0; count < mLaunchCount; count++) { 129 for (String activityName : mActivityList) { 130 ComponentName cn = new ComponentName(mTargetPackageName, 131 mDispatcher != null ? mDispatcher : activityName); 132 Intent intent = new Intent(Intent.ACTION_MAIN); 133 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 134 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 135 intent.setComponent(cn); 136 137 if (mDispatcher != null) { 138 intent.putExtra("ACTIVITY_NAME", activityName); 139 intent.putExtra("SIMPLEPERF_DIR", mSimpleperfDir); 140 intent.putExtra("SIMPLEPERF_EVT", mSimpleperfEvt); 141 intent.putExtra("SIMPLEPERF_BIN", mSimpleperfBin); 142 } 143 144 // Start the atrace 145 if (mRecordTrace) { 146 assertNotNull( 147 "Unable to start atrace async", 148 getInstrumentation().getUiAutomation() 149 .executeShellCommand(ATRACE_START)); 150 // Sleep for 10 secs to make sure atrace command is started 151 Thread.sleep(10 * 1000); 152 } 153 154 // Launch the activity 155 mContext.startActivity(intent); 156 Thread.sleep(5 * 1000); 157 158 // Make sure we stops simpleperf 159 if (mDispatcher != null) { 160 try { 161 Runtime.getRuntime().exec("pkill -l SIGINT simpleperf").waitFor(); 162 } catch (Exception e) { 163 Log.v(TAG, "simpleperf throw exception"); 164 e.printStackTrace(); 165 } 166 } 167 168 // Dump atrace info and write it to file 169 if (mRecordTrace) { 170 int processId = getProcessId(mTargetPackageName); 171 assertTrue("Not able to retrive the process id for the package:" 172 + mTargetPackageName, processId > 0); 173 String fileName = String.format("%s-%d-%d", activityName, count, processId); 174 ParcelFileDescriptor parcelFile = 175 getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_DUMP); 176 assertNotNull("Unable to get the File descriptor to standard out", 177 parcelFile); 178 InputStream inputStream = new FileInputStream(parcelFile.getFileDescriptor()); 179 File file = new File(logsDir, fileName); 180 FileOutputStream outputStream = new FileOutputStream(file); 181 try { 182 byte[] buffer = new byte[1024]; 183 int length; 184 while ((length = inputStream.read(buffer)) > 0) { 185 outputStream.write(buffer, 0, length); 186 } 187 } catch (IOException e) { 188 Log.w(TAG, "Error writing atrace info to file", e); 189 } 190 inputStream.close(); 191 outputStream.close(); 192 193 // Stop the atrace 194 assertNotNull( 195 "Unable to stop the atrace", 196 getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_STOP)); 197 198 // To keep track of the activity name,list of atrace file name 199 registerTraceFileNames(activityName, fileName); 200 } 201 assertNotNull("Unable to stop recent activity launched", 202 getInstrumentation().getUiAutomation().executeShellCommand( 203 FORCE_STOP + mTargetPackageName)); 204 Thread.sleep(5 * 1000); 205 } 206 } 207 getInstrumentation().sendStatus(0, mResult); 208 } 209 210 /** 211 * Method to check if external storage is writable 212 * @return 213 */ isExternalStorageWritable()214 public boolean isExternalStorageWritable() { 215 return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); 216 } 217 218 /** 219 * Method to get list of activities present in given target package If customActivityList is 220 * passed then include only those activities 221 * @return list of activity names 222 */ getActivityList(String customActivityList)223 private List<String> getActivityList(String customActivityList) { 224 mActivityList = new ArrayList<String>(); 225 try { 226 mPackageInfo = mContext.getPackageManager().getPackageInfo( 227 mTargetPackageName, 1); 228 assertNotNull("Unable to get the target package info", mPackageInfo); 229 } catch (NameNotFoundException e) { 230 fail(String.format("Target application: %s not found", mTargetPackageName)); 231 } 232 for (ActivityInfo activityInfo : mPackageInfo.activities) { 233 mActivityList.add(activityInfo.name); 234 } 235 if (!customActivityList.isEmpty()) { 236 List<String> finalActivityList = new 237 ArrayList<String>(); 238 String customList[] = customActivityList.split(","); 239 for (int count = 0; count < customList.length; count++) { 240 if (mActivityList.contains(customList[count])) { 241 finalActivityList.add(customList[count]); 242 } else { 243 fail(String.format("Activity: %s not present in the target package : %s ", 244 customList[count], mTargetPackageName)); 245 } 246 } 247 mActivityList = finalActivityList; 248 } 249 return mActivityList; 250 } 251 252 /** 253 * Method to retrieve process id from the activity manager 254 * @param processName 255 * @return 256 */ getProcessId(String processName)257 private int getProcessId(String processName) { 258 ActivityManager am = (ActivityManager) getInstrumentation() 259 .getContext().getSystemService(Context.ACTIVITY_SERVICE); 260 List<RunningAppProcessInfo> appsInfo = am.getRunningAppProcesses(); 261 assertNotNull("Unable to retrieve running apps info", appsInfo); 262 for (RunningAppProcessInfo appInfo : appsInfo) { 263 if (appInfo.processName.equals(processName)) { 264 return appInfo.pid; 265 } 266 } 267 return -1; 268 } 269 270 /** 271 * To add the process id to the result map 272 * @param activityNamereturn 273 * @return 274 * @throws IOException 275 */ registerTraceFileNames(String activityName, String absPath)276 private void registerTraceFileNames(String activityName, String absPath) 277 throws IOException { 278 if (mResult.containsKey(activityName)) { 279 String existingResult = (String) mResult.get(activityName); 280 mResult.putString(activityName, existingResult + "," + absPath); 281 } else { 282 mResult.putString(activityName, "" + absPath); 283 } 284 } 285 } 286 287