├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── LICENSE
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── android
│ │ └── benchmark
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── android
│ │ │ └── benchmark
│ │ │ ├── api
│ │ │ ├── JankBenchAPI.java
│ │ │ └── JankBenchService.java
│ │ │ ├── app
│ │ │ ├── BenchmarkDashboardFragment.java
│ │ │ ├── BenchmarkListAdapter.java
│ │ │ ├── HomeActivity.java
│ │ │ ├── PerfTimeline.java
│ │ │ ├── RunLocalBenchmarksActivity.java
│ │ │ └── UiResultsFragment.java
│ │ │ ├── config
│ │ │ └── Constants.java
│ │ │ ├── models
│ │ │ ├── Entry.java
│ │ │ └── Result.java
│ │ │ ├── registry
│ │ │ ├── BenchmarkCategory.java
│ │ │ ├── BenchmarkGroup.java
│ │ │ └── BenchmarkRegistry.java
│ │ │ ├── results
│ │ │ ├── GlobalResultsStore.java
│ │ │ └── UiBenchmarkResult.java
│ │ │ ├── synthetic
│ │ │ ├── MemoryActivity.java
│ │ │ └── TestInterface.java
│ │ │ ├── ui
│ │ │ ├── BitmapUploadActivity.java
│ │ │ ├── EditTextInputActivity.java
│ │ │ ├── FullScreenOverdrawActivity.java
│ │ │ ├── ImageListViewScrollActivity.java
│ │ │ ├── ListActivityBase.java
│ │ │ ├── ListViewScrollActivity.java
│ │ │ ├── ShadowGridActivity.java
│ │ │ ├── TextScrollActivity.java
│ │ │ ├── Utils.java
│ │ │ └── automation
│ │ │ │ ├── Automator.java
│ │ │ │ ├── CollectorThread.java
│ │ │ │ ├── FrameTimingStats.java
│ │ │ │ └── Interaction.java
│ │ │ └── utils
│ │ │ └── CatFile.java
│ ├── jni
│ │ ├── Android.bp.converted
│ │ ├── Application.mk
│ │ ├── Bench.cpp
│ │ ├── Bench.h
│ │ ├── WorkerPool.cpp
│ │ ├── WorkerPool.h
│ │ └── test.cpp
│ └── res
│ │ ├── drawable
│ │ ├── ic_play.png
│ │ ├── img1.jpg
│ │ ├── img2.jpg
│ │ ├── img3.jpg
│ │ └── img4.jpg
│ │ ├── layout
│ │ ├── activity_bitmap_upload.xml
│ │ ├── activity_home.xml
│ │ ├── activity_list_fragment.xml
│ │ ├── activity_memory.xml
│ │ ├── activity_running_list.xml
│ │ ├── benchmark_list_group_row.xml
│ │ ├── benchmark_list_item.xml
│ │ ├── card_row.xml
│ │ ├── content_main.xml
│ │ ├── fragment_dashboard.xml
│ │ ├── fragment_ui_results_detail.xml
│ │ ├── image_scroll_list_item.xml
│ │ ├── results_list_item.xml
│ │ └── running_benchmark_list_item.xml
│ │ ├── menu
│ │ ├── menu_main.xml
│ │ └── menu_memory.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ids.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── benchmark.xml
│ └── test
│ └── java
│ └── com
│ └── android
│ └── benchmark
│ ├── ExampleUnitTest.java
│ └── api
│ └── JankBenchServiceTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── scripts
├── adbutil.py
├── collect.py
├── devices.py
├── external
│ ├── __init__.py
│ └── statistics.py
├── itr_collect.py
└── runall.py
└── settings.gradle
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: set up JDK 1.8
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.8
16 | - name: Assemble with Gradle
17 | run: ./gradlew assemble
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | /app/release
16 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 | defaultConfig {
7 | applicationId "com.android.benchmark"
8 | minSdkVersion 24
9 | targetSdkVersion 29
10 | versionCode 6
11 | versionName "2.2"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | repositories {
27 | maven { url 'https://jitpack.io' }
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 | implementation 'androidx.appcompat:appcompat:1.1.0'
33 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
34 | implementation 'com.google.android.material:material:1.0.0'
35 | testImplementation 'junit:junit:4.12'
36 |
37 | implementation 'androidx.cardview:cardview:1.0.0'
38 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
39 | implementation 'androidx.leanback:leanback:1.0.0'
40 | implementation 'org.apache.commons:commons-math3:3.6'
41 | implementation 'com.squareup.retrofit2:retrofit:2.1.0'
42 | implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
43 |
44 | def libsuVersion = '2.5.1'
45 | implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"
46 | implementation "com.github.topjohnwu.libsu:io:${libsuVersion}"
47 | implementation "com.github.topjohnwu.libsu:busybox:${libsuVersion}"
48 | }
49 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/android/benchmark/ApplicationTest.java:
--------------------------------------------------------------------------------
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 | /*
18 | * Copyright (C) 2015 The Android Open Source Project
19 | *
20 | * Licensed under the Apache License, Version 2.0 (the "License");
21 | * you may not use this file except in compliance with the License.
22 | * You may obtain a copy of the License at
23 | *
24 | * http://www.apache.org/licenses/LICENSE-2.0
25 | * Unless required by applicable law or agreed to in writing, software
26 | * distributed under the License is distributed on an "AS IS" BASIS,
27 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28 | * See the License for the specific language governing permissions and limitations under the
29 | * License.
30 | *
31 | */
32 |
33 | package com.android.benchmark;
34 |
35 | import android.app.Application;
36 | import android.test.ApplicationTestCase;
37 |
38 | /**
39 | * Testing Fundamentals
40 | */
41 | public class ApplicationTest extends ApplicationTestCase {
42 | public ApplicationTest() {
43 | super(Application.class);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/api/JankBenchAPI.java:
--------------------------------------------------------------------------------
1 | package com.android.benchmark.api;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 | import android.os.Build;
7 | import android.view.FrameMetrics;
8 |
9 | import com.android.benchmark.config.Constants;
10 | import com.android.benchmark.models.Entry;
11 | import com.android.benchmark.models.Result;
12 | import com.android.benchmark.results.GlobalResultsStore;
13 | import com.android.benchmark.results.UiBenchmarkResult;
14 | import com.topjohnwu.superuser.Shell;
15 |
16 | import java.io.IOException;
17 | import java.util.ArrayList;
18 | import java.util.HashMap;
19 | import java.util.List;
20 | import java.util.Map;
21 |
22 | import retrofit2.Call;
23 | import retrofit2.Response;
24 | import retrofit2.Retrofit;
25 | import retrofit2.converter.gson.GsonConverterFactory;
26 |
27 | public class JankBenchAPI {
28 | public static boolean uploadResults(Context context, String baseUrl) {
29 | boolean success= false;
30 | Entry entry = createEntry(context);
31 |
32 | try {
33 | success = upload(entry, baseUrl);
34 | } catch (IOException e) {
35 | e.printStackTrace();
36 | }
37 |
38 | return success;
39 | }
40 |
41 | private static boolean upload(Entry entry, String url) throws IOException {
42 | Retrofit retrofit = new Retrofit.Builder()
43 | .baseUrl(url)
44 | .addConverterFactory(GsonConverterFactory.create())
45 | .build();
46 |
47 | JankBenchService resource = retrofit.create(JankBenchService.class);
48 |
49 | Call call = resource.uploadEntry(entry);
50 | Response response = call.execute();
51 |
52 | return response.isSuccessful();
53 | }
54 |
55 | private static Entry createEntry(Context context) {
56 | int lastRunId = GlobalResultsStore.getInstance(context).getLastRunId();
57 | SQLiteDatabase db = GlobalResultsStore.getInstance(context).getReadableDatabase();
58 | int lastRunRefreshRate;
59 | try {
60 | lastRunRefreshRate = GlobalResultsStore.getInstance(context).loadRefreshRate(lastRunId, db);
61 | } finally {
62 | db.close();
63 | }
64 | HashMap resultsMap = GlobalResultsStore.getInstance(context).loadDetailedAggregatedResults(lastRunId);
65 |
66 | Entry entry = new Entry();
67 |
68 | entry.setRunId(lastRunId);
69 | entry.setBenchmarkVersion(Constants.BENCHMARK_VERSION);
70 | entry.setDeviceName(Build.DEVICE);
71 | entry.setDeviceModel(Build.MODEL);
72 | entry.setDeviceProduct(Build.PRODUCT);
73 | entry.setDeviceBoard(Build.BOARD);
74 | entry.setDeviceManufacturer(Build.MANUFACTURER);
75 | entry.setDeviceBrand(Build.BRAND);
76 | entry.setDeviceHardware(Build.HARDWARE);
77 | entry.setAndroidVersion(Build.VERSION.RELEASE);
78 | entry.setBuildType(Build.TYPE);
79 | entry.setBuildTime(String.valueOf(Build.TIME));
80 | entry.setFingerprint(Build.FINGERPRINT);
81 | entry.setRefreshRate(lastRunRefreshRate);
82 |
83 | String kernel_version = getKernelVersion();
84 | entry.setKernelVersion(kernel_version);
85 |
86 | List results = new ArrayList<>();
87 |
88 | for (Map.Entry resultEntry : resultsMap.entrySet()) {
89 | String testName = resultEntry.getKey();
90 | UiBenchmarkResult uiResult = resultEntry.getValue();
91 |
92 | Result result = new Result();
93 | result.setTestName(testName);
94 | result.setScore(uiResult.getScore());
95 | result.setJankPenalty(uiResult.getJankPenalty());
96 | result.setConsistencyBonus(uiResult.getConsistencyBonus());
97 | result.setJankPct(100 * uiResult.getNumJankFrames() / (double) uiResult.getTotalFrameCount());
98 | result.setBadFramePct(100 * uiResult.getNumBadFrames() / (double) uiResult.getTotalFrameCount());
99 | result.setTotalFrames(uiResult.getTotalFrameCount());
100 | result.setMsAvg(uiResult.getAverage(FrameMetrics.TOTAL_DURATION));
101 | result.setMs10thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 10));
102 | result.setMs20thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 20));
103 | result.setMs30thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 30));
104 | result.setMs40thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 40));
105 | result.setMs50thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 50));
106 | result.setMs60thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 60));
107 | result.setMs70thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 70));
108 | result.setMs80thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 80));
109 | result.setMs90thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 90));
110 | result.setMs95thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 95));
111 | result.setMs99thPctl(uiResult.getPercentile(FrameMetrics.TOTAL_DURATION, 99));
112 |
113 | results.add(result);
114 | }
115 |
116 | entry.setResults(results);
117 |
118 | return entry;
119 | }
120 |
121 | private static String getKernelVersion() {
122 | List unameOutput = Shell.sh("uname -a").exec().getOut();
123 | String kernel_version = unameOutput.size() == 0 ? null : unameOutput.get(0);
124 | return kernel_version;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/api/JankBenchService.java:
--------------------------------------------------------------------------------
1 | package com.android.benchmark.api;
2 |
3 | import com.android.benchmark.models.Entry;
4 |
5 | import retrofit2.Call;
6 | import retrofit2.http.Body;
7 | import retrofit2.http.POST;
8 |
9 | public interface JankBenchService {
10 | @POST("v1/results")
11 | Call uploadEntry(@Body Entry entry);
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/app/BenchmarkDashboardFragment.java:
--------------------------------------------------------------------------------
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 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and limitations under the
13 | * License.
14 | *
15 | */
16 |
17 | package com.android.benchmark.app;
18 |
19 | import androidx.fragment.app.Fragment;
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 |
25 | import com.android.benchmark.R;
26 |
27 | /**
28 | * Fragment for the Benchmark dashboard
29 | */
30 | public class BenchmarkDashboardFragment extends Fragment {
31 |
32 | public BenchmarkDashboardFragment() {
33 | }
34 |
35 | @Override
36 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
37 | Bundle savedInstanceState) {
38 | return inflater.inflate(R.layout.fragment_dashboard, container, false);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/app/BenchmarkListAdapter.java:
--------------------------------------------------------------------------------
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 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and limitations under the
13 | * License.
14 | *
15 | */
16 |
17 | package com.android.benchmark.app;
18 |
19 | import android.graphics.Typeface;
20 | import android.view.LayoutInflater;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 | import android.widget.BaseExpandableListAdapter;
24 | import android.widget.CheckBox;
25 | import android.widget.TextView;
26 |
27 | import com.android.benchmark.registry.BenchmarkGroup;
28 | import com.android.benchmark.registry.BenchmarkRegistry;
29 | import com.android.benchmark.R;
30 |
31 | /**
32 | *
33 | */
34 | public class BenchmarkListAdapter extends BaseExpandableListAdapter {
35 |
36 | private final LayoutInflater mInflater;
37 | private final BenchmarkRegistry mRegistry;
38 |
39 | BenchmarkListAdapter(LayoutInflater inflater,
40 | BenchmarkRegistry registry) {
41 | mInflater = inflater;
42 | mRegistry = registry;
43 | }
44 |
45 | @Override
46 | public int getGroupCount() {
47 | return mRegistry.getGroupCount();
48 | }
49 |
50 | @Override
51 | public int getChildrenCount(int groupPosition) {
52 | return mRegistry.getBenchmarkCount(groupPosition);
53 | }
54 |
55 | @Override
56 | public Object getGroup(int groupPosition) {
57 | return mRegistry.getBenchmarkGroup(groupPosition);
58 | }
59 |
60 | @Override
61 | public Object getChild(int groupPosition, int childPosition) {
62 | BenchmarkGroup benchmarkGroup = mRegistry.getBenchmarkGroup(groupPosition);
63 |
64 | if (benchmarkGroup != null) {
65 | return benchmarkGroup.getBenchmarks()[childPosition];
66 | }
67 |
68 | return null;
69 | }
70 |
71 | @Override
72 | public long getGroupId(int groupPosition) {
73 | return groupPosition;
74 | }
75 |
76 | @Override
77 | public long getChildId(int groupPosition, int childPosition) {
78 | return childPosition;
79 | }
80 |
81 | @Override
82 | public boolean hasStableIds() {
83 | return false;
84 | }
85 |
86 | @Override
87 | public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
88 | BenchmarkGroup group = (BenchmarkGroup) getGroup(groupPosition);
89 | if (convertView == null) {
90 | convertView = mInflater.inflate(R.layout.benchmark_list_group_row, null);
91 | }
92 |
93 | TextView title = (TextView) convertView.findViewById(R.id.group_name);
94 | title.setTypeface(null, Typeface.BOLD);
95 | title.setText(group.getTitle());
96 | return convertView;
97 | }
98 |
99 | @Override
100 | public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
101 | View convertView, ViewGroup parent) {
102 | BenchmarkGroup.Benchmark benchmark =
103 | (BenchmarkGroup.Benchmark) getChild(groupPosition, childPosition);
104 | if (convertView == null) {
105 | convertView = mInflater.inflate(R.layout.benchmark_list_item, null);
106 | }
107 |
108 | TextView name = (TextView) convertView.findViewById(R.id.benchmark_name);
109 | name.setText(benchmark.getName());
110 | CheckBox enabledBox = (CheckBox) convertView.findViewById(R.id.benchmark_enable_checkbox);
111 | enabledBox.setOnClickListener(benchmark);
112 | enabledBox.setChecked(benchmark.isEnabled());
113 |
114 | return convertView;
115 | }
116 |
117 | @Override
118 | public boolean isChildSelectable(int groupPosition, int childPosition) {
119 | return true;
120 | }
121 |
122 | public int getChildrenHeight() {
123 | // TODO
124 | return 1024;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/app/HomeActivity.java:
--------------------------------------------------------------------------------
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 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and limitations under the
13 | * License.
14 | *
15 | */
16 |
17 | package com.android.benchmark.app;
18 |
19 | import android.content.Intent;
20 | import android.net.Uri;
21 | import android.os.AsyncTask;
22 | import android.os.Build;
23 | import android.os.Bundle;
24 | import android.util.Log;
25 | import android.view.LayoutInflater;
26 | import android.view.Menu;
27 | import android.view.MenuItem;
28 | import android.view.View;
29 | import android.view.ViewGroup;
30 | import android.widget.Button;
31 | import android.widget.ExpandableListView;
32 | import android.widget.Toast;
33 |
34 | import androidx.appcompat.app.AppCompatActivity;
35 | import androidx.appcompat.widget.Toolbar;
36 |
37 | import com.android.benchmark.R;
38 | import com.android.benchmark.api.JankBenchAPI;
39 | import com.android.benchmark.config.Constants;
40 | import com.android.benchmark.registry.BenchmarkRegistry;
41 | import com.android.benchmark.results.GlobalResultsStore;
42 |
43 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
44 | import com.topjohnwu.superuser.Shell;
45 |
46 | import java.io.IOException;
47 | import java.util.LinkedList;
48 | import java.util.Queue;
49 |
50 | public class HomeActivity extends AppCompatActivity implements Button.OnClickListener {
51 |
52 | static {
53 | /* Shell.Config methods shall be called before any shell is created
54 | * This is the why in this example we call it in a static block
55 | * The followings are some examples, check Javadoc for more details */
56 | Shell.Config.setFlags(Shell.FLAG_REDIRECT_STDERR);
57 | Shell.Config.setTimeout(10);
58 | }
59 |
60 | private FloatingActionButton mStartButton;
61 | private BenchmarkRegistry mRegistry;
62 | private Queue mRunnableBenchmarks;
63 |
64 | @Override
65 | protected void onCreate(Bundle savedInstanceState) {
66 | super.onCreate(savedInstanceState);
67 | setContentView(R.layout.activity_home);
68 |
69 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
70 | setSupportActionBar(toolbar);
71 |
72 | mStartButton = (FloatingActionButton) findViewById(R.id.start_button);
73 | mStartButton.setActivated(true);
74 | mStartButton.setOnClickListener(this);
75 |
76 | mRegistry = new BenchmarkRegistry(this);
77 |
78 | mRunnableBenchmarks = new LinkedList<>();
79 |
80 | ExpandableListView listView = (ExpandableListView) findViewById(R.id.test_list);
81 | BenchmarkListAdapter adapter =
82 | new BenchmarkListAdapter(LayoutInflater.from(this), mRegistry);
83 | listView.setAdapter(adapter);
84 |
85 | adapter.notifyDataSetChanged();
86 | ViewGroup.LayoutParams layoutParams = listView.getLayoutParams();
87 | layoutParams.height = 2048;
88 | listView.setLayoutParams(layoutParams);
89 | listView.requestLayout();
90 | System.out.println(System.getProperties().stringPropertyNames());
91 | }
92 |
93 | @Override
94 | public boolean onCreateOptionsMenu(Menu menu) {
95 | // Inflate the menu; this adds items to the action bar if it is present.
96 | getMenuInflater().inflate(R.menu.menu_main, menu);
97 | return true;
98 | }
99 |
100 | @Override
101 | public boolean onOptionsItemSelected(MenuItem item) {
102 | // Handle action bar item clicks here. The action bar will
103 | // automatically handle clicks on the Home/Up button, so long
104 | // as you specify a parent activity in AndroidManifest.xml.
105 | int id = item.getItemId();
106 |
107 | //noinspection SimplifiableIfStatement
108 | if (id == R.id.action_settings) {
109 | new AsyncTask() {
110 | @Override
111 | protected Void doInBackground(Void... voids) {
112 | try {
113 | HomeActivity.this.runOnUiThread(new Runnable() {
114 | @Override
115 | public void run() {
116 | Toast.makeText(HomeActivity.this, "Exporting...", Toast.LENGTH_LONG).show();
117 | }
118 | });
119 | GlobalResultsStore.getInstance(HomeActivity.this).exportToCsv();
120 | } catch (IOException e) {
121 | e.printStackTrace();
122 | }
123 | return null;
124 | }
125 |
126 | @Override
127 | protected void onPostExecute(Void aVoid) {
128 | HomeActivity.this.runOnUiThread(new Runnable() {
129 | @Override
130 | public void run() {
131 | Toast.makeText(HomeActivity.this, "Done", Toast.LENGTH_LONG).show();
132 | }
133 | });
134 | }
135 | }.execute();
136 |
137 | return true;
138 | } else if (id == R.id.action_upload) {
139 |
140 | new AsyncTask() {
141 | boolean success;
142 |
143 | @Override
144 | protected Void doInBackground(Void... voids) {
145 | HomeActivity.this.runOnUiThread(new Runnable() {
146 | @Override
147 | public void run() {
148 | Toast.makeText(HomeActivity.this, "Uploading results...", Toast.LENGTH_LONG).show();
149 | }
150 | });
151 |
152 | success = JankBenchAPI.uploadResults(HomeActivity.this, Constants.BASE_URL);
153 |
154 | return null;
155 | }
156 |
157 | @Override
158 | protected void onPostExecute(Void aVoid) {
159 | HomeActivity.this.runOnUiThread(new Runnable() {
160 | @Override
161 | public void run() {
162 | Toast.makeText(HomeActivity.this, success ? "Upload succeeded" : "Upload failed", Toast.LENGTH_LONG).show();
163 | }
164 | });
165 | }
166 | }.execute();
167 |
168 | return true;
169 | } else if (id == R.id.action_view_results) {
170 | Uri webpage = Uri.parse("https://jankbenchx.vercel.app");
171 | Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
172 | if (intent.resolveActivity(getPackageManager()) != null) {
173 | startActivity(intent);
174 | }
175 | }
176 |
177 |
178 | return super.onOptionsItemSelected(item);
179 | }
180 |
181 | @Override
182 | public void onClick(View v) {
183 | final int groupCount = mRegistry.getGroupCount();
184 | for (int i = 0; i < groupCount; i++) {
185 |
186 | Intent intent = mRegistry.getBenchmarkGroup(i).getIntent();
187 | if (intent != null) {
188 | mRunnableBenchmarks.add(intent);
189 | }
190 | }
191 |
192 | handleNextBenchmark();
193 | }
194 |
195 | @Override
196 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
197 | super.onActivityResult(requestCode, resultCode, data);
198 | }
199 |
200 | private void handleNextBenchmark() {
201 | Intent nextIntent = mRunnableBenchmarks.peek();
202 | startActivityForResult(nextIntent, 0);
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/app/PerfTimeline.java:
--------------------------------------------------------------------------------
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.benchmark.app;
18 |
19 | import android.content.Context;
20 | import android.content.res.TypedArray;
21 | import android.graphics.*;
22 | import android.text.TextPaint;
23 | import android.util.AttributeSet;
24 | import android.view.View;
25 |
26 | import com.android.benchmark.R;
27 |
28 |
29 | /**
30 | * TODO: document your custom view class.
31 | */
32 | public class PerfTimeline extends View {
33 | private String mExampleString; // TODO: use a default from R.string...
34 | private int mExampleColor = Color.RED; // TODO: use a default from R.color...
35 | private float mExampleDimension = 300; // TODO: use a default from R.dimen...
36 |
37 | private TextPaint mTextPaint;
38 | private float mTextWidth;
39 | private float mTextHeight;
40 |
41 | private Paint mPaintBaseLow;
42 | private Paint mPaintBaseHigh;
43 | private Paint mPaintValue;
44 |
45 |
46 | public float[] mLinesLow;
47 | public float[] mLinesHigh;
48 | public float[] mLinesValue;
49 |
50 | public PerfTimeline(Context context) {
51 | super(context);
52 | init(null, 0);
53 | }
54 |
55 | public PerfTimeline(Context context, AttributeSet attrs) {
56 | super(context, attrs);
57 | init(attrs, 0);
58 | }
59 |
60 | public PerfTimeline(Context context, AttributeSet attrs, int defStyle) {
61 | super(context, attrs, defStyle);
62 | init(attrs, defStyle);
63 | }
64 |
65 | private void init(AttributeSet attrs, int defStyle) {
66 | // Load attributes
67 | final TypedArray a = getContext().obtainStyledAttributes(
68 | attrs, R.styleable.PerfTimeline, defStyle, 0);
69 |
70 | mExampleString = "xx";//a.getString(R.styleable.PerfTimeline_exampleString, "xx");
71 | mExampleColor = a.getColor(R.styleable.PerfTimeline_exampleColor, mExampleColor);
72 | // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with
73 | // values that should fall on pixel boundaries.
74 | mExampleDimension = a.getDimension(
75 | R.styleable.PerfTimeline_exampleDimension,
76 | mExampleDimension);
77 |
78 | a.recycle();
79 |
80 | // Set up a default TextPaint object
81 | mTextPaint = new TextPaint();
82 | mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
83 | mTextPaint.setTextAlign(Paint.Align.LEFT);
84 |
85 | // Update TextPaint and text measurements from attributes
86 | invalidateTextPaintAndMeasurements();
87 |
88 | mPaintBaseLow = new Paint(Paint.ANTI_ALIAS_FLAG);
89 | mPaintBaseLow.setStyle(Paint.Style.FILL);
90 | mPaintBaseLow.setColor(0xff000000);
91 |
92 | mPaintBaseHigh = new Paint(Paint.ANTI_ALIAS_FLAG);
93 | mPaintBaseHigh.setStyle(Paint.Style.FILL);
94 | mPaintBaseHigh.setColor(0x7f7f7f7f);
95 |
96 | mPaintValue = new Paint(Paint.ANTI_ALIAS_FLAG);
97 | mPaintValue.setStyle(Paint.Style.FILL);
98 | mPaintValue.setColor(0x7fff0000);
99 |
100 | }
101 |
102 | private void invalidateTextPaintAndMeasurements() {
103 | mTextPaint.setTextSize(mExampleDimension);
104 | mTextPaint.setColor(mExampleColor);
105 | mTextWidth = mTextPaint.measureText(mExampleString);
106 |
107 | Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
108 | mTextHeight = fontMetrics.bottom;
109 | }
110 |
111 | @Override
112 | protected void onDraw(Canvas canvas) {
113 | super.onDraw(canvas);
114 |
115 | // TODO: consider storing these as member variables to reduce
116 | // allocations per draw cycle.
117 | int paddingLeft = getPaddingLeft();
118 | int paddingTop = getPaddingTop();
119 | int paddingRight = getPaddingRight();
120 | int paddingBottom = getPaddingBottom();
121 |
122 | int contentWidth = getWidth() - paddingLeft - paddingRight;
123 | int contentHeight = getHeight() - paddingTop - paddingBottom;
124 |
125 | // Draw the text.
126 | //canvas.drawText(mExampleString,
127 | // paddingLeft + (contentWidth - mTextWidth) / 2,
128 | // paddingTop + (contentHeight + mTextHeight) / 2,
129 | // mTextPaint);
130 |
131 |
132 |
133 |
134 | // Draw the shadow
135 | //RectF rf = new RectF(10.f, 10.f, 100.f, 100.f);
136 | //canvas.drawOval(rf, mShadowPaint);
137 |
138 | if (mLinesLow != null) {
139 | canvas.drawLines(mLinesLow, mPaintBaseLow);
140 | }
141 | if (mLinesHigh != null) {
142 | canvas.drawLines(mLinesHigh, mPaintBaseHigh);
143 | }
144 | if (mLinesValue != null) {
145 | canvas.drawLines(mLinesValue, mPaintValue);
146 | }
147 |
148 |
149 | /*
150 | // Draw the pie slices
151 | for (int i = 0; i < mData.size(); ++i) {
152 | Item it = mData.get(i);
153 | mPiePaint.setShader(it.mShader);
154 | canvas.drawArc(mBounds,
155 | 360 - it.mEndAngle,
156 | it.mEndAngle - it.mStartAngle,
157 | true, mPiePaint);
158 | }
159 | */
160 | // Draw the pointer
161 | //canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
162 | //canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
163 | }
164 |
165 | /**
166 | * Gets the example string attribute value.
167 | *
168 | * @return The example string attribute value.
169 | */
170 | public String getExampleString() {
171 | return mExampleString;
172 | }
173 |
174 | /**
175 | * Sets the view's example string attribute value. In the example view, this string
176 | * is the text to draw.
177 | *
178 | * @param exampleString The example string attribute value to use.
179 | */
180 | public void setExampleString(String exampleString) {
181 | mExampleString = exampleString;
182 | invalidateTextPaintAndMeasurements();
183 | }
184 |
185 | /**
186 | * Gets the example color attribute value.
187 | *
188 | * @return The example color attribute value.
189 | */
190 | public int getExampleColor() {
191 | return mExampleColor;
192 | }
193 |
194 | /**
195 | * Sets the view's example color attribute value. In the example view, this color
196 | * is the font color.
197 | *
198 | * @param exampleColor The example color attribute value to use.
199 | */
200 | public void setExampleColor(int exampleColor) {
201 | mExampleColor = exampleColor;
202 | invalidateTextPaintAndMeasurements();
203 | }
204 |
205 | /**
206 | * Gets the example dimension attribute value.
207 | *
208 | * @return The example dimension attribute value.
209 | */
210 | public float getExampleDimension() {
211 | return mExampleDimension;
212 | }
213 |
214 | /**
215 | * Sets the view's example dimension attribute value. In the example view, this dimension
216 | * is the font size.
217 | *
218 | * @param exampleDimension The example dimension attribute value to use.
219 | */
220 | public void setExampleDimension(float exampleDimension) {
221 | mExampleDimension = exampleDimension;
222 | invalidateTextPaintAndMeasurements();
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/benchmark/app/UiResultsFragment.java:
--------------------------------------------------------------------------------
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.benchmark.app;
18 |
19 | import android.annotation.TargetApi;
20 | import android.os.AsyncTask;
21 | import android.os.Bundle;
22 | import androidx.annotation.Nullable;
23 | import androidx.fragment.app.ListFragment;
24 | import android.util.Log;
25 | import android.view.FrameMetrics;
26 | import android.widget.SimpleAdapter;
27 |
28 | import com.android.benchmark.R;
29 | import com.android.benchmark.registry.BenchmarkGroup;
30 | import com.android.benchmark.registry.BenchmarkRegistry;
31 | import com.android.benchmark.results.GlobalResultsStore;
32 | import com.android.benchmark.results.UiBenchmarkResult;
33 |
34 | import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
35 |
36 | import java.io.FileWriter;
37 | import java.io.IOException;
38 | import java.net.URI;
39 | import java.text.DecimalFormat;
40 | import java.util.ArrayList;
41 | import java.util.HashMap;
42 | import java.util.Map;
43 |
44 | @TargetApi(24)
45 | public class UiResultsFragment extends ListFragment {
46 | private static final String TAG = "UiResultsFragment";
47 | private static final int NUM_FIELDS = 20;
48 |
49 | private ArrayList mResults = new ArrayList<>();
50 |
51 | private AsyncTask>> mLoadScoresTask =
52 | new AsyncTask>>() {
53 | @Override
54 | protected ArrayList