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 ACTIVITYLIST = "activitylist"; 54 private static final String LAUNCHCOUNT = "launchcount"; 55 private static final String RECORDTRACE = "recordtrace"; 56 private static final String ATRACE_START = "atrace --async_start am view gfx"; 57 private static final String ATRACE_DUMP = "atrace --async_dump"; 58 private static final String ATRACE_STOP = "atrace --async_stop"; 59 private static final String FORCE_STOP = "am force-stop "; 60 61 private Context mContext; 62 private Bundle mResult; 63 private String mTargetPackageName; 64 private int mLaunchCount; 65 private String mCustomActivityList; 66 private PackageInfo mPackageInfo; 67 private boolean mRecordTrace = true; 68 private List<String> mActivityList; 69 70 /** 71 * {@inheritDoc} 72 */ 73 @Override setUp()74 public void setUp() throws Exception { 75 super.setUp(); 76 mContext = getInstrumentation().getTargetContext(); 77 assertNotNull("Failed to get context", mContext); 78 Bundle args = InstrumentationRegistry.getArguments(); 79 assertNotNull("Unable to get the args", args); 80 mTargetPackageName = args.getString(TARGETPACKAGE); 81 assertNotNull("Target package name not set", mTargetPackageName); 82 mCustomActivityList = args.getString(ACTIVITYLIST); 83 if (mCustomActivityList == null || mCustomActivityList.isEmpty()) { 84 // Get full list of activities from the target package 85 mActivityList = getActivityList(""); 86 } else { 87 // Get only the user defined list of activities from the target package 88 mActivityList = getActivityList(mCustomActivityList); 89 } 90 assertTrue("Activity List is empty", (mActivityList.size() > 0)); 91 mLaunchCount = Integer.parseInt(args.getString(LAUNCHCOUNT)); 92 assertTrue("Invalid Launch Count", mLaunchCount > 0); 93 if (args.getString(RECORDTRACE) != null 94 && args.getString(RECORDTRACE).equalsIgnoreCase("false")) { 95 mRecordTrace = false; 96 } 97 mResult = new Bundle(); 98 } 99 100 @MediumTest testAppLaunchPerformance()101 public void testAppLaunchPerformance() throws Exception { 102 assertTrue("Cannot write in External File", isExternalStorageWritable()); 103 File root = Environment.getExternalStorageDirectory(); 104 assertNotNull("Unable to get the root of the external storage", root); 105 File logsDir = new File(root, "atrace_logs"); 106 assertTrue("Unable to create the directory to store atrace logs", logsDir.mkdir()); 107 for (int count = 0; count < mLaunchCount; count++) { 108 for (String activityName : mActivityList) { 109 ComponentName cn = new ComponentName(mTargetPackageName, 110 activityName); 111 Intent intent = new Intent(Intent.ACTION_MAIN); 112 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 113 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 114 intent.setComponent(cn); 115 116 // Start the atrace 117 if (mRecordTrace) { 118 assertNotNull( 119 "Unable to start atrace async", 120 getInstrumentation().getUiAutomation() 121 .executeShellCommand(ATRACE_START)); 122 // Sleep for 10 secs to make sure atrace command is started 123 Thread.sleep(10 * 1000); 124 } 125 126 // Launch the activity 127 mContext.startActivity(intent); 128 Thread.sleep(5 * 1000); 129 130 // Dump atrace info and write it to file 131 if (mRecordTrace) { 132 int processId = getProcessId(mTargetPackageName); 133 assertTrue("Not able to retrive the process id for the package:" 134 + mTargetPackageName, processId > 0); 135 String fileName = String.format("%s-%d-%d", activityName, count, processId); 136 ParcelFileDescriptor parcelFile = 137 getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_DUMP); 138 assertNotNull("Unable to get the File descriptor to standard out", 139 parcelFile); 140 InputStream inputStream = new FileInputStream(parcelFile.getFileDescriptor()); 141 File file = new File(logsDir, fileName); 142 FileOutputStream outputStream = new FileOutputStream(file); 143 try { 144 byte[] buffer = new byte[1024]; 145 int length; 146 while ((length = inputStream.read(buffer)) > 0) { 147 outputStream.write(buffer, 0, length); 148 } 149 } catch (IOException e) { 150 Log.w(TAG, "Error writing atrace info to file", e); 151 } 152 inputStream.close(); 153 outputStream.close(); 154 155 // Stop the atrace 156 assertNotNull( 157 "Unable to stop the atrace", 158 getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_STOP)); 159 160 // To keep track of the activity name,list of atrace file name 161 registerTraceFileNames(activityName, fileName); 162 } 163 assertNotNull("Unable to stop recent activity launched", 164 getInstrumentation().getUiAutomation().executeShellCommand( 165 FORCE_STOP + mTargetPackageName)); 166 Thread.sleep(5 * 1000); 167 } 168 } 169 getInstrumentation().sendStatus(0, mResult); 170 } 171 172 /** 173 * Method to check if external storage is writable 174 * @return 175 */ isExternalStorageWritable()176 public boolean isExternalStorageWritable() { 177 return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); 178 } 179 180 /** 181 * Method to get list of activities present in given target package If customActivityList is 182 * passed then include only those activities 183 * @return list of activity names 184 */ getActivityList(String customActivityList)185 private List<String> getActivityList(String customActivityList) { 186 mActivityList = new ArrayList<String>(); 187 try { 188 mPackageInfo = mContext.getPackageManager().getPackageInfo( 189 mTargetPackageName, 1); 190 assertNotNull("Unable to get the target package info", mPackageInfo); 191 } catch (NameNotFoundException e) { 192 fail(String.format("Target application: %s not found", mTargetPackageName)); 193 } 194 for (ActivityInfo activityInfo : mPackageInfo.activities) { 195 mActivityList.add(activityInfo.name); 196 } 197 if (!customActivityList.isEmpty()) { 198 List<String> finalActivityList = new 199 ArrayList<String>(); 200 String customList[] = customActivityList.split(","); 201 for (int count = 0; count < customList.length; count++) { 202 if (mActivityList.contains(customList[count])) { 203 finalActivityList.add(customList[count]); 204 } else { 205 fail(String.format("Activity: %s not present in the target package : %s ", 206 customList[count], mTargetPackageName)); 207 } 208 } 209 mActivityList = finalActivityList; 210 } 211 return mActivityList; 212 } 213 214 /** 215 * Method to retrieve process id from the activity manager 216 * @param processName 217 * @return 218 */ getProcessId(String processName)219 private int getProcessId(String processName) { 220 ActivityManager am = (ActivityManager) getInstrumentation() 221 .getContext().getSystemService(Context.ACTIVITY_SERVICE); 222 List<RunningAppProcessInfo> appsInfo = am.getRunningAppProcesses(); 223 assertNotNull("Unable to retrieve running apps info", appsInfo); 224 for (RunningAppProcessInfo appInfo : appsInfo) { 225 if (appInfo.processName.equals(processName)) { 226 return appInfo.pid; 227 } 228 } 229 return -1; 230 } 231 232 /** 233 * To add the process id to the result map 234 * @param activityNamereturn 235 * @return 236 * @throws IOException 237 */ registerTraceFileNames(String activityName, String absPath)238 private void registerTraceFileNames(String activityName, String absPath) 239 throws IOException { 240 if (mResult.containsKey(activityName)) { 241 String existingResult = (String) mResult.get(activityName); 242 mResult.putString(activityName, existingResult + "," + absPath); 243 } else { 244 mResult.putString(activityName, "" + absPath); 245 } 246 } 247 } 248 249