| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.documentsui; |
| |
| import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; |
| |
| import static org.junit.Assume.assumeNotNull; |
| |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.Instrumentation; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.os.Bundle; |
| import android.os.SystemClock; |
| import android.provider.DocumentsContract; |
| import android.util.Log; |
| |
| import androidx.test.uiautomator.UiDevice; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| public class FilesAppPerfTest { |
| private static final String TAG = "FilesAppPerfTest"; |
| |
| // Keys used to report metrics to APCT. |
| private static final String KEY_FILES_COLD_START_PERFORMANCE_MEDIAN = |
| "files-cold-start-performance-median"; |
| private static final String KEY_FILES_WARM_START_PERFORMANCE_MEDIAN = |
| "files-warm-start-performance-median"; |
| |
| private static final int NUM_MEASUREMENTS = 10; |
| private static final long REMOVAL_TIMEOUT_MS = 3000; |
| private static final long TIMEOUT_INTERVAL_MS = 200; |
| |
| private Instrumentation mInstrumentation; |
| private Context mContext; |
| private LauncherActivity mLauncherActivity; |
| private ActivityInfo mDocumentsUiActivityInfo; |
| |
| @Before |
| public void setUp() { |
| mInstrumentation = getInstrumentation(); |
| mContext = mInstrumentation.getContext(); |
| final ResolveInfo info = mContext.getPackageManager().resolveActivity( |
| LauncherActivity.OPEN_DOCUMENT_INTENT, PackageManager.ResolveInfoFlags.of(0)); |
| assumeNotNull(info); |
| mDocumentsUiActivityInfo = info.activityInfo; |
| mLauncherActivity = (LauncherActivity) mInstrumentation.startActivitySync( |
| new Intent(mContext, LauncherActivity.class).addFlags( |
| Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION)); |
| } |
| |
| @After |
| public void tearDown() { |
| mLauncherActivity.finishAndRemoveTask(); |
| } |
| |
| @Test |
| public void testFilesColdStartPerformance() throws Exception { |
| runFilesStartPerformanceTest(true); |
| } |
| |
| @Test |
| public void testFilesWarmStartPerformance() throws Exception { |
| runFilesStartPerformanceTest(false); |
| } |
| |
| public void runFilesStartPerformanceTest(boolean cold) throws Exception { |
| final String documentsUiPackageName = mDocumentsUiActivityInfo.packageName; |
| String[] providerPackageNames = null; |
| if (cold) { |
| providerPackageNames = getDocumentsProviderPackageNames(); |
| } |
| final ActivityManager am = mContext.getSystemService(ActivityManager.class); |
| long[] measurements = new long[NUM_MEASUREMENTS]; |
| for (int i = 0; i < NUM_MEASUREMENTS; i++) { |
| if (cold) { |
| // Kill all providers, as well as DocumentsUI to measure a cold start. |
| for (String pkgName : providerPackageNames) { |
| // Use kill-bg to avoid affecting other important services. |
| Log.i(TAG, "killBackgroundProcesses " + pkgName); |
| am.killBackgroundProcesses(pkgName); |
| } |
| Log.i(TAG, "forceStopPackage " + documentsUiPackageName); |
| am.forceStopPackage(documentsUiPackageName); |
| // Wait for any closing animations to finish. |
| mInstrumentation.getUiAutomation().syncInputTransactions(); |
| } |
| |
| measurements[i] = mLauncherActivity.startAndWaitDocumentsUi(); |
| // The DocumentUi will finish automatically according to the request code for testing, |
| // so wait until it is completely removed to avoid affecting next iteration. |
| waitUntilDocumentsUiActivityRemoved(); |
| } |
| |
| reportMetrics(cold ? KEY_FILES_COLD_START_PERFORMANCE_MEDIAN |
| : KEY_FILES_WARM_START_PERFORMANCE_MEDIAN, measurements); |
| } |
| |
| private void reportMetrics(String key, long[] measurements) { |
| final Bundle status = new Bundle(); |
| Arrays.sort(measurements); |
| final long median = measurements[NUM_MEASUREMENTS / 2 - 1]; |
| status.putDouble(key + "(ms)", median); |
| |
| mInstrumentation.sendStatus(Activity.RESULT_OK, status); |
| } |
| |
| private String[] getDocumentsProviderPackageNames() { |
| final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE); |
| final List<ResolveInfo> providers = mContext.getPackageManager() |
| .queryIntentContentProviders(intent, PackageManager.ResolveInfoFlags.of(0)); |
| final String[] pkgNames = new String[providers.size()]; |
| for (int i = 0; i < providers.size(); i++) { |
| pkgNames[i] = providers.get(i).providerInfo.packageName; |
| } |
| return pkgNames; |
| } |
| |
| private void waitUntilDocumentsUiActivityRemoved() { |
| final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation); |
| final String classPattern = new ComponentName(mDocumentsUiActivityInfo.packageName, |
| mDocumentsUiActivityInfo.name).flattenToShortString(); |
| final long startTime = SystemClock.uptimeMillis(); |
| while (SystemClock.uptimeMillis() - startTime <= REMOVAL_TIMEOUT_MS) { |
| SystemClock.sleep(TIMEOUT_INTERVAL_MS); |
| final String windowTokenDump; |
| try { |
| windowTokenDump = uiDevice.executeShellCommand("dumpsys window tokens"); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| if (!windowTokenDump.contains(classPattern)) { |
| return; |
| } |
| } |
| Log.i(TAG, "Removal timeout of " + classPattern); |
| } |
| } |