diff options
Diffstat (limited to 'tests')
187 files changed, 11965 insertions, 192 deletions
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt new file mode 100644 index 000000000000..77e0e90623fa --- /dev/null +++ b/tests/ActivityManagerPerfTests/README.txt @@ -0,0 +1,34 @@ +ActivityManagerPerfTests + +Performance tests for various ActivityManager components, e.g. Services, Broadcasts + +Command to run tests (not working yet, atest seems buggy) +* atest .../frameworks/base/tests/ActivityManagerPerfTests +* m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \ + adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \ + adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \ + adb shell am instrument -w \ + com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner + +Overview +* The numbers we are trying to measure are end-to-end numbers + * For example, the time it takes from sending an Intent to start a Service + to the time the Service runs its callbacks +* System.nanoTime() is monotonic and consistent between processes, so we use that for measuring time +* To make sure the test app is running, we start an Activity +* If the test app is involved, it will measure the time and send it back to the instrumentation test + * The time is sent back through a Binder interface in the Intent + * Each sent time is tagged with an id since there can be multiple events that send back a time + * For example, one is sent when the Activity is started, and another could be sent when a + Broadcast is received + +Structure +* tests + * Instrumentation test which runs the various performance tests and reports the results + +* test-app + * Target package which contains the Services, BroadcastReceivers, etc. to test against + * Sends the time it measures back to the test package + +* utils + * Utilities that both the instrumentation test and test app can use diff --git a/tests/ActivityManagerPerfTests/test-app/Android.mk b/tests/ActivityManagerPerfTests/test-app/Android.mk new file mode 100644 index 000000000000..b0a5db7a3134 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/Android.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2018 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + ActivityManagerPerfTestsUtils + +LOCAL_PACKAGE_NAME := ActivityManagerPerfTestsTestApp + +include $(BUILD_PACKAGE) diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml new file mode 100644 index 000000000000..23a151c0350a --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.perftests.amteststestapp"> + <uses-sdk + android:minSdkVersion="21" + android:targetSdkVersion="27" /> + <application android:name=".TestApplication"> + <activity android:name=".TestActivity" android:exported="true"/> + <provider + android:authorities="com.android.frameworks.perftests.amteststestapp" + android:name=".TestContentProvider" + android:exported="true" /> + <receiver + android:name=".TestBroadcastReceiver" + android:exported="true"> + <intent-filter> + <action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </receiver> + <service + android:name=".TestService" + android:exported="true" /> + </application> +</manifest> diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java new file mode 100644 index 000000000000..4e7bb4cc101e --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.amteststestapp; + +import android.app.Activity; +import android.os.Looper; + +import com.android.frameworks.perftests.am.util.Constants; +import com.android.frameworks.perftests.am.util.Utils; + +public class TestActivity extends Activity { + @Override + protected void onResume() { + super.onResume(); + Looper.myQueue().addIdleHandler(() -> { + Utils.sendTime(getIntent(), Constants.TYPE_TARGET_PACKAGE_START); + return false; + }); + } +} diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java new file mode 100644 index 000000000000..e26ffcde7c07 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestApplication.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.amteststestapp; + +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; + +import com.android.frameworks.perftests.am.util.Constants; +import com.android.frameworks.perftests.am.util.Utils; + +public class TestApplication extends Application { + private static final String TAG = TestApplication.class.getSimpleName(); + + @Override + public void onCreate() { + createRegisteredReceiver(); + + super.onCreate(); + } + + // Create registered BroadcastReceiver + private void createRegisteredReceiver() { + BroadcastReceiver registered = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.i(TAG, "RegisteredReceiver.onReceive"); + Utils.sendTime(intent, Constants.TYPE_BROADCAST_RECEIVE); + } + }; + IntentFilter intentFilter = new IntentFilter(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE); + registerReceiver(registered, intentFilter); + } +} diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java new file mode 100644 index 000000000000..336bf9d1fab6 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.amteststestapp; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.android.frameworks.perftests.am.util.Constants; +import com.android.frameworks.perftests.am.util.Utils; + +public class TestBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Utils.sendTime(intent, Constants.TYPE_BROADCAST_RECEIVE); + } +} diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java new file mode 100644 index 000000000000..0940578e4b28 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.amteststestapp; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class TestContentProvider extends ContentProvider { + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public boolean onCreate() { + return false; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java new file mode 100644 index 000000000000..b6534fc87ae3 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.amteststestapp; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; + +import com.android.frameworks.perftests.am.util.Constants; +import com.android.frameworks.perftests.am.util.Utils; + +public class TestService extends Service { + @Override + public IBinder onBind(Intent intent) { + return new Binder(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Utils.sendTime(intent, Constants.TYPE_SERVICE_START); + return super.onStartCommand(intent, flags, startId); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/Android.mk b/tests/ActivityManagerPerfTests/tests/Android.mk new file mode 100644 index 000000000000..daf603d6a63f --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/Android.mk @@ -0,0 +1,33 @@ +# Copyright (C) 2018 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + apct-perftests-utils \ + ActivityManagerPerfTestsUtils + +LOCAL_PACKAGE_NAME := ActivityManagerPerfTests + +# For android.permission.FORCE_STOP_PACKAGES permission +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml new file mode 100644 index 000000000000..a1ab33a96248 --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.perftests.amtests"> + <uses-sdk + android:minSdkVersion="21" + android:targetSdkVersion="27" /> + <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.perftests.amtests"/> +</manifest> diff --git a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml new file mode 100644 index 000000000000..ffb5404d7d94 --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> +<configuration description="Runs ActivityManager Performance Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="ActivityManagerPerfTests.apk"/> + <option name="test-file-name" value="ActivityManagerPerfTestsTestApp.apk"/> + <option name="cleanup-apks" value="true"/> + </target_preparer> + + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="ActivityManagerPerfTests"/> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.frameworks.perftests.amtests"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration>
\ No newline at end of file diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java new file mode 100644 index 000000000000..cf175e00b217 --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.tests; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.perftests.utils.ManualBenchmarkState; +import android.perftests.utils.PerfManualStatusReporter; +import android.support.test.InstrumentationRegistry; + +import com.android.frameworks.perftests.am.util.TargetPackageUtils; +import com.android.frameworks.perftests.am.util.TimeReceiver; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.LongSupplier; + +public class BasePerfTest { + private static final String TAG = BasePerfTest.class.getSimpleName(); + private static final long AWAIT_SERVICE_CONNECT_MS = 2000; + + private TimeReceiver mTimeReceiver; + + @Rule + public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter(); + + protected Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + mTimeReceiver = new TimeReceiver(); + } + + @After + public void tearDown() { + TargetPackageUtils.killTargetPackage(mContext); + } + + protected void addReceivedTimeNs(String type) { + mTimeReceiver.addTimeForTypeToQueue(type, System.nanoTime()); + } + + protected Intent createServiceIntent() { + final Intent intent = new Intent(); + intent.setClassName(TargetPackageUtils.PACKAGE_NAME, + TargetPackageUtils.SERVICE_NAME); + putTimeReceiverBinderExtra(intent); + return intent; + } + + protected ServiceConnection bindAndWaitForConnectedService() { + return bindAndWaitForConnectedService(0); + } + + protected ServiceConnection bindAndWaitForConnectedService(int flags) { + CountDownLatch countDownLatch = new CountDownLatch(1); + final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + countDownLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + + final Intent intent = createServiceIntent(); + final boolean success = mContext.bindService(intent, serviceConnection, + Context.BIND_AUTO_CREATE | flags); + Assert.assertTrue("Could not bind to service", success); + + try { + boolean connectedSuccess = countDownLatch.await(AWAIT_SERVICE_CONNECT_MS, + TimeUnit.MILLISECONDS); + Assert.assertTrue("Timeout when waiting for ServiceConnection.onServiceConnected()", + connectedSuccess); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + return serviceConnection; + } + + protected void unbindFromService(ServiceConnection serviceConnection) { + if (serviceConnection != null) { + mContext.unbindService(serviceConnection); + } + } + + protected Intent createBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags( + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + putTimeReceiverBinderExtra(intent); + return intent; + } + + protected void putTimeReceiverBinderExtra(Intent intent) { + intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder()); + } + + private void setUpIteration() { + mTimeReceiver.clear(); + TargetPackageUtils.killTargetPackage(mContext); + } + + protected void startTargetPackage() { + TargetPackageUtils.startTargetPackage(mContext, mTimeReceiver); + } + + protected long getReceivedTimeNs(String type) { + return mTimeReceiver.getReceivedTimeNs(type); + } + + protected void runPerfFunction(LongSupplier func) { + final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState(); + long elapsedTimeNs = 0; + while (benchmarkState.keepRunning(elapsedTimeNs)) { + setUpIteration(); + elapsedTimeNs = func.getAsLong(); + } + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java new file mode 100644 index 000000000000..f7dab03f10ee --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.tests; + +import android.content.Intent; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.Constants; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BroadcastPerfTest extends BasePerfTest { + @Test + public void manifestBroadcastRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createBroadcastIntent( + Constants.ACTION_BROADCAST_MANIFEST_RECEIVE); + + final long startTime = System.nanoTime(); + + mContext.sendBroadcast(intent); + + final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE); + + return endTime - startTime; + }); + } + + @Test + public void manifestBroadcastNotRunning() { + runPerfFunction(() -> { + final Intent intent = createBroadcastIntent( + Constants.ACTION_BROADCAST_MANIFEST_RECEIVE); + + final long startTime = System.nanoTime(); + + mContext.sendBroadcast(intent); + + final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE); + + return endTime - startTime; + }); + } + + @Test + public void registeredBroadcast() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createBroadcastIntent( + Constants.ACTION_BROADCAST_REGISTERED_RECEIVE); + + final long startTime = System.nanoTime(); + + mContext.sendBroadcast(intent); + + final long endTime = getReceivedTimeNs(Constants.TYPE_BROADCAST_RECEIVE); + + return endTime - startTime; + }); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java new file mode 100644 index 000000000000..3bf56ce8b085 --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.tests; + +import android.content.ContentProviderClient; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.TargetPackageUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ContentProviderPerfTest extends BasePerfTest { + /** + * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is + * running. + */ + @Test + public void contentProviderRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + long startTimeNs = System.nanoTime(); + final ContentProviderClient contentProviderClient = + mContext.getContentResolver() + .acquireContentProviderClient(TargetPackageUtils.PACKAGE_NAME); + final long endTimeNs = System.nanoTime(); + + contentProviderClient.close(); + + return endTimeNs - startTimeNs; + }); + } + + /** + * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is + * not running. + */ + @Test + public void contentProviderNotRunning() { + runPerfFunction(() -> { + final long startTimeNs = System.nanoTime(); + final ContentProviderClient contentProviderClient = + mContext.getContentResolver().acquireContentProviderClient( + TargetPackageUtils.PACKAGE_NAME); + final long endTimeNs = System.nanoTime(); + + contentProviderClient.close(); + + return endTimeNs - startTimeNs; + }); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java new file mode 100644 index 000000000000..6d2935a148fd --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.tests; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.Constants; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ServiceBindPerfTest extends BasePerfTest { + /** + * Create and return a ServiceConnection that will add the current time with type + * Constants.TYPE_SERVICE_CONNECTED. + */ + private ServiceConnection createServiceConnectionReportTime() { + return new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + addReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + } + + /** + * Try to bind to the service with the input parameters, throwing a RuntimeException with the + * errorMessage on failure. + */ + private void bindService(Intent intent, ServiceConnection serviceConnection, int flags) { + final boolean success = mContext.bindService(intent, serviceConnection, flags); + Assert.assertTrue("Could not bind to service", success); + } + + /** + * Benchmark time from Context.bindService() to Service.onBind() when target package is not + * running. + */ + @Test + public void bindServiceNotRunning() { + runPerfFunction(() -> { + final Intent intent = createServiceIntent(); + final ServiceConnection serviceConnection = createServiceConnectionReportTime(); + + final long startTimeNs = System.nanoTime(); + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + try { + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnection); + } + }); + } + + /** + * Benchmark time from Context.bindService() to Service.onBind() when target package is running. + */ + @Test + public void bindServiceRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createServiceIntent(); + final ServiceConnection serviceConnection = createServiceConnectionReportTime(); + + final long startTimeNs = System.nanoTime(); + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + try { + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnection); + } + }); + } + + /** + * Benchmark time from Context.bindService() to Service.onBind() when service is already bound + * to. + */ + @Test + public void bindServiceAlreadyBound() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createServiceIntent(); + final ServiceConnection alreadyBoundServiceConnection = bindAndWaitForConnectedService(); + + try { + final ServiceConnection serviceConnection = createServiceConnectionReportTime(); + + final long startTimeNs = System.nanoTime(); + try { + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnection); + } + } finally { + unbindFromService(alreadyBoundServiceConnection); + } + }); + } + + /** + * Benchmark time from Context.bindService() (without BIND_ALLOW_OOM_MANAGEMENT) to + * Service.onBind() when service is already bound to with BIND_ALLOW_OOM_MANAGEMENT. + */ + @Test + public void bindServiceAllowOomManagement() { + runPerfFunction(() -> { + final Intent intentNoOom = createServiceIntent(); + final ServiceConnection serviceConnectionOom = bindAndWaitForConnectedService( + Context.BIND_ALLOW_OOM_MANAGEMENT); + + try { + final ServiceConnection serviceConnectionNoOom = + createServiceConnectionReportTime(); + try { + final long startTimeNs = System.nanoTime(); + bindService(intentNoOom, serviceConnectionNoOom, Context.BIND_AUTO_CREATE); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED); + + return endTimeNs - startTimeNs; + } finally { + unbindFromService(serviceConnectionNoOom); + } + } finally { + unbindFromService(serviceConnectionOom); + } + }); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java new file mode 100644 index 000000000000..626ee020542d --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.tests; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.Constants; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ServiceStartPerfTest extends BasePerfTest { + + /** + * Tries to start the service with the given intent, throwing a RuntimeException with the + * errorMessage on failure. + */ + private void startService(Intent intent) { + final ComponentName componentName = mContext.startService(intent); + Assert.assertNotNull("Could not start service", componentName); + } + + /** + * Benchmark time from Context.startService() to Service.onStartCommand() when target process is + * not running. + */ + @Test + public void startServiceNotRunning() { + runPerfFunction(() -> { + final Intent intent = createServiceIntent(); + + final long startTimeNs = System.nanoTime(); + + startService(intent); + + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + return endTimeNs - startTimeNs; + }); + } + + /** + * Benchmark time from Context.startService() to Service.onStartCommand() when target process is + * running. + */ + @Test + public void startServiceProcessRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + final Intent intent = createServiceIntent(); + + final long startTimeNs = System.nanoTime(); + startService(intent); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + + return endTimeNs - startTimeNs; + }); + } + + /** + * Benchmark time from Context.startService() to Service.onStartCommand() when service is + * already bound to. + */ + @Test + public void startServiceAlreadyBound() { + runPerfFunction(() -> { + final ServiceConnection alreadyBoundServiceConnection = + bindAndWaitForConnectedService(); + try { + final Intent intent = createServiceIntent(); + + final long startTimeNs = System.nanoTime(); + startService(intent); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + + return endTimeNs - startTimeNs; + } finally { + unbindFromService(alreadyBoundServiceConnection); + } + }); + } + + /** + * Benchmark time from Context.startService() with FLAG_GRANT_READ_URI_PERMISSION to + * Service.onStartCommand() when target process is running. + */ + @Test + public void startServiceProcessRunningReadUriPermission() { + runPerfFunction(() -> { + final ServiceConnection alreadyBoundServiceConnection = + bindAndWaitForConnectedService(); + try { + final Intent intent = createServiceIntent(); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + final long startTimeNs = System.nanoTime(); + startService(intent); + final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START); + + return endTimeNs - startTimeNs; + } finally { + unbindFromService(alreadyBoundServiceConnection); + } + }); + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java new file mode 100644 index 000000000000..3db8abce90da --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.util; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.SystemClock; + +public class TargetPackageUtils { + private static final String TAG = TargetPackageUtils.class.getSimpleName(); + + public static final String PACKAGE_NAME = "com.android.frameworks.perftests.amteststestapp"; + public static final String ACTIVITY_NAME = PACKAGE_NAME + ".TestActivity"; + public static final String SERVICE_NAME = PACKAGE_NAME + ".TestService"; + + private static final long WAIT_TIME_MS = 100L; + + // Cache for test app's uid, so we only have to query it once. + private static int sTestAppUid = -1; + + /** + * Kills the test package synchronously. + */ + public static void killTargetPackage(Context context) { + ActivityManager activityManager = context.getSystemService(ActivityManager.class); + activityManager.forceStopPackage(PACKAGE_NAME); + while (targetPackageIsRunning(context)) { + sleep(); + } + + Utils.drainBroadcastQueue(); + } + + /** + * Starts the test package synchronously. It does so by starting an Activity. + */ + public static void startTargetPackage(Context context, TimeReceiver timeReceiver) { + // "am start-activity -W PACKAGE_NAME/ACTIVITY_CLASS_NAME" still requires a sleep even + // though it should be synchronous, so just use Intent instead + final Intent intent = new Intent(); + intent.putExtras(timeReceiver.createReceiveTimeExtraBinder()); + intent.setClassName(PACKAGE_NAME, ACTIVITY_NAME); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + + while (!targetPackageIsRunning(context)) { + sleep(); + } + // make sure Application has run + timeReceiver.getReceivedTimeNs(Constants.TYPE_TARGET_PACKAGE_START); + Utils.drainBroadcastQueue(); + } + + private static boolean targetPackageIsRunning(Context context) { + final int uid = getTestAppUid(context); + final String result = Utils.runShellCommand( + String.format("cmd activity get-uid-state %d", uid)); + return !result.contains("(NONEXISTENT)"); + } + + private static void sleep() { + SystemClock.sleep(WAIT_TIME_MS); + } + + private static int getTestAppUid(Context context) { + if (sTestAppUid == -1) { + final PackageManager pm = context.getPackageManager(); + try { + sTestAppUid = pm.getPackageUid(PACKAGE_NAME, 0); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + } + return sTestAppUid; + } + +} + diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java new file mode 100644 index 000000000000..a86a5c7ac3d4 --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.util; + +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + + +/** + * TimeReceiver will listen for any messages containing a timestamp by starting a BroadcastReceiver + * which listens for Intents with the SendTime.ACTION_SEND_TIME action. + */ +public class TimeReceiver { + private static final String TAG = TimeReceiver.class.getSimpleName(); + private static final long DEFAULT_RECEIVE_TIME_TIMEOUT_MILLIS = 10000L; + + private BlockingQueue<ReceivedMessage> mQueue = new LinkedBlockingQueue<>(); + + private static class ReceivedMessage { + private final String mReceivedMessageType; + private final long mReceivedTimeNs; + + public ReceivedMessage(String receivedMessageType, long receivedTimeNs) { + mReceivedMessageType = receivedMessageType; + mReceivedTimeNs = receivedTimeNs; + } + } + + public void addTimeForTypeToQueue(String type, long timeNs) { + if (type == null) { + throw new IllegalArgumentException("type is null when adding time to queue"); + } + if (timeNs < 0) { + throw new RuntimeException( + "time is negative/non-existant (" + timeNs + ") when adding time to queue"); + } + mQueue.add(new ReceivedMessage(type, timeNs)); + } + + public Bundle createReceiveTimeExtraBinder() { + Bundle extras = new Bundle(); + extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, new ITimeReceiverCallback.Stub() { + @Override + public void sendTime(String type, long timeNs) throws RemoteException { + addTimeForTypeToQueue(type, timeNs); + } + }); + return extras; + } + + public long getReceivedTimeNs(String type) { + return getReceivedTimeNs(type, DEFAULT_RECEIVE_TIME_TIMEOUT_MILLIS); + } + + /** + * Returns a received timestamp with the given type tag. Will throw away any messages with a + * different type tag. If it times out, a RuntimeException is thrown. + */ + public long getReceivedTimeNs(String type, long timeoutMs) { + ReceivedMessage message; + long endTimeNs = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs); + do { + long curTimeNs = System.nanoTime(); + if (curTimeNs > endTimeNs) { + throw new RuntimeException("Timed out when listening for a time: " + type); + } + try { + Log.i(TAG, "waiting for message " + type); + message = mQueue.poll(endTimeNs - curTimeNs, TimeUnit.NANOSECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (message == null) { + throw new RuntimeException("Timed out when listening for a time: " + type); + } + Log.i(TAG, "got message " + message.mReceivedMessageType); + if (!type.equals(message.mReceivedMessageType)) { + Log.i(TAG, String.format("Expected type \"%s\", got \"%s\" (%d), skipping", type, + message.mReceivedMessageType, message.mReceivedTimeNs)); + } + } while (!type.equals(message.mReceivedMessageType)); + return message.mReceivedTimeNs; + } + + /** + * Clears the message queue. + */ + public void clear() { + mQueue.clear(); + } +} diff --git a/tests/ActivityManagerPerfTests/utils/Android.mk b/tests/ActivityManagerPerfTests/utils/Android.mk new file mode 100644 index 000000000000..7276e37afc7a --- /dev/null +++ b/tests/ActivityManagerPerfTests/utils/Android.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2018 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) \ + src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + junit \ + ub-uiautomator + +LOCAL_MODULE := ActivityManagerPerfTestsUtils + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java new file mode 100644 index 000000000000..ffb3f84cee51 --- /dev/null +++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.util; + +public class Constants { + public static final String TYPE_TARGET_PACKAGE_START = "target_package_start"; + public static final String TYPE_BROADCAST_RECEIVE = "broadcast_receive"; + public static final String TYPE_SERVICE_BIND = "service_bind"; + public static final String TYPE_SERVICE_START = "service_start"; + public static final String TYPE_SERVICE_CONNECTED = "service_connection_connect"; + + public static final String ACTION_BROADCAST_MANIFEST_RECEIVE = + "com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE"; + public static final String ACTION_BROADCAST_REGISTERED_RECEIVE = + "com.android.frameworks.perftests.ACTION_BROADCAST_REGISTERED_RECEIVE"; + + public static final String EXTRA_RECEIVER_CALLBACK = "receiver_callback_binder"; +} diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl new file mode 100644 index 000000000000..b43d49a1b8e4 --- /dev/null +++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.util; + +interface ITimeReceiverCallback { + void sendTime(String type, long timeNs); +}
\ No newline at end of file diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java new file mode 100644 index 000000000000..493d8cdd0803 --- /dev/null +++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 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.frameworks.perftests.am.util; + +import android.content.Intent; +import android.os.RemoteException; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.UiDevice; +import android.util.Log; + +import java.io.IOException; + +public class Utils { + private static final String TAG = "AmPerfTestsUtils"; + + public static void drainBroadcastQueue() { + runShellCommand("am wait-for-broadcast-idle"); + } + + /** + * Runs the command and returns the stdout. + */ + public static String runShellCommand(String cmd) { + try { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Sends the current time in a message with the given type so TimeReceiver can receive it. + */ + public static void sendTime(Intent intent, String type) { + final long time = System.nanoTime(); + final ITimeReceiverCallback sendTimeBinder = ITimeReceiverCallback.Stub.asInterface( + intent.getExtras().getBinder(Constants.EXTRA_RECEIVER_CALLBACK)); + try { + sendTimeBinder.sendTime(type, time); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + } +} diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index ceb3993e1705..0f4960887a33 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -117,46 +117,6 @@ public class ActivityTestMain extends Activity { } } - private void addThumbnail(LinearLayout container, Bitmap bm, - final ActivityManager.RecentTaskInfo task, - final ActivityManager.TaskThumbnail thumbs) { - ImageView iv = new ImageView(this); - if (bm != null) { - iv.setImageBitmap(bm); - } - iv.setBackgroundResource(android.R.drawable.gallery_thumb); - int w = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_width); - int h = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_height); - container.addView(iv, new LinearLayout.LayoutParams(w, h)); - - iv.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (task.id >= 0 && thumbs != null) { - mAm.moveTaskToFront(task.id, ActivityManager.MOVE_TASK_WITH_HOME); - } else { - try { - startActivity(task.baseIntent); - } catch (ActivityNotFoundException e) { - Log.w("foo", "Unable to start task: " + e); - } - } - buildUi(); - } - }); - iv.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (task.id >= 0 && thumbs != null) { - mAm.removeTask(task.id); - buildUi(); - return true; - } - return false; - } - }); - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -600,7 +560,6 @@ public class ActivityTestMain extends Activity { if (recents != null) { for (int i=0; i<recents.size(); i++) { ActivityManager.RecentTaskInfo r = recents.get(i); - ActivityManager.TaskThumbnail tt = mAm.getTaskThumbnail(r.persistentId); TextView tv = new TextView(this); tv.setText(r.baseIntent.getComponent().flattenToShortString()); top.addView(tv, new LinearLayout.LayoutParams( @@ -608,7 +567,6 @@ public class ActivityTestMain extends Activity { LinearLayout.LayoutParams.WRAP_CONTENT)); LinearLayout item = new LinearLayout(this); item.setOrientation(LinearLayout.HORIZONTAL); - addThumbnail(item, tt != null ? tt.mainThumbnail : null, r, tt); top.addView(item, new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk index d01b1f96ee05..917293fa266b 100644 --- a/tests/AppLaunch/Android.mk +++ b/tests/AppLaunch/Android.mk @@ -9,10 +9,12 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := AppLaunch LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner LOCAL_STATIC_JAVA_LIBRARIES := android-support-test +LOCAL_COMPATIBILITY_SUITE := device-tests + include $(BUILD_PACKAGE) # Use the following include to make our test apk. diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 1443fc1ae3ee..063060f166dc 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -83,7 +83,7 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval"; private static final String WEARABLE_ACTION_GOOGLE = "com.google.android.wearable.action.GOOGLE"; - private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle + private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps private static final String LAUNCH_SUB_DIRECTORY = "launch_logs"; diff --git a/tests/BackgroundDexOptServiceIntegrationTests/Android.mk b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk new file mode 100644 index 000000000000..da1a08b79a8f --- /dev/null +++ b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2017 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + +LOCAL_PACKAGE_NAME := BackgroundDexOptServiceIntegrationTests +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml new file mode 100644 index 000000000000..afae155f88fe --- /dev/null +++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.bgdexopttest"> + + + <!-- Uses API introduced in O (26) --> + <uses-sdk + android:minSdkVersion="1" + android:targetSdkVersion="26" /> + + <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> + <uses-permission android:name="android.permission.SET_TIME" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.bgdexopttest" + android:label="Integration test for BackgroundDexOptService" /> +</manifest> diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml new file mode 100644 index 000000000000..9bb1e280b861 --- /dev/null +++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> +<configuration description="Runs BackgroundDexOptService Integration Tests"> + <!--DeviceSetup should go before TimeSetter because it stops automatic update of time--> + <target_preparer + class="com.android.tradefed.targetprep.DeviceSetup"> + <option name="auto-update-time" value="OFF"/> + <option name="auto-update-timezone" value="OFF"/> + <option name="set-property" key="pm.dexopt.downgrade_after_inactive_days" value="2"/> + <option name="set-property" key="pm.dexopt.disable_bg_dexopt" value="true"/> + <option name="set-property" key="pm.dexopt.inactive" value="verify"/> + <option name="set-property" key="pm.dexopt.bg-dexopt" value="speed"/> + <option name="restore-settings" value="true"/> + <option name="restore-properties" value="true"/> + </target_preparer> + + <!--Test app needs to be installed when we change its settings below--> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="BackgroundDexOptServiceIntegrationTests.apk"/> + <option name="cleanup-apks" value="true"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.SetPackagesRecentlyUsed"> + <option name="package-recently-used-time" value="0d"/> + <option name="package-recently-used-name" value="com.android.frameworks.bgdexopttest"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.RestartSystemServerTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceStorageFiller"> + <!--32GB--> + <!--necessary because a package cannot create a file larger than 100GB--> + <option name="free-bytes" value="34359738368"/> + </target_preparer> + + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="BackgroundDexOptServiceIntegrationTests"/> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.frameworks.bgdexopttest"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java new file mode 100644 index 000000000000..3734412981d8 --- /dev/null +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2017 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.server.pm; + +import android.app.AlarmManager; +import android.content.Context; +import android.os.Environment; +import android.os.SystemProperties; +import android.os.storage.StorageManager; +import android.support.test.InstrumentationRegistry; +import android.util.Log; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.concurrent.TimeUnit; + +/** + * Integration tests for {@link BackgroundDexOptService}. + * + * Tests various scenarios around BackgroundDexOptService. + * 1. Under normal conditions, check that dexopt upgrades test app to + * $(getprop pm.dexopt.bg-dexopt). + * 2. Under low storage conditions and package is unused, check + * that dexopt downgrades test app to $(getprop pm.dexopt.inactive). + * 3. Under low storage conditions and package is recently used, check + * that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). + * + * Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest". + * + * The setup for these tests make sure this package has been configured to have been recently used + * plus installed far enough in the past. If a test case requires that this package has not been + * recently used, it sets the time forward more than + * `getprop pm.dexopt.downgrade_after_inactive_days` days. + * + * For tests that require low storage, the phone is filled up. + * + * Run with "atest BackgroundDexOptServiceIntegrationTests". + */ +@RunWith(JUnit4.class) +public final class BackgroundDexOptServiceIntegrationTests { + + private static final String TAG = BackgroundDexOptServiceIntegrationTests.class.getSimpleName(); + + // Name of package to test on. + private static final String PACKAGE_NAME = "com.android.frameworks.bgdexopttest"; + // Name of file used to fill up storage. + private static final String BIG_FILE = "bigfile"; + private static final String BG_DEXOPT_COMPILER_FILTER = SystemProperties.get( + "pm.dexopt.bg-dexopt"); + private static final String DOWNGRADE_COMPILER_FILTER = SystemProperties.get( + "pm.dexopt.inactive"); + private static final long DOWNGRADE_AFTER_DAYS = SystemProperties.getLong( + "pm.dexopt.downgrade_after_inactive_days", 0); + // Needs to be between 1.0 and 2.0. + private static final double LOW_STORAGE_MULTIPLIER = 1.5; + + // The file used to fill up storage. + private File mBigFile; + + // Remember start time. + @BeforeClass + public static void setUpAll() { + if (!SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt", false)) { + throw new RuntimeException( + "bg-dexopt is not disabled (set pm.dexopt.disable_bg_dexopt to true)"); + } + if (DOWNGRADE_AFTER_DAYS < 1) { + throw new RuntimeException( + "pm.dexopt.downgrade_after_inactive_days must be at least 1"); + } + if ("quicken".equals(BG_DEXOPT_COMPILER_FILTER)) { + throw new RuntimeException("pm.dexopt.bg-dexopt should not be \"quicken\""); + } + if ("quicken".equals(DOWNGRADE_COMPILER_FILTER)) { + throw new RuntimeException("pm.dexopt.inactive should not be \"quicken\""); + } + } + + + private static Context getContext() { + return InstrumentationRegistry.getTargetContext(); + } + + @Before + public void setUp() throws IOException { + File dataDir = getContext().getDataDir(); + mBigFile = new File(dataDir, BIG_FILE); + } + + @After + public void tearDown() { + if (mBigFile.exists()) { + boolean result = mBigFile.delete(); + if (!result) { + throw new RuntimeException("Couldn't delete big file"); + } + } + } + + // Return the content of the InputStream as a String. + private static String inputStreamToString(InputStream is) throws IOException { + char[] buffer = new char[1024]; + StringBuilder builder = new StringBuilder(); + try (InputStreamReader reader = new InputStreamReader(is)) { + for (; ; ) { + int count = reader.read(buffer, 0, buffer.length); + if (count < 0) { + break; + } + builder.append(buffer, 0, count); + } + } + return builder.toString(); + } + + // Run the command and return the stdout. + private static String runShellCommand(String cmd) throws IOException { + Log.i(TAG, String.format("running command: '%s'", cmd)); + long startTime = System.nanoTime(); + Process p = Runtime.getRuntime().exec(cmd); + int res; + try { + res = p.waitFor(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + String stdout = inputStreamToString(p.getInputStream()); + String stderr = inputStreamToString(p.getErrorStream()); + long elapsedTime = System.nanoTime() - startTime; + Log.i(TAG, String.format("ran command: '%s' in %d ms with return code %d", cmd, + TimeUnit.NANOSECONDS.toMillis(elapsedTime), res)); + Log.i(TAG, "stdout"); + Log.i(TAG, stdout); + Log.i(TAG, "stderr"); + Log.i(TAG, stderr); + if (res != 0) { + throw new RuntimeException(String.format("failed command: '%s'", cmd)); + } + return stdout; + } + + // Run the command and return the stdout split by lines. + private static String[] runShellCommandSplitLines(String cmd) throws IOException { + return runShellCommand(cmd).split("\n"); + } + + // Return the compiler filter of a package. + private static String getCompilerFilter(String pkg) throws IOException { + String cmd = String.format("dumpsys package %s", pkg); + String[] lines = runShellCommandSplitLines(cmd); + final String substr = "compilation_filter="; + for (String line : lines) { + int startIndex = line.indexOf(substr); + if (startIndex < 0) { + continue; + } + startIndex += substr.length(); + int endIndex = line.indexOf(']', startIndex); + return line.substring(startIndex, endIndex); + } + throw new RuntimeException("Couldn't find compiler filter in dumpsys package"); + } + + // Return the number of bytes available in the data partition. + private static long getDataDirUsableSpace() { + return Environment.getDataDirectory().getUsableSpace(); + } + + // Fill up the storage until there are bytesRemaining number of bytes available in the data + // partition. Writes to the current package's data directory. + private void fillUpStorage(long bytesRemaining) throws IOException { + Log.i(TAG, String.format("Filling up storage with %d bytes remaining", bytesRemaining)); + logSpaceRemaining(); + long numBytesToAdd = getDataDirUsableSpace() - bytesRemaining; + String cmd = String.format("fallocate -l %d %s", numBytesToAdd, mBigFile.getAbsolutePath()); + runShellCommand(cmd); + logSpaceRemaining(); + } + + // Fill up storage so that device is in low storage condition. + private void fillUpToLowStorage() throws IOException { + fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); + } + + // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run + private static void runBackgroundDexOpt() throws IOException { + runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); + } + + // Set the time ahead of the last use time of the test app in days. + private static void setTimeFutureDays(long futureDays) { + setTimeFutureMillis(TimeUnit.DAYS.toMillis(futureDays)); + } + + // Set the time ahead of the last use time of the test app in milliseconds. + private static void setTimeFutureMillis(long futureMillis) { + long currentTime = System.currentTimeMillis(); + setTime(currentTime + futureMillis); + } + + private static void setTime(long time) { + AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); + am.setTime(time); + } + + // Return the number of free bytes when the data partition is considered low on storage. + private static long getStorageLowBytes() { + StorageManager storageManager = (StorageManager) getContext().getSystemService( + Context.STORAGE_SERVICE); + return storageManager.getStorageLowBytes(Environment.getDataDirectory()); + } + + // Log the amount of space remaining in the data directory. + private static void logSpaceRemaining() throws IOException { + runShellCommand("df -h /data"); + } + + // Compile the given package with the given compiler filter. + private static void compilePackageWithFilter(String pkg, String filter) throws IOException { + runShellCommand(String.format("cmd package compile -f -m %s %s", filter, pkg)); + } + + // Test that background dexopt under normal conditions succeeds. + @Test + public void testBackgroundDexOpt() throws IOException { + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "verify"); + Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); + + runBackgroundDexOpt(); + + // Verify that bg-dexopt is successful. + Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } + + // Test that background dexopt under low storage conditions upgrades used packages. + @Test + public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException { + // Should be less than DOWNGRADE_AFTER_DAYS. + long deltaDays = DOWNGRADE_AFTER_DAYS - 1; + try { + // Set time to future. + setTimeFutureDays(deltaDays); + + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "quicken"); + Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + + // Fill up storage to trigger low storage threshold. + fillUpToLowStorage(); + + runBackgroundDexOpt(); + + // Verify that downgrade did not happen. + Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } finally { + // Reset time. + setTimeFutureDays(-deltaDays); + } + } + + // Test that background dexopt under low storage conditions downgrades unused packages. + @Test + public void testBackgroundDexOptDowngradeSuccessful() throws IOException { + // Should be more than DOWNGRADE_AFTER_DAYS. + long deltaDays = DOWNGRADE_AFTER_DAYS + 1; + try { + // Set time to future. + setTimeFutureDays(deltaDays); + + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "quicken"); + Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + + // Fill up storage to trigger low storage threshold. + fillUpToLowStorage(); + + runBackgroundDexOpt(); + + // Verify that downgrade is successful. + Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } finally { + // Reset time. + setTimeFutureDays(-deltaDays); + } + } + +} diff --git a/tests/BrowserPowerTest/Android.mk b/tests/BrowserPowerTest/Android.mk index 59bc729a6280..57655751cfd8 100644 --- a/tests/BrowserPowerTest/Android.mk +++ b/tests/BrowserPowerTest/Android.mk @@ -18,8 +18,8 @@ include $(CLEAR_VARS) # We only want this apk build for tests. LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk index 527d1bbfd886..9e7f61892f93 100644 --- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk @@ -24,7 +24,7 @@ LOCAL_PACKAGE_NAME := SmartCamera-tests LOCAL_SRC_FILES += $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.base LOCAL_STATIC_JAVA_LIBRARIES := guava junit LOCAL_PROGUARD_ENABLED := disabled diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk index 90de503ebd81..b071ec4c494b 100644 --- a/tests/CanvasCompare/Android.mk +++ b/tests/CanvasCompare/Android.mk @@ -23,7 +23,7 @@ LOCAL_PACKAGE_NAME := CanvasCompare LOCAL_MODULE_TAGS := tests -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit include $(BUILD_PACKAGE) diff --git a/tests/CoreTests/android/Android.mk b/tests/CoreTests/android/Android.mk index c9f11615381f..56d7918aa01e 100644 --- a/tests/CoreTests/android/Android.mk +++ b/tests/CoreTests/android/Android.mk @@ -6,8 +6,14 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt org.apache.http.legacy -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + bouncycastle \ + conscrypt \ + org.apache.http.legacy \ + android.test.base \ + +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := CoreTests diff --git a/tests/DataIdleTest/Android.mk b/tests/DataIdleTest/Android.mk index 4e15729221a9..85f7edf7438d 100644 --- a/tests/DataIdleTest/Android.mk +++ b/tests/DataIdleTest/Android.mk @@ -20,8 +20,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := DataIdleTest -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_SRC_FILES := $(call all-java-files-under, src) # We need to sign it to get access to the network usage history. diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk new file mode 100644 index 000000000000..7187a3795433 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/Android.mk @@ -0,0 +1,49 @@ +# +# Copyright 2017 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. +# + +LOCAL_PATH:= $(call my-dir) + +# Build a tiny library that the test app can dynamically load + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := DexLoggerTestLibrary +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl) + +include $(BUILD_JAVA_LIBRARY) + +dexloggertest_jar := $(LOCAL_BUILT_MODULE) + + +# Build the test app itself + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests +LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_CERTIFICATE := platform +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + truth-prebuilt \ + +# This gets us the javalib.jar built by DexLoggerTestLibrary above. +LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar) + +include $(BUILD_PACKAGE) diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DexLoggerIntegrationTests/AndroidManifest.xml new file mode 100644 index 000000000000..a847e8f3b921 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.dexloggertest"> + + <!-- Tests feature introduced in P (27) --> + <uses-sdk + android:minSdkVersion="27" + android:targetSdkVersion="27" /> + + <uses-permission android:name="android.permission.READ_LOGS" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.dexloggertest" + android:label="Integration test for DexLogger" /> +</manifest> diff --git a/tests/DexLoggerIntegrationTests/AndroidTest.xml b/tests/DexLoggerIntegrationTests/AndroidTest.xml new file mode 100644 index 000000000000..8ed19f893476 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 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. +--> +<configuration description="Runs DexLogger Integration Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="DexLoggerIntegrationTests.apk"/> + <option name="cleanup-apks" value="true"/> + </target_preparer> + + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="DexLoggerIntegrationTests"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.frameworks.dexloggertest"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java b/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java new file mode 100644 index 000000000000..e995a26ea5c9 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017 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.dcl; + +/** Dummy class which is built into a jar purely so we can pass it to DexClassLoader. */ +public final class Simple { + public Simple() {} +} diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java new file mode 100644 index 000000000000..d8b3b2086335 --- /dev/null +++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java @@ -0,0 +1,154 @@ +/* + * Copyright 2017 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.server.pm.dex; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.util.EventLog; +import dalvik.system.DexClassLoader; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Formatter; +import java.util.List; + +/** + * Integration tests for {@link com.android.server.pm.dex.DexLogger}. + * + * The setup for the test dynamically loads code in a jar extracted + * from our assets (a secondary dex file). + * + * We then use adb to trigger secondary dex file reconcilation (and + * wait for it to complete). As a side-effect of this DexLogger should + * be notified of the file and should log the hash of the file's name + * and content. We verify that this message appears in the event log. + * + * Run with "atest DexLoggerIntegrationTests". + */ +@LargeTest +@RunWith(JUnit4.class) +public final class DexLoggerIntegrationTests { + + private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest"; + + // Event log tag used for SNET related events + private static final int SNET_TAG = 0x534e4554; + // Subtag used to distinguish dynamic code loading events + private static final String DCL_SUBTAG = "dcl"; + + // Obtained via "echo -n copied.jar | sha256sum" + private static final String EXPECTED_NAME_HASH = + "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C"; + + private static String expectedContentHash; + + @BeforeClass + public static void setUpAll() throws Exception { + Context context = InstrumentationRegistry.getTargetContext(); + MessageDigest hasher = MessageDigest.getInstance("SHA-256"); + + // Copy the jar from our Java resources to a private data directory + File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar"); + Class<?> thisClass = DexLoggerIntegrationTests.class; + try (InputStream input = thisClass.getResourceAsStream("/javalib.jar"); + OutputStream output = new FileOutputStream(privateCopy)) { + byte[] buffer = new byte[1024]; + while (true) { + int numRead = input.read(buffer); + if (numRead < 0) { + break; + } + output.write(buffer, 0, numRead); + hasher.update(buffer, 0, numRead); + } + } + + // Remember the SHA-256 of the file content to check that it is the same as + // the value we see logged. + Formatter formatter = new Formatter(); + for (byte b : hasher.digest()) { + formatter.format("%02X", b); + } + expectedContentHash = formatter.toString(); + + // Feed the jar to a class loader and make sure it contains what we expect. + ClassLoader loader = + new DexClassLoader( + privateCopy.toString(), null, null, context.getClass().getClassLoader()); + loader.loadClass("com.android.dcl.Simple"); + } + + @Test + public void testDexLoggerReconcileGeneratesEvents() throws Exception { + int[] tagList = new int[] { SNET_TAG }; + List<EventLog.Event> events = new ArrayList<>(); + + // There may already be events in the event log - figure out the most recent one + EventLog.readEvents(tagList, events); + long previousEventNanos = + events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos(); + events.clear(); + + Process process = Runtime.getRuntime().exec( + "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME); + int exitCode = process.waitFor(); + assertThat(exitCode).isEqualTo(0); + + int myUid = android.os.Process.myUid(); + String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash; + + EventLog.readEvents(tagList, events); + boolean found = false; + for (EventLog.Event event : events) { + if (event.getTimeNanos() <= previousEventNanos) { + continue; + } + Object[] data = (Object[]) event.getData(); + + // We only care about DCL events that we generated. + String subTag = (String) data[0]; + if (!DCL_SUBTAG.equals(subTag)) { + continue; + } + int uid = (int) data[1]; + if (uid != myUid) { + continue; + } + + String message = (String) data[2]; + assertThat(message).isEqualTo(expectedMessage); + found = true; + } + + assertThat(found).isTrue(); + } +} diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk index d2ec75347531..1873cc1de8a9 100644 --- a/tests/FrameworkPerf/Android.mk +++ b/tests/FrameworkPerf/Android.mk @@ -7,8 +7,8 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := FrameworkPerf -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,161dpi,320dpi,nodpi diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk index f8c865631f93..8550d703678c 100644 --- a/tests/HierarchyViewerTest/Android.mk +++ b/tests/HierarchyViewerTest/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := HierarchyViewerTest -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit include $(BUILD_PACKAGE) diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 9caf9d0f6e26..c8f96c9f0670 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -282,6 +282,16 @@ <category android:name="com.android.test.hwui.TEST" /> </intent-filter> </activity> + + <activity + android:name="ColoredShadowsActivity" + android:label="View/ColoredShadows" + android:theme="@style/ThemeColoredShadows"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> <activity android:name="OpaqueActivity" diff --git a/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml b/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml new file mode 100644 index 000000000000..18633250cfcb --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2017, 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. +*/ +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/colored_grid"> + <include layout="@layout/colored_shadows_row" /> + <include layout="@layout/colored_shadows_row" /> + <include layout="@layout/colored_shadows_row" /> + <include layout="@layout/colored_shadows_row" /> + <include layout="@layout/colored_shadows_row" /> + <include layout="@layout/colored_shadows_row" /> + <include layout="@layout/colored_shadows_row" /> +</LinearLayout> diff --git a/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml b/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml new file mode 100644 index 000000000000..61b075974926 --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2017, 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. +*/ +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="25dp" + android:paddingBottom="25dp" + android:clipToPadding="false" > + <View android:id="@+id/grey" + android:layout_width="50dp" + android:layout_height="50dp" + android:elevation="16dp" + android:background="#3C4043" + android:layout_marginLeft="20dp" /> + <View android:id="@+id/blue" + android:layout_width="50dp" + android:layout_height="50dp" + android:elevation="16dp" + android:background="#185ABC" + android:layout_marginLeft="20dp"/> + <View android:id="@+id/red" + android:layout_width="50dp" + android:layout_height="50dp" + android:elevation="16dp" + android:background="#B31412" + android:layout_marginLeft="20dp"/> + <View android:id="@+id/yellow" + android:layout_width="50dp" + android:layout_height="50dp" + android:elevation="16dp" + android:background="#EA8600" + android:layout_marginLeft="20dp"/> + <View android:id="@+id/green" + android:layout_width="50dp" + android:layout_height="50dp" + android:elevation="16dp" + android:background="#137333" + android:layout_marginLeft="20dp"/> +</LinearLayout> diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml index 108709bd76b2..fa5437f38ace 100644 --- a/tests/HwAccelerationTest/res/values/styles.xml +++ b/tests/HwAccelerationTest/res/values/styles.xml @@ -34,4 +34,11 @@ <item name="android:translationZ">400dp</item> <item name="android:layout_alignParentBottom">true</item> </style> + + <style name="ThemeColoredShadows" parent="@android:style/Theme.Material.Light"> + <!-- + <item name="android:ambientShadowAlpha">0</item> + <item name="android:spotShadowAlpha">1</item> + --> + </style> </resources> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java new file mode 100644 index 000000000000..901d90eed70a --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 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.test.hwui; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; + +public class ColoredShadowsActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.colored_shadows_activity); + ViewGroup grid = findViewById(R.id.colored_grid); + for (int i = 0; i < grid.getChildCount(); i++) { + setShadowColors((ViewGroup) grid.getChildAt(i), i); + } + } + + private void setShadowColors(ViewGroup row, int rowIndex) { + for (int i = 0; i < row.getChildCount(); i++) { + View view = row.getChildAt(i); + //view.setBackground(new MyHackyBackground()); + view.setOutlineSpotShadowColor(shadowColorFor(view)); + view.setOutlineAmbientShadowColor(shadowColorFor(view)); + view.setElevation(6.0f * (rowIndex + 1)); + } + } + + private int shadowColorFor(View view) { + switch (view.getId()) { + case R.id.grey: return 0xFF3C4043; + case R.id.blue: return Color.BLUE; + case R.id.red: return 0xFFEA4335; + case R.id.yellow: return 0xFFFBBC04; + case R.id.green: return 0xFF34A853; + default: return 0xFF000000; + } + } + + private static class MyHackyBackground extends ColorDrawable { + MyHackyBackground() { + super(0); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public int getAlpha() { + return 254; + } + } +} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java index faabdfc01de7..af8e10bc07ae 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java @@ -27,6 +27,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Paint; +import android.graphics.Picture; import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.os.Bundle; @@ -47,10 +48,8 @@ public class DrawIntoHwBitmapActivity extends Activity { } Bitmap createBitmap() { - RenderNode node = RenderNode.create("HwuiCanvas", null); - node.setLeftTopRightBottom(0, 0, 500, 500); - node.setClipToBounds(false); - DisplayListCanvas canvas = node.start(500, 500); + Picture picture = new Picture(); + Canvas canvas = picture.beginRecording(500, 500); Paint p = new Paint(); p.setColor(Color.BLACK); p.setTextSize(20 * getResources().getDisplayMetrics().density); @@ -59,7 +58,7 @@ public class DrawIntoHwBitmapActivity extends Activity { canvas.drawRect(0, 0, 500, 100, p); p.setColor(Color.BLACK); canvas.drawText("Hello, World!", 0, 90, p); - node.end(canvas); - return ThreadedRenderer.createHardwareBitmap(node, 500, 500); + picture.endRecording(); + return Bitmap.createBitmap(picture); } } diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk index 60424712d02c..a0df959cf185 100644 --- a/tests/ImfTest/tests/Android.mk +++ b/tests/ImfTest/tests/Android.mk @@ -7,8 +7,8 @@ LOCAL_MODULE_TAGS := tests # Include all test java files. LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := ImfTestTests diff --git a/tests/Internal/Android.mk b/tests/Internal/Android.mk index fc001e928e80..b69e3e4fdd4b 100644 --- a/tests/Internal/Android.mk +++ b/tests/Internal/Android.mk @@ -11,10 +11,10 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_STATIC_JAVA_LIBRARIES := junit \ - legacy-android-test \ android-support-test \ mockito-target-minus-junit4 +LOCAL_JAVA_RESOURCE_DIRS := res LOCAL_CERTIFICATE := platform LOCAL_PACKAGE_NAME := InternalTests diff --git a/tests/Internal/AndroidManifest.xml b/tests/Internal/AndroidManifest.xml index a2c95fbbfc0b..e5a56949fe4e 100644 --- a/tests/Internal/AndroidManifest.xml +++ b/tests/Internal/AndroidManifest.xml @@ -18,8 +18,24 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.internal.tests"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.BIND_WALLPAPER" /> <application> <uses-library android:name="android.test.runner" /> + + <service android:name="stub.DummyWallpaperService" + android:enabled="true" + android:directBootAware="true" + android:label="Dummy wallpaper" + android:permission="android.permission.BIND_WALLPAPER"> + + <intent-filter> + <action android:name="android.service.wallpaper.WallpaperService" /> + </intent-filter> + + <!-- Link to XML that defines the wallpaper info. --> + <meta-data android:name="android.service.wallpaper" + android:resource="@xml/livewallpaper" /> + </service> </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" diff --git a/tests/Internal/res/xml/livewallpaper.xml b/tests/Internal/res/xml/livewallpaper.xml new file mode 100644 index 000000000000..dbb0e4761cba --- /dev/null +++ b/tests/Internal/res/xml/livewallpaper.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2017 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 + --> +<wallpaper + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + androidprv:supportsAmbientMode="true"/>
\ No newline at end of file diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java new file mode 100644 index 000000000000..9d26270fb96a --- /dev/null +++ b/tests/Internal/src/android/app/WallpaperInfoTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 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 android.app; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Parcel; +import android.service.wallpaper.WallpaperService; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +/** + * Tests for hidden WallpaperInfo methods. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WallpaperInfoTest { + + @Test + public void testSupportsAmbientMode() throws Exception { + Context context = InstrumentationRegistry.getTargetContext(); + + Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); + intent.setPackage("com.android.internal.tests"); + PackageManager pm = context.getPackageManager(); + List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA); + assertEquals(1, result.size()); + ResolveInfo info = result.get(0); + WallpaperInfo wallpaperInfo = new WallpaperInfo(context, info); + + // Defined as true in the XML + assertTrue("supportsAmbientMode should be true, as defined in the XML.", + wallpaperInfo.getSupportsAmbientMode()); + Parcel parcel = Parcel.obtain(); + wallpaperInfo.writeToParcel(parcel, 0 /* flags */); + parcel.setDataPosition(0); + WallpaperInfo fromParcel = WallpaperInfo.CREATOR.createFromParcel(parcel); + assertTrue("supportsAmbientMode should have been restored from parcelable", + fromParcel.getSupportsAmbientMode()); + parcel.recycle(); + } +} + diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java new file mode 100644 index 000000000000..8d8fc84126ec --- /dev/null +++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 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 android.service.wallpaper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.support.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SmallTest +@RunWith(JUnit4.class) +public class WallpaperServiceTest { + + @Test + public void testDeliversAmbientModeChanged() { + int[] ambientModeChangedCount = {0}; + WallpaperService service = new WallpaperService() { + @Override + public Engine onCreateEngine() { + return new Engine() { + @Override + public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) { + ambientModeChangedCount[0]++; + } + }; + } + }; + WallpaperService.Engine engine = service.onCreateEngine(); + engine.setCreated(true); + + engine.doAmbientModeChanged(false, false); + assertFalse("ambient mode should be false", engine.isInAmbientMode()); + assertEquals("onAmbientModeChanged should have been called", + ambientModeChangedCount[0], 1); + + engine.doAmbientModeChanged(true, false); + assertTrue("ambient mode should be false", engine.isInAmbientMode()); + assertEquals("onAmbientModeChanged should have been called", + ambientModeChangedCount[0], 2); + } + +} diff --git a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java index d46fff4db65a..a7d5ae8f69a3 100644 --- a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java +++ b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java @@ -99,6 +99,21 @@ public class TonalTest { } @Test + public void tonal_rangeTest() { + Tonal.ConfigParser config = new Tonal.ConfigParser(InstrumentationRegistry.getContext()); + for (Tonal.TonalPalette palette : config.getTonalPalettes()) { + assertTrue("minHue should be >= to 0.", palette.minHue >= 0); + assertTrue("maxHue should be <= to 360.", palette.maxHue <= 360); + + assertTrue("S should be >= to 0.", palette.s[0] >= 0); + assertTrue("S should be <= to 1.", palette.s[1] <= 1); + + assertTrue("L should be >= to 0.", palette.l[0] >= 0); + assertTrue("L should be <= to 1.", palette.l[1] <= 1); + } + } + + @Test public void tonal_blacklistTest() { // Make sure that palette generation will fail. final Tonal tonal = new Tonal(InstrumentationRegistry.getContext()); diff --git a/tests/Internal/src/stub/DummyWallpaperService.java b/tests/Internal/src/stub/DummyWallpaperService.java new file mode 100644 index 000000000000..084c036bea26 --- /dev/null +++ b/tests/Internal/src/stub/DummyWallpaperService.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 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 stub; + +import android.service.wallpaper.WallpaperService; + +/** + * Dummy wallpaper service only for test purposes, won't draw anything. + */ +public class DummyWallpaperService extends WallpaperService { + @Override + public Engine onCreateEngine() { + return new Engine(); + } +} diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk new file mode 100644 index 000000000000..291ba78758b4 --- /dev/null +++ b/tests/JankBench/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml + +LOCAL_SDK_VERSION := current + +LOCAL_USE_AAPT2 := true + +# omit gradle 'build' dir +LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java) + +# use appcompat/support lib from the tree, so improvements/ +# regressions are reflected in test data +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/app/src/main/res \ + + +LOCAL_STATIC_ANDROID_LIBRARIES := \ + $(ANDROID_SUPPORT_DESIGN_TARGETS) \ + android-support-v4 \ + android-support-v7-appcompat \ + android-support-v7-cardview \ + android-support-v7-recyclerview \ + android-support-v17-leanback \ + +LOCAL_STATIC_JAVA_LIBRARIES := \ + apache-commons-math \ + junit + + +LOCAL_PACKAGE_NAME := JankBench + +LOCAL_COMPATIBILITY_SUITE := device-tests + +include $(BUILD_PACKAGE) diff --git a/tests/JankBench/app/src/androidTest/java/com/android/benchmark/ApplicationTest.java b/tests/JankBench/app/src/androidTest/java/com/android/benchmark/ApplicationTest.java new file mode 100644 index 000000000000..79aff900fd0e --- /dev/null +++ b/tests/JankBench/app/src/androidTest/java/com/android/benchmark/ApplicationTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 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. + */ + +/* + * Copyright (C) 2015 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.benchmark; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + public ApplicationTest() { + super(Application.class); + } +} diff --git a/tests/JankBench/app/src/main/AndroidManifest.xml b/tests/JankBench/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..58aa66fcd05d --- /dev/null +++ b/tests/JankBench/app/src/main/AndroidManifest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2015 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. + ~ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.benchmark"> + + <uses-sdk android:minSdkVersion="24" /> + + <android:uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <android:uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <android:uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + <activity + android:name=".app.HomeActivity" + android:label="@string/app_name" + android:theme="@style/AppTheme.NoActionBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".app.RunLocalBenchmarksActivity" + android:exported="true"> + <intent-filter> + <action android:name="com.android.benchmark.ACTION_BENCHMARK" /> + </intent-filter> + + <meta-data + android:name="com.android.benchmark.benchmark_group" + android:resource="@xml/benchmark" /> + </activity> + <activity android:name=".ui.ListViewScrollActivity" /> + <activity android:name=".ui.ImageListViewScrollActivity" /> + <activity android:name=".ui.ShadowGridActivity" /> + <activity android:name=".ui.TextScrollActivity" /> + <activity android:name=".ui.EditTextInputActivity" /> + <activity android:name=".synthetic.MemoryActivity" /> + <activity android:name=".ui.FullScreenOverdrawActivity"></activity> + <activity android:name=".ui.BitmapUploadActivity"></activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/BenchmarkDashboardFragment.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/BenchmarkDashboardFragment.java new file mode 100644 index 000000000000..b0a97ae0995b --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/BenchmarkDashboardFragment.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 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.benchmark.app; + +import android.support.v4.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.benchmark.R; + +/** + * Fragment for the Benchmark dashboard + */ +public class BenchmarkDashboardFragment extends Fragment { + + public BenchmarkDashboardFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_dashboard, container, false); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/BenchmarkListAdapter.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/BenchmarkListAdapter.java new file mode 100644 index 000000000000..7419b30814f6 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/BenchmarkListAdapter.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 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.benchmark.app; + +import android.graphics.Typeface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseExpandableListAdapter; +import android.widget.CheckBox; +import android.widget.TextView; + +import com.android.benchmark.registry.BenchmarkGroup; +import com.android.benchmark.registry.BenchmarkRegistry; +import com.android.benchmark.R; + +/** + * + */ +public class BenchmarkListAdapter extends BaseExpandableListAdapter { + + private final LayoutInflater mInflater; + private final BenchmarkRegistry mRegistry; + + BenchmarkListAdapter(LayoutInflater inflater, + BenchmarkRegistry registry) { + mInflater = inflater; + mRegistry = registry; + } + + @Override + public int getGroupCount() { + return mRegistry.getGroupCount(); + } + + @Override + public int getChildrenCount(int groupPosition) { + return mRegistry.getBenchmarkCount(groupPosition); + } + + @Override + public Object getGroup(int groupPosition) { + return mRegistry.getBenchmarkGroup(groupPosition); + } + + @Override + public Object getChild(int groupPosition, int childPosition) { + BenchmarkGroup benchmarkGroup = mRegistry.getBenchmarkGroup(groupPosition); + + if (benchmarkGroup != null) { + return benchmarkGroup.getBenchmarks()[childPosition]; + } + + return null; + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return childPosition; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + BenchmarkGroup group = (BenchmarkGroup) getGroup(groupPosition); + if (convertView == null) { + convertView = mInflater.inflate(R.layout.benchmark_list_group_row, null); + } + + TextView title = (TextView) convertView.findViewById(R.id.group_name); + title.setTypeface(null, Typeface.BOLD); + title.setText(group.getTitle()); + return convertView; + } + + @Override + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, + View convertView, ViewGroup parent) { + BenchmarkGroup.Benchmark benchmark = + (BenchmarkGroup.Benchmark) getChild(groupPosition, childPosition); + if (convertView == null) { + convertView = mInflater.inflate(R.layout.benchmark_list_item, null); + } + + TextView name = (TextView) convertView.findViewById(R.id.benchmark_name); + name.setText(benchmark.getName()); + CheckBox enabledBox = (CheckBox) convertView.findViewById(R.id.benchmark_enable_checkbox); + enabledBox.setOnClickListener(benchmark); + enabledBox.setChecked(benchmark.isEnabled()); + + return convertView; + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + public int getChildrenHeight() { + // TODO + return 1024; + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java new file mode 100644 index 000000000000..79bafd62e2a2 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015 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.benchmark.app; + +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ExpandableListView; +import android.widget.Toast; + +import com.android.benchmark.registry.BenchmarkRegistry; +import com.android.benchmark.R; +import com.android.benchmark.results.GlobalResultsStore; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.Queue; + +public class HomeActivity extends AppCompatActivity implements Button.OnClickListener { + + private FloatingActionButton mStartButton; + private BenchmarkRegistry mRegistry; + private Queue<Intent> mRunnableBenchmarks; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_home); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + mStartButton = (FloatingActionButton) findViewById(R.id.start_button); + mStartButton.setActivated(true); + mStartButton.setOnClickListener(this); + + mRegistry = new BenchmarkRegistry(this); + + mRunnableBenchmarks = new LinkedList<>(); + + ExpandableListView listView = (ExpandableListView) findViewById(R.id.test_list); + BenchmarkListAdapter adapter = + new BenchmarkListAdapter(LayoutInflater.from(this), mRegistry); + listView.setAdapter(adapter); + + adapter.notifyDataSetChanged(); + ViewGroup.LayoutParams layoutParams = listView.getLayoutParams(); + layoutParams.height = 2048; + listView.setLayoutParams(layoutParams); + listView.requestLayout(); + System.out.println(System.getProperties().stringPropertyNames()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... voids) { + try { + HomeActivity.this.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(HomeActivity.this, "Exporting...", Toast.LENGTH_LONG).show(); + } + }); + GlobalResultsStore.getInstance(HomeActivity.this).exportToCsv(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + HomeActivity.this.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(HomeActivity.this, "Done", Toast.LENGTH_LONG).show(); + } + }); + } + }.execute(); + + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onClick(View v) { + final int groupCount = mRegistry.getGroupCount(); + for (int i = 0; i < groupCount; i++) { + + Intent intent = mRegistry.getBenchmarkGroup(i).getIntent(); + if (intent != null) { + mRunnableBenchmarks.add(intent); + } + } + + handleNextBenchmark(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + } + + private void handleNextBenchmark() { + Intent nextIntent = mRunnableBenchmarks.peek(); + startActivityForResult(nextIntent, 0); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/PerfTimeline.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/PerfTimeline.java new file mode 100644 index 000000000000..1c82d6db3483 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/PerfTimeline.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2015 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.benchmark.app; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.*; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.View; + +import com.android.benchmark.R; + + +/** + * TODO: document your custom view class. + */ +public class PerfTimeline extends View { + private String mExampleString; // TODO: use a default from R.string... + private int mExampleColor = Color.RED; // TODO: use a default from R.color... + private float mExampleDimension = 300; // TODO: use a default from R.dimen... + + private TextPaint mTextPaint; + private float mTextWidth; + private float mTextHeight; + + private Paint mPaintBaseLow; + private Paint mPaintBaseHigh; + private Paint mPaintValue; + + + public float[] mLinesLow; + public float[] mLinesHigh; + public float[] mLinesValue; + + public PerfTimeline(Context context) { + super(context); + init(null, 0); + } + + public PerfTimeline(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public PerfTimeline(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + private void init(AttributeSet attrs, int defStyle) { + // Load attributes + final TypedArray a = getContext().obtainStyledAttributes( + attrs, R.styleable.PerfTimeline, defStyle, 0); + + mExampleString = "xx";//a.getString(R.styleable.PerfTimeline_exampleString, "xx"); + mExampleColor = a.getColor(R.styleable.PerfTimeline_exampleColor, mExampleColor); + // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with + // values that should fall on pixel boundaries. + mExampleDimension = a.getDimension( + R.styleable.PerfTimeline_exampleDimension, + mExampleDimension); + + a.recycle(); + + // Set up a default TextPaint object + mTextPaint = new TextPaint(); + mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setTextAlign(Paint.Align.LEFT); + + // Update TextPaint and text measurements from attributes + invalidateTextPaintAndMeasurements(); + + mPaintBaseLow = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaintBaseLow.setStyle(Paint.Style.FILL); + mPaintBaseLow.setColor(0xff000000); + + mPaintBaseHigh = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaintBaseHigh.setStyle(Paint.Style.FILL); + mPaintBaseHigh.setColor(0x7f7f7f7f); + + mPaintValue = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaintValue.setStyle(Paint.Style.FILL); + mPaintValue.setColor(0x7fff0000); + + } + + private void invalidateTextPaintAndMeasurements() { + mTextPaint.setTextSize(mExampleDimension); + mTextPaint.setColor(mExampleColor); + mTextWidth = mTextPaint.measureText(mExampleString); + + Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); + mTextHeight = fontMetrics.bottom; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // TODO: consider storing these as member variables to reduce + // allocations per draw cycle. + int paddingLeft = getPaddingLeft(); + int paddingTop = getPaddingTop(); + int paddingRight = getPaddingRight(); + int paddingBottom = getPaddingBottom(); + + int contentWidth = getWidth() - paddingLeft - paddingRight; + int contentHeight = getHeight() - paddingTop - paddingBottom; + + // Draw the text. + //canvas.drawText(mExampleString, + // paddingLeft + (contentWidth - mTextWidth) / 2, + // paddingTop + (contentHeight + mTextHeight) / 2, + // mTextPaint); + + + + + // Draw the shadow + //RectF rf = new RectF(10.f, 10.f, 100.f, 100.f); + //canvas.drawOval(rf, mShadowPaint); + + if (mLinesLow != null) { + canvas.drawLines(mLinesLow, mPaintBaseLow); + } + if (mLinesHigh != null) { + canvas.drawLines(mLinesHigh, mPaintBaseHigh); + } + if (mLinesValue != null) { + canvas.drawLines(mLinesValue, mPaintValue); + } + + +/* + // Draw the pie slices + for (int i = 0; i < mData.size(); ++i) { + Item it = mData.get(i); + mPiePaint.setShader(it.mShader); + canvas.drawArc(mBounds, + 360 - it.mEndAngle, + it.mEndAngle - it.mStartAngle, + true, mPiePaint); + } +*/ + // Draw the pointer + //canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint); + //canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint); + } + + /** + * Gets the example string attribute value. + * + * @return The example string attribute value. + */ + public String getExampleString() { + return mExampleString; + } + + /** + * Sets the view's example string attribute value. In the example view, this string + * is the text to draw. + * + * @param exampleString The example string attribute value to use. + */ + public void setExampleString(String exampleString) { + mExampleString = exampleString; + invalidateTextPaintAndMeasurements(); + } + + /** + * Gets the example color attribute value. + * + * @return The example color attribute value. + */ + public int getExampleColor() { + return mExampleColor; + } + + /** + * Sets the view's example color attribute value. In the example view, this color + * is the font color. + * + * @param exampleColor The example color attribute value to use. + */ + public void setExampleColor(int exampleColor) { + mExampleColor = exampleColor; + invalidateTextPaintAndMeasurements(); + } + + /** + * Gets the example dimension attribute value. + * + * @return The example dimension attribute value. + */ + public float getExampleDimension() { + return mExampleDimension; + } + + /** + * Sets the view's example dimension attribute value. In the example view, this dimension + * is the font size. + * + * @param exampleDimension The example dimension attribute value to use. + */ + public void setExampleDimension(float exampleDimension) { + mExampleDimension = exampleDimension; + invalidateTextPaintAndMeasurements(); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java new file mode 100644 index 000000000000..7641d0095a70 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2015 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.benchmark.app; + +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.ListFragment; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.android.benchmark.R; +import com.android.benchmark.registry.BenchmarkGroup; +import com.android.benchmark.registry.BenchmarkRegistry; +import com.android.benchmark.results.GlobalResultsStore; +import com.android.benchmark.results.UiBenchmarkResult; +import com.android.benchmark.synthetic.MemoryActivity; +import com.android.benchmark.ui.BitmapUploadActivity; +import com.android.benchmark.ui.EditTextInputActivity; +import com.android.benchmark.ui.FullScreenOverdrawActivity; +import com.android.benchmark.ui.ImageListViewScrollActivity; +import com.android.benchmark.ui.ListViewScrollActivity; +import com.android.benchmark.ui.ShadowGridActivity; +import com.android.benchmark.ui.TextScrollActivity; + +import org.apache.commons.math.stat.descriptive.SummaryStatistics; + +import java.util.ArrayList; +import java.util.HashMap; + +public class RunLocalBenchmarksActivity extends AppCompatActivity { + + public static final int RUN_COUNT = 5; + + private ArrayList<LocalBenchmark> mBenchmarksToRun; + private int mBenchmarkCursor; + private int mCurrentRunId; + private boolean mFinish; + + private Handler mHandler = new Handler(); + + private static final int[] ALL_TESTS = new int[] { + R.id.benchmark_list_view_scroll, + R.id.benchmark_image_list_view_scroll, + R.id.benchmark_shadow_grid, + R.id.benchmark_text_high_hitrate, + R.id.benchmark_text_low_hitrate, + R.id.benchmark_edit_text_input, + R.id.benchmark_overdraw, + }; + + public static class LocalBenchmarksList extends ListFragment { + private ArrayList<LocalBenchmark> mBenchmarks; + private int mRunId; + + public void setBenchmarks(ArrayList<LocalBenchmark> benchmarks) { + mBenchmarks = benchmarks; + } + + public void setRunId(int id) { + mRunId = id; + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + if (getActivity().findViewById(R.id.list_fragment_container) != null) { + FragmentManager fm = getActivity().getSupportFragmentManager(); + UiResultsFragment resultsView = new UiResultsFragment(); + String testName = BenchmarkRegistry.getBenchmarkName(v.getContext(), + mBenchmarks.get(position).id); + resultsView.setRunInfo(testName, mRunId); + FragmentTransaction fragmentTransaction = fm.beginTransaction(); + fragmentTransaction.replace(R.id.list_fragment_container, resultsView); + fragmentTransaction.addToBackStack(null); + fragmentTransaction.commit(); + } + } + } + + + private class LocalBenchmark { + int id; + int runCount = 0; + int totalCount = 0; + ArrayList<String> mResultsUri = new ArrayList<>(); + + LocalBenchmark(int id, int runCount) { + this.id = id; + this.runCount = 0; + this.totalCount = runCount; + } + + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_running_list); + + initLocalBenchmarks(getIntent()); + + if (findViewById(R.id.list_fragment_container) != null) { + FragmentManager fm = getSupportFragmentManager(); + LocalBenchmarksList listView = new LocalBenchmarksList(); + listView.setListAdapter(new LocalBenchmarksListAdapter(LayoutInflater.from(this))); + listView.setBenchmarks(mBenchmarksToRun); + listView.setRunId(mCurrentRunId); + fm.beginTransaction().add(R.id.list_fragment_container, listView).commit(); + } + + TextView scoreView = (TextView) findViewById(R.id.score_text_view); + scoreView.setText("Running tests!"); + } + + private int translateBenchmarkIndex(int index) { + if (index >= 0 && index < ALL_TESTS.length) { + return ALL_TESTS[index]; + } + + return -1; + } + + private void initLocalBenchmarks(Intent intent) { + mBenchmarksToRun = new ArrayList<>(); + int[] enabledIds = intent.getIntArrayExtra(BenchmarkGroup.BENCHMARK_EXTRA_ENABLED_TESTS); + int runCount = intent.getIntExtra(BenchmarkGroup.BENCHMARK_EXTRA_RUN_COUNT, RUN_COUNT); + mFinish = intent.getBooleanExtra(BenchmarkGroup.BENCHMARK_EXTRA_FINISH, false); + + if (enabledIds == null) { + // run all tests + enabledIds = ALL_TESTS; + } + + StringBuilder idString = new StringBuilder(); + idString.append(runCount); + idString.append(System.currentTimeMillis()); + + for (int i = 0; i < enabledIds.length; i++) { + int id = enabledIds[i]; + System.out.println("considering " + id); + if (!isValidBenchmark(id)) { + System.out.println("not valid " + id); + id = translateBenchmarkIndex(id); + System.out.println("got out " + id); + System.out.println("expected: " + R.id.benchmark_overdraw); + } + + if (isValidBenchmark(id)) { + int localRunCount = runCount; + if (isCompute(id)) { + localRunCount = 1; + } + mBenchmarksToRun.add(new LocalBenchmark(id, localRunCount)); + idString.append(id); + } + } + + mBenchmarkCursor = 0; + mCurrentRunId = idString.toString().hashCode(); + } + + private boolean isCompute(int id) { + switch (id) { + case R.id.benchmark_cpu_gflops: + case R.id.benchmark_cpu_heat_soak: + case R.id.benchmark_memory_bandwidth: + case R.id.benchmark_memory_latency: + case R.id.benchmark_power_management: + return true; + default: + return false; + } + } + + private static boolean isValidBenchmark(int benchmarkId) { + switch (benchmarkId) { + case R.id.benchmark_list_view_scroll: + case R.id.benchmark_image_list_view_scroll: + case R.id.benchmark_shadow_grid: + case R.id.benchmark_text_high_hitrate: + case R.id.benchmark_text_low_hitrate: + case R.id.benchmark_edit_text_input: + case R.id.benchmark_overdraw: + case R.id.benchmark_memory_bandwidth: + case R.id.benchmark_memory_latency: + case R.id.benchmark_power_management: + case R.id.benchmark_cpu_heat_soak: + case R.id.benchmark_cpu_gflops: + return true; + default: + return false; + } + } + + @Override + protected void onResume() { + super.onResume(); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + runNextBenchmark(); + } + }, 1000); + } + + private void computeOverallScore() { + final TextView scoreView = (TextView) findViewById(R.id.score_text_view); + scoreView.setText("Computing score..."); + new AsyncTask<Void, Void, Integer>() { + @Override + protected Integer doInBackground(Void... voids) { + GlobalResultsStore gsr = + GlobalResultsStore.getInstance(RunLocalBenchmarksActivity.this); + ArrayList<Double> testLevelScores = new ArrayList<>(); + final SummaryStatistics stats = new SummaryStatistics(); + for (LocalBenchmark b : mBenchmarksToRun) { + HashMap<String, ArrayList<UiBenchmarkResult>> detailedResults = + gsr.loadDetailedResults(mCurrentRunId); + for (ArrayList<UiBenchmarkResult> testResult : detailedResults.values()) { + for (UiBenchmarkResult res : testResult) { + int score = res.getScore(); + if (score == 0) { + score = 1; + } + stats.addValue(score); + } + + testLevelScores.add(stats.getGeometricMean()); + stats.clear(); + } + + } + + for (double score : testLevelScores) { + stats.addValue(score); + } + + return (int)Math.round(stats.getGeometricMean()); + } + + @Override + protected void onPostExecute(Integer score) { + TextView view = (TextView) + RunLocalBenchmarksActivity.this.findViewById(R.id.score_text_view); + view.setText("Score: " + score); + } + }.execute(); + } + + private void runNextBenchmark() { + LocalBenchmark benchmark = mBenchmarksToRun.get(mBenchmarkCursor); + boolean runAgain = false; + + if (benchmark.runCount < benchmark.totalCount) { + runBenchmarkForId(mBenchmarksToRun.get(mBenchmarkCursor).id, benchmark.runCount++); + } else if (mBenchmarkCursor + 1 < mBenchmarksToRun.size()) { + mBenchmarkCursor++; + benchmark = mBenchmarksToRun.get(mBenchmarkCursor); + runBenchmarkForId(benchmark.id, benchmark.runCount++); + } else if (runAgain) { + mBenchmarkCursor = 0; + initLocalBenchmarks(getIntent()); + + runBenchmarkForId(mBenchmarksToRun.get(mBenchmarkCursor).id, benchmark.runCount); + } else if (mFinish) { + finish(); + } else { + Log.i("BENCH", "BenchmarkDone!"); + computeOverallScore(); + } + } + + private void runBenchmarkForId(int id, int iteration) { + Intent intent; + int syntheticTestId = -1; + + System.out.println("iteration: " + iteration); + + switch (id) { + case R.id.benchmark_list_view_scroll: + intent = new Intent(getApplicationContext(), ListViewScrollActivity.class); + break; + case R.id.benchmark_image_list_view_scroll: + intent = new Intent(getApplicationContext(), ImageListViewScrollActivity.class); + break; + case R.id.benchmark_shadow_grid: + intent = new Intent(getApplicationContext(), ShadowGridActivity.class); + break; + case R.id.benchmark_text_high_hitrate: + intent = new Intent(getApplicationContext(), TextScrollActivity.class); + intent.putExtra(TextScrollActivity.EXTRA_HIT_RATE, 80); + intent.putExtra(BenchmarkRegistry.EXTRA_ID, id); + break; + case R.id.benchmark_text_low_hitrate: + intent = new Intent(getApplicationContext(), TextScrollActivity.class); + intent.putExtra(TextScrollActivity.EXTRA_HIT_RATE, 20); + intent.putExtra(BenchmarkRegistry.EXTRA_ID, id); + break; + case R.id.benchmark_edit_text_input: + intent = new Intent(getApplicationContext(), EditTextInputActivity.class); + break; + case R.id.benchmark_overdraw: + intent = new Intent(getApplicationContext(), BitmapUploadActivity.class); + break; + case R.id.benchmark_memory_bandwidth: + syntheticTestId = 0; + intent = new Intent(getApplicationContext(), MemoryActivity.class); + intent.putExtra("test", syntheticTestId); + break; + case R.id.benchmark_memory_latency: + syntheticTestId = 1; + intent = new Intent(getApplicationContext(), MemoryActivity.class); + intent.putExtra("test", syntheticTestId); + break; + case R.id.benchmark_power_management: + syntheticTestId = 2; + intent = new Intent(getApplicationContext(), MemoryActivity.class); + intent.putExtra("test", syntheticTestId); + break; + case R.id.benchmark_cpu_heat_soak: + syntheticTestId = 3; + intent = new Intent(getApplicationContext(), MemoryActivity.class); + intent.putExtra("test", syntheticTestId); + break; + case R.id.benchmark_cpu_gflops: + syntheticTestId = 4; + intent = new Intent(getApplicationContext(), MemoryActivity.class); + intent.putExtra("test", syntheticTestId); + break; + + default: + intent = null; + } + + if (intent != null) { + intent.putExtra("com.android.benchmark.RUN_ID", mCurrentRunId); + intent.putExtra("com.android.benchmark.ITERATION", iteration); + startActivityForResult(intent, id & 0xffff, null); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case R.id.benchmark_shadow_grid: + case R.id.benchmark_list_view_scroll: + case R.id.benchmark_image_list_view_scroll: + case R.id.benchmark_text_high_hitrate: + case R.id.benchmark_text_low_hitrate: + case R.id.benchmark_edit_text_input: + break; + default: + } + } + + class LocalBenchmarksListAdapter extends BaseAdapter { + + private final LayoutInflater mInflater; + + LocalBenchmarksListAdapter(LayoutInflater inflater) { + mInflater = inflater; + } + + @Override + public int getCount() { + return mBenchmarksToRun.size(); + } + + @Override + public Object getItem(int i) { + return mBenchmarksToRun.get(i); + } + + @Override + public long getItemId(int i) { + return mBenchmarksToRun.get(i).id; + } + + @Override + public View getView(int i, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = mInflater.inflate(R.layout.running_benchmark_list_item, null); + } + + TextView name = (TextView) convertView.findViewById(R.id.benchmark_name); + name.setText(BenchmarkRegistry.getBenchmarkName( + RunLocalBenchmarksActivity.this, mBenchmarksToRun.get(i).id)); + return convertView; + } + + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/UiResultsFragment.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/UiResultsFragment.java new file mode 100644 index 000000000000..56e94d538ab5 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/UiResultsFragment.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2015 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.benchmark.app; + +import android.annotation.TargetApi; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.ListFragment; +import android.util.Log; +import android.view.FrameMetrics; +import android.widget.SimpleAdapter; + +import com.android.benchmark.R; +import com.android.benchmark.registry.BenchmarkGroup; +import com.android.benchmark.registry.BenchmarkRegistry; +import com.android.benchmark.results.GlobalResultsStore; +import com.android.benchmark.results.UiBenchmarkResult; + +import org.apache.commons.math.stat.descriptive.SummaryStatistics; + +import java.io.FileWriter; +import java.io.IOException; +import java.net.URI; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +@TargetApi(24) +public class UiResultsFragment extends ListFragment { + private static final String TAG = "UiResultsFragment"; + private static final int NUM_FIELDS = 20; + + private ArrayList<UiBenchmarkResult> mResults = new ArrayList<>(); + + private AsyncTask<Void, Void, ArrayList<Map<String, String>>> mLoadScoresTask = + new AsyncTask<Void, Void, ArrayList<Map<String, String>>>() { + @Override + protected ArrayList<Map<String, String>> doInBackground(Void... voids) { + String[] data; + if (mResults.size() == 0 || mResults.get(0) == null) { + data = new String[] { + "No metrics reported", "" + }; + } else { + data = new String[NUM_FIELDS * (1 + mResults.size()) + 2]; + SummaryStatistics stats = new SummaryStatistics(); + int totalFrameCount = 0; + double totalAvgFrameDuration = 0; + double total99FrameDuration = 0; + double total95FrameDuration = 0; + double total90FrameDuration = 0; + double totalLongestFrame = 0; + double totalShortestFrame = 0; + + for (int i = 0; i < mResults.size(); i++) { + int start = (i * NUM_FIELDS) + + NUM_FIELDS; + data[(start++)] = "Iteration"; + data[(start++)] = "" + i; + data[(start++)] = "Total Frames"; + int currentFrameCount = mResults.get(i).getTotalFrameCount(); + totalFrameCount += currentFrameCount; + data[(start++)] = Integer.toString(currentFrameCount); + data[(start++)] = "Average frame duration:"; + double currentAvgFrameDuration = mResults.get(i).getAverage(FrameMetrics.TOTAL_DURATION); + totalAvgFrameDuration += currentAvgFrameDuration; + data[(start++)] = String.format("%.2f", currentAvgFrameDuration); + data[(start++)] = "Frame duration 99th:"; + double current99FrameDuration = mResults.get(i).getPercentile(FrameMetrics.TOTAL_DURATION, 99); + total99FrameDuration += current99FrameDuration; + data[(start++)] = String.format("%.2f", current99FrameDuration); + data[(start++)] = "Frame duration 95th:"; + double current95FrameDuration = mResults.get(i).getPercentile(FrameMetrics.TOTAL_DURATION, 95); + total95FrameDuration += current95FrameDuration; + data[(start++)] = String.format("%.2f", current95FrameDuration); + data[(start++)] = "Frame duration 90th:"; + double current90FrameDuration = mResults.get(i).getPercentile(FrameMetrics.TOTAL_DURATION, 90); + total90FrameDuration += current90FrameDuration; + data[(start++)] = String.format("%.2f", current90FrameDuration); + data[(start++)] = "Longest frame:"; + double longestFrame = mResults.get(i).getMaximum(FrameMetrics.TOTAL_DURATION); + if (totalLongestFrame == 0 || longestFrame > totalLongestFrame) { + totalLongestFrame = longestFrame; + } + data[(start++)] = String.format("%.2f", longestFrame); + data[(start++)] = "Shortest frame:"; + double shortestFrame = mResults.get(i).getMinimum(FrameMetrics.TOTAL_DURATION); + if (totalShortestFrame == 0 || totalShortestFrame > shortestFrame) { + totalShortestFrame = shortestFrame; + } + data[(start++)] = String.format("%.2f", shortestFrame); + data[(start++)] = "Score:"; + double score = mResults.get(i).getScore(); + stats.addValue(score); + data[(start++)] = String.format("%.2f", score); + data[(start++)] = "=============="; + data[(start++)] = "============================"; + }; + + int start = 0; + data[0] = "Overall: "; + data[1] = ""; + data[(start++)] = "Total Frames"; + data[(start++)] = Integer.toString(totalFrameCount); + data[(start++)] = "Average frame duration:"; + data[(start++)] = String.format("%.2f", totalAvgFrameDuration / mResults.size()); + data[(start++)] = "Frame duration 99th:"; + data[(start++)] = String.format("%.2f", total99FrameDuration / mResults.size()); + data[(start++)] = "Frame duration 95th:"; + data[(start++)] = String.format("%.2f", total95FrameDuration / mResults.size()); + data[(start++)] = "Frame duration 90th:"; + data[(start++)] = String.format("%.2f", total90FrameDuration / mResults.size()); + data[(start++)] = "Longest frame:"; + data[(start++)] = String.format("%.2f", totalLongestFrame); + data[(start++)] = "Shortest frame:"; + data[(start++)] = String.format("%.2f", totalShortestFrame); + data[(start++)] = "Score:"; + data[(start++)] = String.format("%.2f", stats.getGeometricMean()); + data[(start++)] = "=============="; + data[(start++)] = "============================"; + } + + ArrayList<Map<String, String>> dataMap = new ArrayList<>(); + for (int i = 0; i < data.length - 1; i += 2) { + HashMap<String, String> map = new HashMap<>(); + map.put("name", data[i]); + map.put("value", data[i + 1]); + dataMap.add(map); + } + + return dataMap; + } + + @Override + protected void onPostExecute(ArrayList<Map<String, String>> dataMap) { + setListAdapter(new SimpleAdapter(getActivity(), dataMap, R.layout.results_list_item, + new String[] {"name", "value"}, new int[] { R.id.result_name, R.id.result_value })); + setListShown(true); + } + }; + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setListShown(false); + mLoadScoresTask.execute(); + } + + public void setRunInfo(String name, int runId) { + mResults = GlobalResultsStore.getInstance(getActivity()).loadTestResults(name, runId); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkCategory.java b/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkCategory.java new file mode 100644 index 000000000000..d91e5798f05b --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkCategory.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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.benchmark.registry; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents the category of a particular benchmark. + */ +@Retention(RetentionPolicy.SOURCE) +@IntDef({BenchmarkCategory.GENERIC, BenchmarkCategory.UI, BenchmarkCategory.COMPUTE}) +@interface BenchmarkCategory { + int GENERIC = 0; + int UI = 1; + int COMPUTE = 2; +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkGroup.java b/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkGroup.java new file mode 100644 index 000000000000..4cb7716ee5e9 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkGroup.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2015 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.benchmark.registry; + +import android.content.ComponentName; +import android.content.Intent; +import android.view.View; +import android.widget.CheckBox; + +/** + * Logical grouping of benchmarks + */ +public class BenchmarkGroup { + public static final String BENCHMARK_EXTRA_ENABLED_TESTS = + "com.android.benchmark.EXTRA_ENABLED_BENCHMARK_IDS"; + + public static final String BENCHMARK_EXTRA_RUN_COUNT = + "com.android.benchmark.EXTRA_RUN_COUNT"; + public static final String BENCHMARK_EXTRA_FINISH = "com.android.benchmark.FINISH_WHEN_DONE"; + + public static class Benchmark implements CheckBox.OnClickListener { + /** The name of this individual benchmark test */ + private final String mName; + + /** The category of this individual benchmark test */ + private final @BenchmarkCategory int mCategory; + + /** Human-readable description of the benchmark */ + private final String mDescription; + + private final int mId; + + private boolean mEnabled; + + Benchmark(int id, String name, @BenchmarkCategory int category, String description) { + mId = id; + mName = name; + mCategory = category; + mDescription = description; + mEnabled = true; + } + + public boolean isEnabled() { return mEnabled; } + + public void setEnabled(boolean enabled) { mEnabled = enabled; } + + public int getId() { return mId; } + + public String getDescription() { return mDescription; } + + public @BenchmarkCategory int getCategory() { return mCategory; } + + public String getName() { return mName; } + + @Override + public void onClick(View view) { + setEnabled(((CheckBox)view).isChecked()); + } + } + + /** + * Component for this benchmark group. + */ + private final ComponentName mComponentName; + + /** + * Benchmark title, showed in the {@link android.widget.ListView} + */ + private final String mTitle; + + /** + * List of all benchmarks exported by this group + */ + private final Benchmark[] mBenchmarks; + + /** + * The intent to launch the benchmark + */ + private final Intent mIntent; + + /** Human-readable description of the benchmark group */ + private final String mDescription; + + BenchmarkGroup(ComponentName componentName, String title, + String description, Benchmark[] benchmarks, Intent intent) { + mComponentName = componentName; + mTitle = title; + mBenchmarks = benchmarks; + mDescription = description; + mIntent = intent; + } + + public Intent getIntent() { + int[] enabledBenchmarksIds = getEnabledBenchmarksIds(); + if (enabledBenchmarksIds.length != 0) { + mIntent.putExtra(BENCHMARK_EXTRA_ENABLED_TESTS, enabledBenchmarksIds); + return mIntent; + } + + return null; + } + + public ComponentName getComponentName() { + return mComponentName; + } + + public String getTitle() { + return mTitle; + } + + public Benchmark[] getBenchmarks() { + return mBenchmarks; + } + + public String getDescription() { + return mDescription; + } + + private int[] getEnabledBenchmarksIds() { + int enabledBenchmarkCount = 0; + for (int i = 0; i < mBenchmarks.length; i++) { + if (mBenchmarks[i].isEnabled()) { + enabledBenchmarkCount++; + } + } + + int writeIndex = 0; + int[] enabledBenchmarks = new int[enabledBenchmarkCount]; + for (int i = 0; i < mBenchmarks.length; i++) { + if (mBenchmarks[i].isEnabled()) { + enabledBenchmarks[writeIndex++] = mBenchmarks[i].getId(); + } + } + + return enabledBenchmarks; + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkRegistry.java b/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkRegistry.java new file mode 100644 index 000000000000..89c6aedd8b5c --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/registry/BenchmarkRegistry.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2015 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.benchmark.registry; + +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.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.Xml; + +import com.android.benchmark.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + */ +public class BenchmarkRegistry { + + /** Metadata key for benchmark XML data */ + private static final String BENCHMARK_GROUP_META_KEY = + "com.android.benchmark.benchmark_group"; + + /** Intent action specifying an activity that runs a single benchmark test. */ + private static final String ACTION_BENCHMARK = "com.android.benchmark.ACTION_BENCHMARK"; + public static final String EXTRA_ID = "com.android.benchmark.EXTRA_ID"; + + private static final String TAG_BENCHMARK_GROUP = "com.android.benchmark.BenchmarkGroup"; + private static final String TAG_BENCHMARK = "com.android.benchmark.Benchmark"; + + private List<BenchmarkGroup> mGroups; + + private final Context mContext; + + public BenchmarkRegistry(Context context) { + mContext = context; + mGroups = new ArrayList<>(); + loadBenchmarks(); + } + + private Intent getIntentFromInfo(ActivityInfo inf) { + Intent intent = new Intent(); + intent.setClassName(inf.packageName, inf.name); + return intent; + } + + public void loadBenchmarks() { + Intent intent = new Intent(ACTION_BENCHMARK); + intent.setPackage(mContext.getPackageName()); + + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, + PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); + + for (ResolveInfo inf : resolveInfos) { + List<BenchmarkGroup> groups = parseBenchmarkGroup(inf.activityInfo); + if (groups != null) { + mGroups.addAll(groups); + } + } + } + + private boolean seekToTag(XmlPullParser parser, String tag) + throws XmlPullParserException, IOException { + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + } + return eventType != XmlPullParser.END_DOCUMENT && tag.equals(parser.getName()); + } + + @BenchmarkCategory int getCategory(int category) { + switch (category) { + case BenchmarkCategory.COMPUTE: + return BenchmarkCategory.COMPUTE; + case BenchmarkCategory.UI: + return BenchmarkCategory.UI; + default: + return BenchmarkCategory.GENERIC; + } + } + + private List<BenchmarkGroup> parseBenchmarkGroup(ActivityInfo activityInfo) { + PackageManager pm = mContext.getPackageManager(); + + ComponentName componentName = new ComponentName( + activityInfo.packageName, activityInfo.name); + + SparseArray<List<BenchmarkGroup.Benchmark>> benchmarks = new SparseArray<>(); + String groupName, groupDescription; + try (XmlResourceParser parser = activityInfo.loadXmlMetaData(pm, BENCHMARK_GROUP_META_KEY)) { + + if (!seekToTag(parser, TAG_BENCHMARK_GROUP)) { + return null; + } + + Resources res = pm.getResourcesForActivity(componentName); + AttributeSet attributeSet = Xml.asAttributeSet(parser); + TypedArray groupAttribs = res.obtainAttributes(attributeSet, R.styleable.BenchmarkGroup); + + groupName = groupAttribs.getString(R.styleable.BenchmarkGroup_name); + groupDescription = groupAttribs.getString(R.styleable.BenchmarkGroup_description); + groupAttribs.recycle(); + parser.next(); + + while (seekToTag(parser, TAG_BENCHMARK)) { + TypedArray benchAttribs = + res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Benchmark); + int id = benchAttribs.getResourceId(R.styleable.Benchmark_id, -1); + String testName = benchAttribs.getString(R.styleable.Benchmark_name); + String testDescription = benchAttribs.getString(R.styleable.Benchmark_description); + int testCategory = benchAttribs.getInt(R.styleable.Benchmark_category, + BenchmarkCategory.GENERIC); + int category = getCategory(testCategory); + BenchmarkGroup.Benchmark benchmark = new BenchmarkGroup.Benchmark( + id, testName, category, testDescription); + List<BenchmarkGroup.Benchmark> benches = benchmarks.get(category); + if (benches == null) { + benches = new ArrayList<>(); + benchmarks.append(category, benches); + } + + benches.add(benchmark); + + benchAttribs.recycle(); + parser.next(); + } + } catch (PackageManager.NameNotFoundException | XmlPullParserException | IOException e) { + return null; + } + + List<BenchmarkGroup> result = new ArrayList<>(); + Intent testIntent = getIntentFromInfo(activityInfo); + for (int i = 0; i < benchmarks.size(); i++) { + int cat = benchmarks.keyAt(i); + List<BenchmarkGroup.Benchmark> thisGroup = benchmarks.get(cat); + BenchmarkGroup.Benchmark[] benchmarkArray = + new BenchmarkGroup.Benchmark[thisGroup.size()]; + thisGroup.toArray(benchmarkArray); + result.add(new BenchmarkGroup(componentName, + groupName + " - " + getCategoryString(cat), groupDescription, benchmarkArray, + testIntent)); + } + + return result; + } + + public int getGroupCount() { + return mGroups.size(); + } + + public int getBenchmarkCount(int benchmarkIndex) { + BenchmarkGroup group = getBenchmarkGroup(benchmarkIndex); + if (group != null) { + return group.getBenchmarks().length; + } + return 0; + } + + public BenchmarkGroup getBenchmarkGroup(int benchmarkIndex) { + if (benchmarkIndex >= mGroups.size()) { + return null; + } + + return mGroups.get(benchmarkIndex); + } + + public static String getCategoryString(int category) { + switch (category) { + case BenchmarkCategory.UI: + return "UI"; + case BenchmarkCategory.COMPUTE: + return "Compute"; + case BenchmarkCategory.GENERIC: + return "Generic"; + default: + return ""; + } + } + + public static String getBenchmarkName(Context context, int benchmarkId) { + switch (benchmarkId) { + case R.id.benchmark_list_view_scroll: + return context.getString(R.string.list_view_scroll_name); + case R.id.benchmark_image_list_view_scroll: + return context.getString(R.string.image_list_view_scroll_name); + case R.id.benchmark_shadow_grid: + return context.getString(R.string.shadow_grid_name); + case R.id.benchmark_text_high_hitrate: + return context.getString(R.string.text_high_hitrate_name); + case R.id.benchmark_text_low_hitrate: + return context.getString(R.string.text_low_hitrate_name); + case R.id.benchmark_edit_text_input: + return context.getString(R.string.edit_text_input_name); + case R.id.benchmark_memory_bandwidth: + return context.getString(R.string.memory_bandwidth_name); + case R.id.benchmark_memory_latency: + return context.getString(R.string.memory_latency_name); + case R.id.benchmark_power_management: + return context.getString(R.string.power_management_name); + case R.id.benchmark_cpu_heat_soak: + return context.getString(R.string.cpu_heat_soak_name); + case R.id.benchmark_cpu_gflops: + return context.getString(R.string.cpu_gflops_name); + case R.id.benchmark_overdraw: + return context.getString(R.string.overdraw_name); + default: + return "Some Benchmark"; + } + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/results/GlobalResultsStore.java b/tests/JankBench/app/src/main/java/com/android/benchmark/results/GlobalResultsStore.java new file mode 100644 index 000000000000..5d0cba278053 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/results/GlobalResultsStore.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2015 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.benchmark.results; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.view.FrameMetrics; +import android.widget.Toast; + +import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; + +import java.io.FileWriter; +import java.io.IOException; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +public class GlobalResultsStore extends SQLiteOpenHelper { + private static final int VERSION = 2; + + private static GlobalResultsStore sInstance; + private static final String UI_RESULTS_TABLE = "ui_results"; + + private final Context mContext; + + private GlobalResultsStore(Context context) { + super(context, "BenchmarkResults", null, VERSION); + mContext = context; + } + + public static GlobalResultsStore getInstance(Context context) { + if (sInstance == null) { + sInstance = new GlobalResultsStore(context.getApplicationContext()); + } + + return sInstance; + } + + @Override + public void onCreate(SQLiteDatabase sqLiteDatabase) { + sqLiteDatabase.execSQL("CREATE TABLE " + UI_RESULTS_TABLE + " (" + + " _id INTEGER PRIMARY KEY AUTOINCREMENT," + + " name TEXT," + + " run_id INTEGER," + + " iteration INTEGER," + + " timestamp TEXT," + + " unknown_delay REAL," + + " input REAL," + + " animation REAL," + + " layout REAL," + + " draw REAL," + + " sync REAL," + + " command_issue REAL," + + " swap_buffers REAL," + + " total_duration REAL," + + " jank_frame BOOLEAN, " + + " device_charging INTEGER);"); + } + + public void storeRunResults(String testName, int runId, int iteration, + UiBenchmarkResult result) { + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + String date = DateFormat.getDateTimeInstance().format(new Date()); + int jankIndexIndex = 0; + int[] sortedJankIndices = result.getSortedJankFrameIndices(); + int totalFrameCount = result.getTotalFrameCount(); + for (int frameIdx = 0; frameIdx < totalFrameCount; frameIdx++) { + ContentValues cv = new ContentValues(); + cv.put("name", testName); + cv.put("run_id", runId); + cv.put("iteration", iteration); + cv.put("timestamp", date); + cv.put("unknown_delay", + result.getMetricAtIndex(frameIdx, FrameMetrics.UNKNOWN_DELAY_DURATION)); + cv.put("input", + result.getMetricAtIndex(frameIdx, FrameMetrics.INPUT_HANDLING_DURATION)); + cv.put("animation", + result.getMetricAtIndex(frameIdx, FrameMetrics.ANIMATION_DURATION)); + cv.put("layout", + result.getMetricAtIndex(frameIdx, FrameMetrics.LAYOUT_MEASURE_DURATION)); + cv.put("draw", + result.getMetricAtIndex(frameIdx, FrameMetrics.DRAW_DURATION)); + cv.put("sync", + result.getMetricAtIndex(frameIdx, FrameMetrics.SYNC_DURATION)); + cv.put("command_issue", + result.getMetricAtIndex(frameIdx, FrameMetrics.COMMAND_ISSUE_DURATION)); + cv.put("swap_buffers", + result.getMetricAtIndex(frameIdx, FrameMetrics.SWAP_BUFFERS_DURATION)); + cv.put("total_duration", + result.getMetricAtIndex(frameIdx, FrameMetrics.TOTAL_DURATION)); + if (jankIndexIndex < sortedJankIndices.length && + sortedJankIndices[jankIndexIndex] == frameIdx) { + jankIndexIndex++; + cv.put("jank_frame", true); + } else { + cv.put("jank_frame", false); + } + db.insert(UI_RESULTS_TABLE, null, cv); + } + db.setTransactionSuccessful(); + Toast.makeText(mContext, "Score: " + result.getScore() + + " Jank: " + (100 * sortedJankIndices.length) / (float) totalFrameCount + "%", + Toast.LENGTH_LONG).show(); + } finally { + db.endTransaction(); + } + + } + + public ArrayList<UiBenchmarkResult> loadTestResults(String testName, int runId) { + SQLiteDatabase db = getReadableDatabase(); + ArrayList<UiBenchmarkResult> resultList = new ArrayList<>(); + try { + String[] columnsToQuery = new String[] { + "name", + "run_id", + "iteration", + "unknown_delay", + "input", + "animation", + "layout", + "draw", + "sync", + "command_issue", + "swap_buffers", + "total_duration", + }; + + Cursor cursor = db.query( + UI_RESULTS_TABLE, columnsToQuery, "run_id=? AND name=?", + new String[] { Integer.toString(runId), testName }, null, null, "iteration"); + + double[] values = new double[columnsToQuery.length - 3]; + + while (cursor.moveToNext()) { + int iteration = cursor.getInt(cursor.getColumnIndexOrThrow("iteration")); + + values[0] = cursor.getDouble( + cursor.getColumnIndexOrThrow("unknown_delay")); + values[1] = cursor.getDouble( + cursor.getColumnIndexOrThrow("input")); + values[2] = cursor.getDouble( + cursor.getColumnIndexOrThrow("animation")); + values[3] = cursor.getDouble( + cursor.getColumnIndexOrThrow("layout")); + values[4] = cursor.getDouble( + cursor.getColumnIndexOrThrow("draw")); + values[5] = cursor.getDouble( + cursor.getColumnIndexOrThrow("sync")); + values[6] = cursor.getDouble( + cursor.getColumnIndexOrThrow("command_issue")); + values[7] = cursor.getDouble( + cursor.getColumnIndexOrThrow("swap_buffers")); + values[8] = cursor.getDouble( + cursor.getColumnIndexOrThrow("total_duration")); + + UiBenchmarkResult iterationResult; + if (resultList.size() == iteration) { + iterationResult = new UiBenchmarkResult(values); + resultList.add(iteration, iterationResult); + } else { + iterationResult = resultList.get(iteration); + iterationResult.update(values); + } + } + + cursor.close(); + } finally { + db.close(); + } + + int total = resultList.get(0).getTotalFrameCount(); + for (int i = 0; i < total; i++) { + System.out.println(""+ resultList.get(0).getMetricAtIndex(0, FrameMetrics.TOTAL_DURATION)); + } + + return resultList; + } + + public HashMap<String, ArrayList<UiBenchmarkResult>> loadDetailedResults(int runId) { + SQLiteDatabase db = getReadableDatabase(); + HashMap<String, ArrayList<UiBenchmarkResult>> results = new HashMap<>(); + try { + String[] columnsToQuery = new String[] { + "name", + "run_id", + "iteration", + "unknown_delay", + "input", + "animation", + "layout", + "draw", + "sync", + "command_issue", + "swap_buffers", + "total_duration", + }; + + Cursor cursor = db.query( + UI_RESULTS_TABLE, columnsToQuery, "run_id=?", + new String[] { Integer.toString(runId) }, null, null, "name, iteration"); + + double[] values = new double[columnsToQuery.length - 3]; + while (cursor.moveToNext()) { + int iteration = cursor.getInt(cursor.getColumnIndexOrThrow("iteration")); + String name = cursor.getString(cursor.getColumnIndexOrThrow("name")); + ArrayList<UiBenchmarkResult> resultList = results.get(name); + if (resultList == null) { + resultList = new ArrayList<>(); + results.put(name, resultList); + } + + values[0] = cursor.getDouble( + cursor.getColumnIndexOrThrow("unknown_delay")); + values[1] = cursor.getDouble( + cursor.getColumnIndexOrThrow("input")); + values[2] = cursor.getDouble( + cursor.getColumnIndexOrThrow("animation")); + values[3] = cursor.getDouble( + cursor.getColumnIndexOrThrow("layout")); + values[4] = cursor.getDouble( + cursor.getColumnIndexOrThrow("draw")); + values[5] = cursor.getDouble( + cursor.getColumnIndexOrThrow("sync")); + values[6] = cursor.getDouble( + cursor.getColumnIndexOrThrow("command_issue")); + values[7] = cursor.getDouble( + cursor.getColumnIndexOrThrow("swap_buffers")); + values[8] = cursor.getDouble( + cursor.getColumnIndexOrThrow("total_duration")); + values[8] = cursor.getDouble( + cursor.getColumnIndexOrThrow("total_duration")); + + UiBenchmarkResult iterationResult; + if (resultList.size() == iteration) { + iterationResult = new UiBenchmarkResult(values); + resultList.add(iterationResult); + } else { + iterationResult = resultList.get(iteration); + iterationResult.update(values); + } + } + + cursor.close(); + } finally { + db.close(); + } + + return results; + } + + public void exportToCsv() throws IOException { + String path = mContext.getFilesDir() + "/results-" + System.currentTimeMillis() + ".csv"; + SQLiteDatabase db = getReadableDatabase(); + + // stats across metrics for each run and each test + HashMap<String, DescriptiveStatistics> stats = new HashMap<>(); + + Cursor runIdCursor = db.query( + UI_RESULTS_TABLE, new String[] { "run_id" }, null, null, "run_id", null, null); + + while (runIdCursor.moveToNext()) { + + int runId = runIdCursor.getInt(runIdCursor.getColumnIndexOrThrow("run_id")); + HashMap<String, ArrayList<UiBenchmarkResult>> detailedResults = + loadDetailedResults(runId); + + writeRawResults(runId, detailedResults); + + DescriptiveStatistics overall = new DescriptiveStatistics(); + try (FileWriter writer = new FileWriter(path, true)) { + writer.write("Run ID, " + runId + "\n"); + writer.write("Test, Iteration, Score, Jank Penalty, Consistency Bonus, 95th, " + + "90th\n"); + for (String testName : detailedResults.keySet()) { + ArrayList<UiBenchmarkResult> results = detailedResults.get(testName); + DescriptiveStatistics scoreStats = new DescriptiveStatistics(); + DescriptiveStatistics jankPenalty = new DescriptiveStatistics(); + DescriptiveStatistics consistencyBonus = new DescriptiveStatistics(); + for (int i = 0; i < results.size(); i++) { + UiBenchmarkResult result = results.get(i); + int score = result.getScore(); + scoreStats.addValue(score); + overall.addValue(score); + jankPenalty.addValue(result.getJankPenalty()); + consistencyBonus.addValue(result.getConsistencyBonus()); + + writer.write(testName); + writer.write(","); + writer.write("" + i); + writer.write(","); + writer.write("" + score); + writer.write(","); + writer.write("" + result.getJankPenalty()); + writer.write(","); + writer.write("" + result.getConsistencyBonus()); + writer.write(","); + writer.write(Double.toString( + result.getPercentile(FrameMetrics.TOTAL_DURATION, 95))); + writer.write(","); + writer.write(Double.toString( + result.getPercentile(FrameMetrics.TOTAL_DURATION, 90))); + writer.write("\n"); + } + + writer.write("Score CV," + + (100 * scoreStats.getStandardDeviation() + / scoreStats.getMean()) + "%\n"); + writer.write("Jank Penalty CV, " + + (100 * jankPenalty.getStandardDeviation() + / jankPenalty.getMean()) + "%\n"); + writer.write("Consistency Bonus CV, " + + (100 * consistencyBonus.getStandardDeviation() + / consistencyBonus.getMean()) + "%\n"); + writer.write("\n"); + } + + writer.write("Overall Score CV," + + (100 * overall.getStandardDeviation() / overall.getMean()) + "%\n"); + writer.flush(); + } + } + + runIdCursor.close(); + } + + private void writeRawResults(int runId, + HashMap<String, ArrayList<UiBenchmarkResult>> detailedResults) { + StringBuilder path = new StringBuilder(); + path.append(mContext.getFilesDir()); + path.append("/"); + path.append(Integer.toString(runId)); + path.append(".csv"); + try (FileWriter writer = new FileWriter(path.toString())) { + for (String test : detailedResults.keySet()) { + writer.write("Test, " + test + "\n"); + writer.write("iteration, unknown delay, input, animation, layout, draw, sync, " + + "command issue, swap buffers\n"); + ArrayList<UiBenchmarkResult> runs = detailedResults.get(test); + for (int i = 0; i < runs.size(); i++) { + UiBenchmarkResult run = runs.get(i); + for (int j = 0; j < run.getTotalFrameCount(); j++) { + writer.write(Integer.toString(i) + "," + + run.getMetricAtIndex(j, FrameMetrics.UNKNOWN_DELAY_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.INPUT_HANDLING_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.ANIMATION_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.LAYOUT_MEASURE_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.DRAW_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.SYNC_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.COMMAND_ISSUE_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.SWAP_BUFFERS_DURATION) + "," + + run.getMetricAtIndex(j, FrameMetrics.TOTAL_DURATION) + "\n"); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int currentVersion) { + if (oldVersion < VERSION) { + sqLiteDatabase.execSQL("ALTER TABLE " + + UI_RESULTS_TABLE + " ADD COLUMN timestamp TEXT;"); + } + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/results/UiBenchmarkResult.java b/tests/JankBench/app/src/main/java/com/android/benchmark/results/UiBenchmarkResult.java new file mode 100644 index 000000000000..da6e05ac051b --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/results/UiBenchmarkResult.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2015 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.benchmark.results; + +import android.annotation.TargetApi; +import android.view.FrameMetrics; + +import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; +import org.apache.commons.math.stat.descriptive.SummaryStatistics; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility for storing and analyzing UI benchmark results. + */ +@TargetApi(24) +public class UiBenchmarkResult { + private static final int BASE_SCORE = 100; + private static final int ZERO_SCORE_TOTAL_DURATION_MS = 32; + private static final int JANK_PENALTY_THRESHOLD_MS = 12; + private static final int ZERO_SCORE_ABOVE_THRESHOLD_MS = + ZERO_SCORE_TOTAL_DURATION_MS - JANK_PENALTY_THRESHOLD_MS; + private static final double JANK_PENALTY_PER_MS_ABOVE_THRESHOLD = + BASE_SCORE / (double)ZERO_SCORE_ABOVE_THRESHOLD_MS; + private static final int CONSISTENCY_BONUS_MAX = 100; + + private static final int METRIC_WAS_JANKY = -1; + + private static final int[] METRICS = new int[] { + FrameMetrics.UNKNOWN_DELAY_DURATION, + FrameMetrics.INPUT_HANDLING_DURATION, + FrameMetrics.ANIMATION_DURATION, + FrameMetrics.LAYOUT_MEASURE_DURATION, + FrameMetrics.DRAW_DURATION, + FrameMetrics.SYNC_DURATION, + FrameMetrics.COMMAND_ISSUE_DURATION, + FrameMetrics.SWAP_BUFFERS_DURATION, + FrameMetrics.TOTAL_DURATION, + }; + public static final int FRAME_PERIOD_MS = 16; + + private final DescriptiveStatistics[] mStoredStatistics; + + public UiBenchmarkResult(List<FrameMetrics> instances) { + mStoredStatistics = new DescriptiveStatistics[METRICS.length]; + insertMetrics(instances); + } + + public UiBenchmarkResult(double[] values) { + mStoredStatistics = new DescriptiveStatistics[METRICS.length]; + insertValues(values); + } + + public void update(List<FrameMetrics> instances) { + insertMetrics(instances); + } + + public void update(double[] values) { + insertValues(values); + } + + public double getAverage(int id) { + int pos = getMetricPosition(id); + return mStoredStatistics[pos].getMean(); + } + + public double getMinimum(int id) { + int pos = getMetricPosition(id); + return mStoredStatistics[pos].getMin(); + } + + public double getMaximum(int id) { + int pos = getMetricPosition(id); + return mStoredStatistics[pos].getMax(); + } + + public int getMaximumIndex(int id) { + int pos = getMetricPosition(id); + double[] storedMetrics = mStoredStatistics[pos].getValues(); + int maxIdx = 0; + for (int i = 0; i < storedMetrics.length; i++) { + if (storedMetrics[i] >= storedMetrics[maxIdx]) { + maxIdx = i; + } + } + + return maxIdx; + } + + public double getMetricAtIndex(int index, int metricId) { + return mStoredStatistics[getMetricPosition(metricId)].getElement(index); + } + + public double getPercentile(int id, int percentile) { + if (percentile > 100) percentile = 100; + if (percentile < 0) percentile = 0; + + int metricPos = getMetricPosition(id); + return mStoredStatistics[metricPos].getPercentile(percentile); + } + + public int getTotalFrameCount() { + if (mStoredStatistics.length == 0) { + return 0; + } + + return (int) mStoredStatistics[0].getN(); + } + + public int getScore() { + SummaryStatistics badFramesStats = new SummaryStatistics(); + + int totalFrameCount = getTotalFrameCount(); + for (int i = 0; i < totalFrameCount; i++) { + double totalDuration = getMetricAtIndex(i, FrameMetrics.TOTAL_DURATION); + if (totalDuration >= 12) { + badFramesStats.addValue(totalDuration); + } + } + + int length = getSortedJankFrameIndices().length; + double jankFrameCount = 100 * length / (double) totalFrameCount; + + System.out.println("Mean: " + badFramesStats.getMean() + " JankP: " + jankFrameCount + + " StdDev: " + badFramesStats.getStandardDeviation() + + " Count Bad: " + badFramesStats.getN() + " Count Jank: " + length); + + return (int) Math.round( + (badFramesStats.getMean()) * jankFrameCount * badFramesStats.getStandardDeviation()); + } + + public int getJankPenalty() { + double total95th = mStoredStatistics[getMetricPosition(FrameMetrics.TOTAL_DURATION)] + .getPercentile(95); + System.out.println("95: " + total95th); + double aboveThreshold = total95th - JANK_PENALTY_THRESHOLD_MS; + if (aboveThreshold <= 0) { + return 0; + } + + if (aboveThreshold > ZERO_SCORE_ABOVE_THRESHOLD_MS) { + return BASE_SCORE; + } + + return (int) Math.ceil(JANK_PENALTY_PER_MS_ABOVE_THRESHOLD * aboveThreshold); + } + + public int getConsistencyBonus() { + DescriptiveStatistics totalDurationStats = + mStoredStatistics[getMetricPosition(FrameMetrics.TOTAL_DURATION)]; + + double standardDeviation = totalDurationStats.getStandardDeviation(); + if (standardDeviation == 0) { + return CONSISTENCY_BONUS_MAX; + } + + // 1 / CV of the total duration. + double bonus = totalDurationStats.getMean() / standardDeviation; + return (int) Math.min(Math.round(bonus), CONSISTENCY_BONUS_MAX); + } + + public int[] getSortedJankFrameIndices() { + ArrayList<Integer> jankFrameIndices = new ArrayList<>(); + boolean tripleBuffered = false; + int totalFrameCount = getTotalFrameCount(); + int totalDurationPos = getMetricPosition(FrameMetrics.TOTAL_DURATION); + + for (int i = 0; i < totalFrameCount; i++) { + double thisDuration = mStoredStatistics[totalDurationPos].getElement(i); + if (!tripleBuffered) { + if (thisDuration > FRAME_PERIOD_MS) { + tripleBuffered = true; + jankFrameIndices.add(i); + } + } else { + if (thisDuration > 2 * FRAME_PERIOD_MS) { + tripleBuffered = false; + jankFrameIndices.add(i); + } + } + } + + int[] res = new int[jankFrameIndices.size()]; + int i = 0; + for (Integer index : jankFrameIndices) { + res[i++] = index; + } + return res; + } + + private int getMetricPosition(int id) { + for (int i = 0; i < METRICS.length; i++) { + if (id == METRICS[i]) { + return i; + } + } + + return -1; + } + + private void insertMetrics(List<FrameMetrics> instances) { + for (FrameMetrics frame : instances) { + for (int i = 0; i < METRICS.length; i++) { + DescriptiveStatistics stats = mStoredStatistics[i]; + if (stats == null) { + stats = new DescriptiveStatistics(); + mStoredStatistics[i] = stats; + } + + mStoredStatistics[i].addValue(frame.getMetric(METRICS[i]) / (double) 1000000); + } + } + } + + private void insertValues(double[] values) { + if (values.length != METRICS.length) { + throw new IllegalArgumentException("invalid values array"); + } + + for (int i = 0; i < values.length; i++) { + DescriptiveStatistics stats = mStoredStatistics[i]; + if (stats == null) { + stats = new DescriptiveStatistics(); + mStoredStatistics[i] = stats; + } + + mStoredStatistics[i].addValue(values[i]); + } + } + } diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/synthetic/MemoryActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/synthetic/MemoryActivity.java new file mode 100644 index 000000000000..aba16d596e6f --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/synthetic/MemoryActivity.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 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.benchmark.synthetic; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; + +import com.android.benchmark.R; +import com.android.benchmark.app.PerfTimeline; + +import junit.framework.Test; + + +public class MemoryActivity extends Activity { + private TextView mTextStatus; + private TextView mTextMin; + private TextView mTextMax; + private TextView mTextTypical; + private PerfTimeline mTimeline; + + TestInterface mTI; + int mActiveTest; + + private class SyntheticTestCallback extends TestInterface.TestResultCallback { + @Override + void onTestResult(int command, float result) { + Intent resultIntent = new Intent(); + resultIntent.putExtra("com.android.benchmark.synthetic.TEST_RESULT", result); + setResult(RESULT_OK, resultIntent); + finish(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_memory); + + mTextStatus = (TextView) findViewById(R.id.textView_status); + mTextMin = (TextView) findViewById(R.id.textView_min); + mTextMax = (TextView) findViewById(R.id.textView_max); + mTextTypical = (TextView) findViewById(R.id.textView_typical); + + mTimeline = (PerfTimeline) findViewById(R.id.mem_timeline); + + mTI = new TestInterface(mTimeline, 2, new SyntheticTestCallback()); + mTI.mTextMax = mTextMax; + mTI.mTextMin = mTextMin; + mTI.mTextStatus = mTextStatus; + mTI.mTextTypical = mTextTypical; + + mTimeline.mLinesLow = mTI.mLinesLow; + mTimeline.mLinesHigh = mTI.mLinesHigh; + mTimeline.mLinesValue = mTI.mLinesValue; + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + @Override + protected void onResume() { + super.onResume(); + Intent i = getIntent(); + mActiveTest = i.getIntExtra("test", 0); + + switch (mActiveTest) { + case 0: + mTI.runMemoryBandwidth(); + break; + case 1: + mTI.runMemoryLatency(); + break; + case 2: + mTI.runPowerManagement(); + break; + case 3: + mTI.runCPUHeatSoak(); + break; + case 4: + mTI.runCPUGFlops(); + break; + default: + break; + + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_memory, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + public void onCpuBandwidth(View v) { + + + } + + + + +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/synthetic/TestInterface.java b/tests/JankBench/app/src/main/java/com/android/benchmark/synthetic/TestInterface.java new file mode 100644 index 000000000000..8f083a27be8d --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/synthetic/TestInterface.java @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2015 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.benchmark.synthetic; + +import android.view.View; +import android.widget.TextView; + +import org.apache.commons.math.stat.StatUtils; +import org.apache.commons.math.stat.descriptive.SummaryStatistics; + +import java.util.LinkedList; +import java.util.Queue; + + +public class TestInterface { + native long nInit(long options); + native long nDestroy(long b); + native float nGetData(long b, float[] data); + native boolean nRunPowerManagementTest(long b, long options); + native boolean nRunCPUHeatSoakTest(long b, long options); + + native boolean nMemTestStart(long b); + native float nMemTestBandwidth(long b, long size); + native float nMemTestLatency(long b, long size); + native void nMemTestEnd(long b); + + native float nGFlopsTest(long b, long opt); + + public static class TestResultCallback { + void onTestResult(int command, float result) { } + } + + static { + System.loadLibrary("nativebench"); + } + + float[] mLinesLow; + float[] mLinesHigh; + float[] mLinesValue; + TextView mTextStatus; + TextView mTextMin; + TextView mTextMax; + TextView mTextTypical; + + private View mViewToUpdate; + + private LooperThread mLT; + + TestInterface(View v, int runtimeSeconds, TestResultCallback callback) { + int buckets = runtimeSeconds * 1000; + mLinesLow = new float[buckets * 4]; + mLinesHigh = new float[buckets * 4]; + mLinesValue = new float[buckets * 4]; + mViewToUpdate = v; + + mLT = new LooperThread(this, callback); + mLT.start(); + } + + static class LooperThread extends Thread { + public static final int CommandExit = 1; + public static final int TestPowerManagement = 2; + public static final int TestMemoryBandwidth = 3; + public static final int TestMemoryLatency = 4; + public static final int TestHeatSoak = 5; + public static final int TestGFlops = 6; + + private volatile boolean mRun = true; + private TestInterface mTI; + private TestResultCallback mCallback; + + Queue<Integer> mCommandQueue = new LinkedList<Integer>(); + + LooperThread(TestInterface ti, TestResultCallback callback) { + super("BenchmarkTestThread"); + mTI = ti; + mCallback = callback; + } + + void runCommand(int command) { + Integer i = Integer.valueOf(command); + + synchronized (this) { + mCommandQueue.add(i); + notifyAll(); + } + } + + public void run() { + long b = mTI.nInit(0); + if (b == 0) { + return; + } + + while (mRun) { + int command = 0; + synchronized (this) { + if (mCommandQueue.isEmpty()) { + try { + wait(); + } catch (InterruptedException e) { + } + } + + if (!mCommandQueue.isEmpty()) { + command = mCommandQueue.remove(); + } + } + + switch (command) { + case CommandExit: + mRun = false; + break; + case TestPowerManagement: + float score = mTI.testPowerManagement(b); + mCallback.onTestResult(command, 0); + break; + case TestMemoryBandwidth: + mTI.testCPUMemoryBandwidth(b); + break; + case TestMemoryLatency: + mTI.testCPUMemoryLatency(b); + break; + case TestHeatSoak: + mTI.testCPUHeatSoak(b); + break; + case TestGFlops: + mTI.testCPUGFlops(b); + break; + + } + + //mViewToUpdate.post(new Runnable() { + // public void run() { + // mViewToUpdate.invalidate(); + //} + //}); + } + + mTI.nDestroy(b); + } + + void exit() { + mRun = false; + } + } + + void postTextToView(TextView v, String s) { + final TextView tv = v; + final String ts = s; + + v.post(new Runnable() { + public void run() { + tv.setText(ts); + } + }); + + } + + float calcAverage(float[] data) { + float total = 0.f; + for (int ct=0; ct < data.length; ct++) { + total += data[ct]; + } + return total / data.length; + } + + void makeGraph(float[] data, float[] lines) { + for (int ct = 0; ct < data.length; ct++) { + lines[ct * 4 + 0] = (float)ct; + lines[ct * 4 + 1] = 500.f - data[ct]; + lines[ct * 4 + 2] = (float)ct; + lines[ct * 4 + 3] = 500.f; + } + } + + float testPowerManagement(long b) { + float[] dat = new float[mLinesLow.length / 4]; + postTextToView(mTextStatus, "Running single-threaded"); + nRunPowerManagementTest(b, 1); + nGetData(b, dat); + makeGraph(dat, mLinesLow); + mViewToUpdate.postInvalidate(); + float avgMin = calcAverage(dat); + + postTextToView(mTextMin, "Single threaded " + avgMin + " per second"); + + postTextToView(mTextStatus, "Running multi-threaded"); + nRunPowerManagementTest(b, 4); + nGetData(b, dat); + makeGraph(dat, mLinesHigh); + mViewToUpdate.postInvalidate(); + float avgMax = calcAverage(dat); + postTextToView(mTextMax, "Multi threaded " + avgMax + " per second"); + + postTextToView(mTextStatus, "Running typical"); + nRunPowerManagementTest(b, 0); + nGetData(b, dat); + makeGraph(dat, mLinesValue); + mViewToUpdate.postInvalidate(); + float avgTypical = calcAverage(dat); + + float ofIdeal = avgTypical / (avgMax + avgMin) * 200.f; + postTextToView(mTextTypical, String.format("Typical mix (50/50) %%%2.0f of ideal", ofIdeal)); + return ofIdeal * (avgMax + avgMin); + } + + float testCPUHeatSoak(long b) { + float[] dat = new float[1000]; + postTextToView(mTextStatus, "Running heat soak test"); + for (int t = 0; t < 1000; t++) { + mLinesLow[t * 4 + 0] = (float)t; + mLinesLow[t * 4 + 1] = 498.f; + mLinesLow[t * 4 + 2] = (float)t; + mLinesLow[t * 4 + 3] = 500.f; + } + + float peak = 0.f; + float total = 0.f; + float dThroughput = 0; + float prev = 0; + SummaryStatistics stats = new SummaryStatistics(); + for (int t = 0; t < 1000; t++) { + nRunCPUHeatSoakTest(b, 1); + nGetData(b, dat); + + float p = calcAverage(dat); + if (prev != 0) { + dThroughput += (prev - p); + } + + prev = p; + + mLinesLow[t * 4 + 1] = 499.f - p; + if (peak < p) { + peak = p; + } + for (float f : dat) { + stats.addValue(f); + } + + total += p; + + mViewToUpdate.postInvalidate(); + postTextToView(mTextMin, "Peak " + peak + " per second"); + postTextToView(mTextMax, "Current " + p + " per second"); + postTextToView(mTextTypical, "Average " + (total / (t + 1)) + " per second"); + } + + + float decreaseOverTime = dThroughput / 1000; + + System.out.println("dthroughput/dt: " + decreaseOverTime); + + float score = (float) (stats.getMean() / (stats.getStandardDeviation() * decreaseOverTime)); + + postTextToView(mTextStatus, "Score: " + score); + return score; + } + + void testCPUMemoryBandwidth(long b) { + int[] sizeK = {1, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 14, 16, 20, 24, 28, + 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320, 384, 448, + 512, 640, 768, 896, 1024, 1280, 1536, 1792, + 2048, 2560, 3584, 4096, 5120, 6144, 7168, + 8192, 10240, 12288, 14336, 16384 + }; + final int subSteps = 15; + float[] results = new float[sizeK.length * subSteps]; + + nMemTestStart(b); + + float[] dat = new float[1000]; + postTextToView(mTextStatus, "Running Memory Bandwidth test"); + for (int t = 0; t < 1000; t++) { + mLinesLow[t * 4 + 0] = (float)t; + mLinesLow[t * 4 + 1] = 498.f; + mLinesLow[t * 4 + 2] = (float)t; + mLinesLow[t * 4 + 3] = 500.f; + } + + for (int i = 0; i < sizeK.length; i++) { + postTextToView(mTextStatus, "Running " + sizeK[i] + " K"); + + float rtot = 0.f; + for (int j = 0; j < subSteps; j++) { + float ret = nMemTestBandwidth(b, sizeK[i] * 1024); + rtot += ret; + results[i * subSteps + j] = ret; + mLinesLow[(i * subSteps + j) * 4 + 1] = 499.f - (results[i*15+j] * 20.f); + mViewToUpdate.postInvalidate(); + } + rtot /= subSteps; + + if (sizeK[i] == 2) { + postTextToView(mTextMin, "2K " + rtot + " GB/s"); + } + if (sizeK[i] == 128) { + postTextToView(mTextMax, "128K " + rtot + " GB/s"); + } + if (sizeK[i] == 8192) { + postTextToView(mTextTypical, "8M " + rtot + " GB/s"); + } + + } + + nMemTestEnd(b); + postTextToView(mTextStatus, "Done"); + } + + void testCPUMemoryLatency(long b) { + int[] sizeK = {1, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 14, 16, 20, 24, 28, + 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320, 384, 448, + 512, 640, 768, 896, 1024, 1280, 1536, 1792, + 2048, 2560, 3584, 4096, 5120, 6144, 7168, + 8192, 10240, 12288, 14336, 16384 + }; + final int subSteps = 15; + float[] results = new float[sizeK.length * subSteps]; + + nMemTestStart(b); + + float[] dat = new float[1000]; + postTextToView(mTextStatus, "Running Memory Latency test"); + for (int t = 0; t < 1000; t++) { + mLinesLow[t * 4 + 0] = (float)t; + mLinesLow[t * 4 + 1] = 498.f; + mLinesLow[t * 4 + 2] = (float)t; + mLinesLow[t * 4 + 3] = 500.f; + } + + for (int i = 0; i < sizeK.length; i++) { + postTextToView(mTextStatus, "Running " + sizeK[i] + " K"); + + float rtot = 0.f; + for (int j = 0; j < subSteps; j++) { + float ret = nMemTestLatency(b, sizeK[i] * 1024); + rtot += ret; + results[i * subSteps + j] = ret; + + if (ret > 400.f) ret = 400.f; + if (ret < 0.f) ret = 0.f; + mLinesLow[(i * subSteps + j) * 4 + 1] = 499.f - ret; + //android.util.Log.e("bench", "test bw " + sizeK[i] + " - " + ret); + mViewToUpdate.postInvalidate(); + } + rtot /= subSteps; + + if (sizeK[i] == 2) { + postTextToView(mTextMin, "2K " + rtot + " ns"); + } + if (sizeK[i] == 128) { + postTextToView(mTextMax, "128K " + rtot + " ns"); + } + if (sizeK[i] == 8192) { + postTextToView(mTextTypical, "8M " + rtot + " ns"); + } + + } + + nMemTestEnd(b); + postTextToView(mTextStatus, "Done"); + } + + void testCPUGFlops(long b) { + int[] sizeK = {1, 2, 3, 4, 5, 6, 7 + }; + final int subSteps = 15; + float[] results = new float[sizeK.length * subSteps]; + + nMemTestStart(b); + + float[] dat = new float[1000]; + postTextToView(mTextStatus, "Running Memory Latency test"); + for (int t = 0; t < 1000; t++) { + mLinesLow[t * 4 + 0] = (float)t; + mLinesLow[t * 4 + 1] = 498.f; + mLinesLow[t * 4 + 2] = (float)t; + mLinesLow[t * 4 + 3] = 500.f; + } + + for (int i = 0; i < sizeK.length; i++) { + postTextToView(mTextStatus, "Running " + sizeK[i] + " K"); + + float rtot = 0.f; + for (int j = 0; j < subSteps; j++) { + float ret = nGFlopsTest(b, sizeK[i] * 1024); + rtot += ret; + results[i * subSteps + j] = ret; + + if (ret > 400.f) ret = 400.f; + if (ret < 0.f) ret = 0.f; + mLinesLow[(i * subSteps + j) * 4 + 1] = 499.f - ret; + mViewToUpdate.postInvalidate(); + } + rtot /= subSteps; + + if (sizeK[i] == 2) { + postTextToView(mTextMin, "2K " + rtot + " ns"); + } + if (sizeK[i] == 128) { + postTextToView(mTextMax, "128K " + rtot + " ns"); + } + if (sizeK[i] == 8192) { + postTextToView(mTextTypical, "8M " + rtot + " ns"); + } + + } + + nMemTestEnd(b); + postTextToView(mTextStatus, "Done"); + } + + public void runPowerManagement() { + mLT.runCommand(mLT.TestPowerManagement); + } + + public void runMemoryBandwidth() { + mLT.runCommand(mLT.TestMemoryBandwidth); + } + + public void runMemoryLatency() { + mLT.runCommand(mLT.TestMemoryLatency); + } + + public void runCPUHeatSoak() { + mLT.runCommand(mLT.TestHeatSoak); + } + + public void runCPUGFlops() { + mLT.runCommand(mLT.TestGFlops); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/BitmapUploadActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/BitmapUploadActivity.java new file mode 100644 index 000000000000..f6a528a8a966 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/BitmapUploadActivity.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Rect; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.MotionEvent; +import android.view.View; + +import com.android.benchmark.R; +import com.android.benchmark.ui.automation.Automator; +import com.android.benchmark.ui.automation.Interaction; + +/** + * + */ +public class BitmapUploadActivity extends AppCompatActivity { + private Automator mAutomator; + + public static class UploadView extends View { + private int mColorValue; + private Bitmap mBitmap; + private final DisplayMetrics mMetrics = new DisplayMetrics(); + private final Rect mRect = new Rect(); + + public UploadView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @SuppressWarnings("unused") + public void setColorValue(int colorValue) { + if (colorValue == mColorValue) return; + + mColorValue = colorValue; + + // modify the bitmap's color to ensure it's uploaded to the GPU + mBitmap.eraseColor(Color.rgb(mColorValue, 255 - mColorValue, 255)); + + invalidate(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + getDisplay().getMetrics(mMetrics); + int minDisplayDimen = Math.min(mMetrics.widthPixels, mMetrics.heightPixels); + int bitmapSize = Math.min((int) (minDisplayDimen * 0.75), 720); + if (mBitmap == null + || mBitmap.getWidth() != bitmapSize + || mBitmap.getHeight() != bitmapSize) { + mBitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (mBitmap != null) { + mRect.set(0, 0, getWidth(), getHeight()); + canvas.drawBitmap(mBitmap, null, mRect, null); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // animate color to force bitmap uploads + return super.onTouchEvent(event); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bitmap_upload); + + final View uploadRoot = findViewById(R.id.upload_root); + uploadRoot.setKeepScreenOn(true); + uploadRoot.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + UploadView uploadView = (UploadView) findViewById(R.id.upload_view); + ObjectAnimator colorValueAnimator = + ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255); + colorValueAnimator.setRepeatMode(ValueAnimator.REVERSE); + colorValueAnimator.setRepeatCount(100); + colorValueAnimator.start(); + + // animate scene root to guarantee there's a minimum amount of GPU rendering work + ObjectAnimator yAnimator = ObjectAnimator.ofFloat( + view, "translationY", 0, 100); + yAnimator.setRepeatMode(ValueAnimator.REVERSE); + yAnimator.setRepeatCount(100); + yAnimator.start(); + + return true; + } + }); + + final UploadView uploadView = (UploadView) findViewById(R.id.upload_view); + final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0); + final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1); + + mAutomator = new Automator("BMUpload", runId, iteration, getWindow(), + new Automator.AutomateCallback() { + @Override + public void onPostAutomate() { + Intent result = new Intent(); + setResult(RESULT_OK, result); + finish(); + } + + @Override + public void onAutomate() { + int[] coordinates = new int[2]; + uploadRoot.getLocationOnScreen(coordinates); + + int x = coordinates[0]; + int y = coordinates[1]; + + float width = uploadRoot.getWidth(); + float height = uploadRoot.getHeight(); + + float middleX = (x + width) / 5; + float middleY = (y + height) / 5; + + addInteraction(Interaction.newTap(middleX, middleY)); + } + }); + + mAutomator.start(); + } + +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/EditTextInputActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/EditTextInputActivity.java new file mode 100644 index 000000000000..ea6fb58f4775 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/EditTextInputActivity.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; +import android.widget.EditText; + +import com.android.benchmark.R; +import com.android.benchmark.ui.automation.Automator; +import com.android.benchmark.ui.automation.Interaction; + +public class EditTextInputActivity extends AppCompatActivity { + + private Automator mAutomator; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final EditText editText = new EditText(this); + final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0); + final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1); + + editText.setWidth(400); + editText.setHeight(200); + setContentView(editText); + + String testName = getString(R.string.edit_text_input_name); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(testName); + } + + mAutomator = new Automator(testName, runId, iteration, getWindow(), + new Automator.AutomateCallback() { + @Override + public void onPostAutomate() { + Intent result = new Intent(); + setResult(RESULT_OK, result); + finish(); + } + + @Override + public void onAutomate() { + + int[] coordinates = new int[2]; + editText.getLocationOnScreen(coordinates); + + int x = coordinates[0]; + int y = coordinates[1]; + + float width = editText.getWidth(); + float height = editText.getHeight(); + + float middleX = (x + width) / 2; + float middleY = (y + height) / 2; + + Interaction tap = Interaction.newTap(middleX, middleY); + addInteraction(tap); + + int[] alphabet = { + KeyEvent.KEYCODE_A, + KeyEvent.KEYCODE_B, + KeyEvent.KEYCODE_C, + KeyEvent.KEYCODE_D, + KeyEvent.KEYCODE_E, + KeyEvent.KEYCODE_F, + KeyEvent.KEYCODE_G, + KeyEvent.KEYCODE_H, + KeyEvent.KEYCODE_I, + KeyEvent.KEYCODE_J, + KeyEvent.KEYCODE_K, + KeyEvent.KEYCODE_L, + KeyEvent.KEYCODE_M, + KeyEvent.KEYCODE_N, + KeyEvent.KEYCODE_O, + KeyEvent.KEYCODE_P, + KeyEvent.KEYCODE_Q, + KeyEvent.KEYCODE_R, + KeyEvent.KEYCODE_S, + KeyEvent.KEYCODE_T, + KeyEvent.KEYCODE_U, + KeyEvent.KEYCODE_V, + KeyEvent.KEYCODE_W, + KeyEvent.KEYCODE_X, + KeyEvent.KEYCODE_Y, + KeyEvent.KEYCODE_Z, + KeyEvent.KEYCODE_SPACE + }; + Interaction typeAlphabet = Interaction.newKeyInput(new int[] { + KeyEvent.KEYCODE_A, + KeyEvent.KEYCODE_B, + KeyEvent.KEYCODE_C, + KeyEvent.KEYCODE_D, + KeyEvent.KEYCODE_E, + KeyEvent.KEYCODE_F, + KeyEvent.KEYCODE_G, + KeyEvent.KEYCODE_H, + KeyEvent.KEYCODE_I, + KeyEvent.KEYCODE_J, + KeyEvent.KEYCODE_K, + KeyEvent.KEYCODE_L, + KeyEvent.KEYCODE_M, + KeyEvent.KEYCODE_N, + KeyEvent.KEYCODE_O, + KeyEvent.KEYCODE_P, + KeyEvent.KEYCODE_Q, + KeyEvent.KEYCODE_R, + KeyEvent.KEYCODE_S, + KeyEvent.KEYCODE_T, + KeyEvent.KEYCODE_U, + KeyEvent.KEYCODE_V, + KeyEvent.KEYCODE_W, + KeyEvent.KEYCODE_X, + KeyEvent.KEYCODE_Y, + KeyEvent.KEYCODE_Z, + KeyEvent.KEYCODE_SPACE, + }); + + for (int i = 0; i < 5; i++) { + addInteraction(typeAlphabet); + } + } + }); + mAutomator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mAutomator != null) { + mAutomator.cancel(); + mAutomator = null; + } + } + + private String getRunFilename() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append(System.currentTimeMillis()); + return builder.toString(); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/FullScreenOverdrawActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/FullScreenOverdrawActivity.java new file mode 100644 index 000000000000..95fce3834f9b --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/FullScreenOverdrawActivity.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; + +import com.android.benchmark.R; +import com.android.benchmark.registry.BenchmarkRegistry; +import com.android.benchmark.ui.automation.Automator; +import com.android.benchmark.ui.automation.Interaction; + +public class FullScreenOverdrawActivity extends AppCompatActivity { + + private Automator mAutomator; + + private class OverdrawView extends View { + Paint paint = new Paint(); + int mColorValue = 0; + + public OverdrawView(Context context) { + super(context); + } + + @SuppressWarnings("unused") + public void setColorValue(int colorValue) { + mColorValue = colorValue; + invalidate(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "colorValue", 0, 255); + objectAnimator.setRepeatMode(ValueAnimator.REVERSE); + objectAnimator.setRepeatCount(100); + objectAnimator.start(); + return super.onTouchEvent(event); + } + + @Override + protected void onDraw(Canvas canvas) { + paint.setColor(Color.rgb(mColorValue, 255 - mColorValue, 255)); + + for (int i = 0; i < 10; i++) { + canvas.drawRect(0, 0, getWidth(), getHeight(), paint); + } + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final OverdrawView overdrawView = new OverdrawView(this); + overdrawView.setKeepScreenOn(true); + setContentView(overdrawView); + + final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0); + final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1); + + String name = BenchmarkRegistry.getBenchmarkName(this, R.id.benchmark_overdraw); + + mAutomator = new Automator(name, runId, iteration, getWindow(), + new Automator.AutomateCallback() { + @Override + public void onPostAutomate() { + Intent result = new Intent(); + setResult(RESULT_OK, result); + finish(); + } + + @Override + public void onAutomate() { + int[] coordinates = new int[2]; + overdrawView.getLocationOnScreen(coordinates); + + int x = coordinates[0]; + int y = coordinates[1]; + + float width = overdrawView.getWidth(); + float height = overdrawView.getHeight(); + + float middleX = (x + width) / 5; + float middleY = (y + height) / 5; + + addInteraction(Interaction.newTap(middleX, middleY)); + } + }); + + mAutomator.start(); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ImageListViewScrollActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ImageListViewScrollActivity.java new file mode 100644 index 000000000000..4644ea1b2980 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ImageListViewScrollActivity.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListAdapter; +import android.widget.TextView; + +import com.android.benchmark.R; + +import java.lang.ref.WeakReference; +import java.util.HashMap; + +public class ImageListViewScrollActivity extends ListViewScrollActivity { + + private static final int LIST_SIZE = 100; + + private static final int[] IMG_RES_ID = new int[]{ + R.drawable.img1, + R.drawable.img2, + R.drawable.img3, + R.drawable.img4, + R.drawable.img1, + R.drawable.img2, + R.drawable.img3, + R.drawable.img4, + R.drawable.img1, + R.drawable.img2, + R.drawable.img3, + R.drawable.img4, + R.drawable.img1, + R.drawable.img2, + R.drawable.img3, + R.drawable.img4, + }; + + private static Bitmap[] mBitmapCache = new Bitmap[IMG_RES_ID.length]; + + private static final String[] WORDS = Utils.buildStringList(LIST_SIZE); + + private HashMap<View, BitmapWorkerTask> mInFlight = new HashMap<>(); + + @Override + protected ListAdapter createListAdapter() { + return new ImageListAdapter(); + } + + @Override + protected String getName() { + return getString(R.string.image_list_view_scroll_name); + } + + class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { + private final WeakReference<ImageView> imageViewReference; + private int data = 0; + private int cacheIdx = 0; + volatile boolean cancelled = false; + + public BitmapWorkerTask(ImageView imageView, int cacheIdx) { + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference<>(imageView); + this.cacheIdx = cacheIdx; + } + + // Decode image in background. + @Override + protected Bitmap doInBackground(Integer... params) { + data = params[0]; + return Utils.decodeSampledBitmapFromResource(getResources(), data, 100, 100); + } + + // Once complete, see if ImageView is still around and set bitmap. + @Override + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + if (!cancelled) { + imageView.setImageBitmap(bitmap); + } + mBitmapCache[cacheIdx] = bitmap; + } + } + } + } + + @Override + protected void onPause() { + super.onPause(); + for (int i = 0; i < mBitmapCache.length; i++) { + mBitmapCache[i] = null; + } + } + + class ImageListAdapter extends BaseAdapter { + + @Override + public int getCount() { + return LIST_SIZE; + } + + @Override + public Object getItem(int postition) { + return null; + } + + @Override + public long getItemId(int postition) { + return postition; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(getBaseContext()) + .inflate(R.layout.image_scroll_list_item, parent, false); + } + + ImageView imageView = (ImageView) convertView.findViewById(R.id.image_scroll_image); + BitmapWorkerTask inFlight = mInFlight.get(convertView); + if (inFlight != null) { + inFlight.cancelled = true; + mInFlight.remove(convertView); + } + + int cacheIdx = position % IMG_RES_ID.length; + Bitmap bitmap = mBitmapCache[(cacheIdx)]; + if (bitmap == null) { + BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView, cacheIdx); + bitmapWorkerTask.execute(IMG_RES_ID[(cacheIdx)]); + mInFlight.put(convertView, bitmapWorkerTask); + } + + imageView.setImageBitmap(bitmap); + + TextView textView = (TextView) convertView.findViewById(R.id.image_scroll_text); + textView.setText(WORDS[position]); + + return convertView; + } + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ListActivityBase.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ListActivityBase.java new file mode 100644 index 000000000000..b973bc76c13f --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ListActivityBase.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.app.ActionBar; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.ListFragment; +import android.support.v7.app.AppCompatActivity; +import android.view.Window; +import android.widget.ListAdapter; + +import com.android.benchmark.R; + +/** + * Simple list activity base class + */ +public abstract class ListActivityBase extends AppCompatActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list_fragment); + + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setTitle(getName()); + } + + if (findViewById(R.id.list_fragment_container) != null) { + FragmentManager fm = getSupportFragmentManager(); + ListFragment listView = new ListFragment(); + listView.setListAdapter(createListAdapter()); + fm.beginTransaction().add(R.id.list_fragment_container, listView).commit(); + } + } + + protected abstract ListAdapter createListAdapter(); + protected abstract String getName(); +} + diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ListViewScrollActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ListViewScrollActivity.java new file mode 100644 index 000000000000..3ffb7706675f --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ListViewScrollActivity.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.view.FrameMetrics; +import android.view.MotionEvent; +import android.widget.ArrayAdapter; +import android.widget.FrameLayout; +import android.widget.ListAdapter; + +import com.android.benchmark.R; +import com.android.benchmark.ui.automation.Automator; +import com.android.benchmark.ui.automation.Interaction; + +import java.io.File; +import java.util.List; + +public class ListViewScrollActivity extends ListActivityBase { + + private static final int LIST_SIZE = 400; + private static final int INTERACTION_COUNT = 4; + + private Automator mAutomator; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0); + final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(getTitle()); + } + + mAutomator = new Automator(getName(), runId, iteration, getWindow(), + new Automator.AutomateCallback() { + @Override + public void onPostAutomate() { + Intent result = new Intent(); + setResult(RESULT_OK, result); + finish(); + } + + @Override + public void onPostInteraction(List<FrameMetrics> metrics) {} + + @Override + public void onAutomate() { + FrameLayout v = (FrameLayout) findViewById(R.id.list_fragment_container); + + int[] coordinates = new int[2]; + v.getLocationOnScreen(coordinates); + + int x = coordinates[0]; + int y = coordinates[1]; + + float width = v.getWidth(); + float height = v.getHeight(); + + float middleX = (x + width) / 5; + float middleY = (y + height) / 5; + + Interaction flingUp = Interaction.newFlingUp(middleX, middleY); + Interaction flingDown = Interaction.newFlingDown(middleX, middleY); + + for (int i = 0; i < INTERACTION_COUNT; i++) { + addInteraction(flingUp); + addInteraction(flingDown); + } + } + }); + + mAutomator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mAutomator != null) { + mAutomator.cancel(); + mAutomator = null; + } + } + + @Override + protected ListAdapter createListAdapter() { + return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, + Utils.buildStringList(LIST_SIZE)); + } + + @Override + protected String getName() { + return getString(R.string.list_view_scroll_name); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ShadowGridActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ShadowGridActivity.java new file mode 100644 index 000000000000..68f75a3f277b --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/ShadowGridActivity.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.ListFragment; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import com.android.benchmark.R; +import com.android.benchmark.ui.automation.Automator; +import com.android.benchmark.ui.automation.Interaction; + +public class ShadowGridActivity extends AppCompatActivity { + private Automator mAutomator; + public static class MyListFragment extends ListFragment { + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getListView().setDivider(null); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0); + final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1); + + FragmentManager fm = getSupportFragmentManager(); + if (fm.findFragmentById(android.R.id.content) == null) { + ListFragment listFragment = new MyListFragment(); + + listFragment.setListAdapter(new ArrayAdapter<>(this, + R.layout.card_row, R.id.card_text, Utils.buildStringList(200))); + fm.beginTransaction().add(android.R.id.content, listFragment).commit(); + + String testName = getString(R.string.shadow_grid_name); + + mAutomator = new Automator(testName, runId, iteration, getWindow(), + new Automator.AutomateCallback() { + @Override + public void onPostAutomate() { + Intent result = new Intent(); + setResult(RESULT_OK, result); + finish(); + } + + @Override + public void onAutomate() { + ListView v = (ListView) findViewById(android.R.id.list); + + int[] coordinates = new int[2]; + v.getLocationOnScreen(coordinates); + + int x = coordinates[0]; + int y = coordinates[1]; + + float width = v.getWidth(); + float height = v.getHeight(); + + float middleX = (x + width) / 2; + float middleY = (y + height) / 2; + + Interaction flingUp = Interaction.newFlingUp(middleX, middleY); + Interaction flingDown = Interaction.newFlingDown(middleX, middleY); + + addInteraction(flingUp); + addInteraction(flingDown); + addInteraction(flingUp); + addInteraction(flingDown); + addInteraction(flingUp); + addInteraction(flingDown); + addInteraction(flingUp); + addInteraction(flingDown); + } + }); + mAutomator.start(); + } + } + + @Override + protected void onPause() { + super.onPause(); + if (mAutomator != null) { + mAutomator.cancel(); + mAutomator = null; + } + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/TextScrollActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/TextScrollActivity.java new file mode 100644 index 000000000000..fcd168e988e0 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/TextScrollActivity.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.widget.ArrayAdapter; +import android.widget.ListAdapter; +import android.widget.ListView; + +import com.android.benchmark.registry.BenchmarkRegistry; +import com.android.benchmark.ui.automation.Automator; +import com.android.benchmark.ui.automation.Interaction; + +import java.io.File; + +public class TextScrollActivity extends ListActivityBase { + + public static final String EXTRA_HIT_RATE = ".TextScrollActivity.EXTRA_HIT_RATE"; + + private static final int PARAGRAPH_COUNT = 200; + + private int mHitPercentage = 100; + private Automator mAutomator; + private String mName; + + @Override + public void onCreate(Bundle savedInstanceState) { + mHitPercentage = getIntent().getIntExtra(EXTRA_HIT_RATE, + mHitPercentage); + super.onCreate(savedInstanceState); + final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0); + final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1); + final int id = getIntent().getIntExtra(BenchmarkRegistry.EXTRA_ID, -1); + + if (id == -1) { + finish(); + return; + } + + mName = BenchmarkRegistry.getBenchmarkName(this, id); + + mAutomator = new Automator(getName(), runId, iteration, getWindow(), + new Automator.AutomateCallback() { + @Override + public void onPostAutomate() { + Intent result = new Intent(); + setResult(RESULT_OK, result); + finish(); + } + + @Override + public void onAutomate() { + ListView v = (ListView) findViewById(android.R.id.list); + + int[] coordinates = new int[2]; + v.getLocationOnScreen(coordinates); + + int x = coordinates[0]; + int y = coordinates[1]; + + float width = v.getWidth(); + float height = v.getHeight(); + + float middleX = (x + width) / 2; + float middleY = (y + height) / 2; + + Interaction flingUp = Interaction.newFlingUp(middleX, middleY); + Interaction flingDown = Interaction.newFlingDown(middleX, middleY); + + addInteraction(flingUp); + addInteraction(flingDown); + addInteraction(flingUp); + addInteraction(flingDown); + addInteraction(flingUp); + addInteraction(flingDown); + addInteraction(flingUp); + addInteraction(flingDown); + } + }); + + mAutomator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mAutomator != null) { + mAutomator.cancel(); + mAutomator = null; + } + } + + @Override + protected ListAdapter createListAdapter() { + return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, + Utils.buildParagraphListWithHitPercentage(PARAGRAPH_COUNT, 80)); + } + + @Override + protected String getName() { + return mName; + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/Utils.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/Utils.java new file mode 100644 index 000000000000..39f9206d4e07 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/Utils.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2015 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.benchmark.ui; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.util.Random; + +public class Utils { + + private static final int RANDOM_WORD_LENGTH = 10; + + public static String getRandomWord(Random random, int length) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + char base = random.nextBoolean() ? 'A' : 'a'; + char nextChar = (char)(random.nextInt(26) + base); + builder.append(nextChar); + } + return builder.toString(); + } + + public static String[] buildStringList(int count) { + Random random = new Random(0); + String[] result = new String[count]; + for (int i = 0; i < count; i++) { + result[i] = getRandomWord(random, RANDOM_WORD_LENGTH); + } + + return result; + } + + // a small number of strings reused frequently, expected to hit + // in the word-granularity text layout cache + static final String[] CACHE_HIT_STRINGS = new String[] { + "a", + "small", + "number", + "of", + "strings", + "reused", + "frequently" + }; + + private static final int WORDS_IN_PARAGRAPH = 150; + + // misses are fairly long 'words' to ensure they miss + private static final int PARAGRAPH_MISS_MIN_LENGTH = 4; + private static final int PARAGRAPH_MISS_MAX_LENGTH = 9; + + static String[] buildParagraphListWithHitPercentage(int paragraphCount, int hitPercentage) { + if (hitPercentage < 0 || hitPercentage > 100) throw new IllegalArgumentException(); + + String[] strings = new String[paragraphCount]; + Random random = new Random(0); + for (int i = 0; i < strings.length; i++) { + StringBuilder result = new StringBuilder(); + for (int word = 0; word < WORDS_IN_PARAGRAPH; word++) { + if (word != 0) { + result.append(" "); + } + if (random.nextInt(100) < hitPercentage) { + // add a common word, which is very likely to hit in the cache + result.append(CACHE_HIT_STRINGS[random.nextInt(CACHE_HIT_STRINGS.length)]); + } else { + // construct a random word, which will *most likely* miss + int length = PARAGRAPH_MISS_MIN_LENGTH; + length += random.nextInt(PARAGRAPH_MISS_MAX_LENGTH - PARAGRAPH_MISS_MIN_LENGTH); + + result.append(getRandomWord(random, length)); + } + } + strings[i] = result.toString(); + } + + return strings; + } + + + public static int calculateInSampleSize( + BitmapFactory.Options options, int reqWidth, int reqHeight) { + // Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + + final int halfHeight = height / 2; + final int halfWidth = width / 2; + + // Calculate the largest inSampleSize value that is a power of 2 and keeps both + // height and width larger than the requested height and width. + while ((halfHeight / inSampleSize) > reqHeight + && (halfWidth / inSampleSize) > reqWidth) { + inSampleSize *= 2; + } + } + + return inSampleSize; + } + + public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, + int reqWidth, int reqHeight) { + + // First decode with inJustDecodeBounds=true to check dimensions + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(res, resId, options); + + // Calculate inSampleSize + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + + // Decode bitmap with inSampleSize set + options.inJustDecodeBounds = false; + return BitmapFactory.decodeResource(res, resId, options); + } + +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/Automator.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/Automator.java new file mode 100644 index 000000000000..1efd6bc2aba6 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/Automator.java @@ -0,0 +1,269 @@ +/* + * 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.benchmark.ui.automation; + +import android.annotation.TargetApi; +import android.app.Instrumentation; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.view.FrameMetrics; +import android.view.MotionEvent; +import android.view.ViewTreeObserver; +import android.view.Window; + +import com.android.benchmark.results.GlobalResultsStore; +import com.android.benchmark.results.UiBenchmarkResult; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +@TargetApi(24) +public class Automator extends HandlerThread + implements ViewTreeObserver.OnGlobalLayoutListener, CollectorThread.CollectorListener { + public static final long FRAME_PERIOD_MILLIS = 16; + + private static final int PRE_READY_STATE_COUNT = 3; + private static final String TAG = "Benchmark.Automator"; + private final AtomicInteger mReadyState; + + private AutomateCallback mCallback; + private Window mWindow; + private AutomatorHandler mHandler; + private CollectorThread mCollectorThread; + private int mRunId; + private int mIteration; + private String mTestName; + + public static class AutomateCallback { + public void onAutomate() {} + public void onPostInteraction(List<FrameMetrics> metrics) {} + public void onPostAutomate() {} + + protected final void addInteraction(Interaction interaction) { + if (mInteractions == null) { + return; + } + + mInteractions.add(interaction); + } + + protected final void setInteractions(List<Interaction> interactions) { + mInteractions = interactions; + } + + private List<Interaction> mInteractions; + } + + private static final class AutomatorHandler extends Handler { + public static final int MSG_NEXT_INTERACTION = 0; + public static final int MSG_ON_AUTOMATE = 1; + public static final int MSG_ON_POST_INTERACTION = 2; + private final String mTestName; + private final int mRunId; + private final int mIteration; + + private Instrumentation mInstrumentation; + private volatile boolean mCancelled; + private CollectorThread mCollectorThread; + private AutomateCallback mCallback; + private Window mWindow; + + LinkedList<Interaction> mInteractions; + private UiBenchmarkResult mResults; + + AutomatorHandler(Looper looper, Window window, CollectorThread collectorThread, + AutomateCallback callback, String testName, int runId, int iteration) { + super(looper); + + mInstrumentation = new Instrumentation(); + + mCallback = callback; + mWindow = window; + mCollectorThread = collectorThread; + mInteractions = new LinkedList<>(); + mTestName = testName; + mRunId = runId; + mIteration = iteration; + } + + @Override + public void handleMessage(Message msg) { + if (mCancelled) { + return; + } + + switch (msg.what) { + case MSG_NEXT_INTERACTION: + if (!nextInteraction()) { + stopCollector(); + writeResults(); + mCallback.onPostAutomate(); + } + break; + case MSG_ON_AUTOMATE: + mCollectorThread.attachToWindow(mWindow); + mCallback.setInteractions(mInteractions); + mCallback.onAutomate(); + postNextInteraction(); + break; + case MSG_ON_POST_INTERACTION: + List<FrameMetrics> collectedStats = (List<FrameMetrics>)msg.obj; + persistResults(collectedStats); + mCallback.onPostInteraction(collectedStats); + postNextInteraction(); + break; + } + } + + public void cancel() { + mCancelled = true; + stopCollector(); + } + + private void stopCollector() { + mCollectorThread.quitCollector(); + } + + private boolean nextInteraction() { + + Interaction interaction = mInteractions.poll(); + if (interaction != null) { + doInteraction(interaction); + return true; + } + return false; + } + + private void doInteraction(Interaction interaction) { + if (mCancelled) { + return; + } + + mCollectorThread.markInteractionStart(); + + if (interaction.getType() == Interaction.Type.KEY_EVENT) { + for (int code : interaction.getKeyCodes()) { + if (!mCancelled) { + mInstrumentation.sendKeyDownUpSync(code); + } else { + break; + } + } + } else { + for (MotionEvent event : interaction.getEvents()) { + if (!mCancelled) { + mInstrumentation.sendPointerSync(event); + } else { + break; + } + } + } + } + + protected void postNextInteraction() { + final Message msg = obtainMessage(AutomatorHandler.MSG_NEXT_INTERACTION); + sendMessage(msg); + } + + private void persistResults(List<FrameMetrics> stats) { + if (stats.isEmpty()) { + return; + } + + if (mResults == null) { + mResults = new UiBenchmarkResult(stats); + } else { + mResults.update(stats); + } + } + + private void writeResults() { + GlobalResultsStore.getInstance(mWindow.getContext()) + .storeRunResults(mTestName, mRunId, mIteration, mResults); + } + } + + private void initHandler() { + mHandler = new AutomatorHandler(getLooper(), mWindow, mCollectorThread, mCallback, + mTestName, mRunId, mIteration); + mWindow = null; + mCallback = null; + mCollectorThread = null; + mTestName = null; + mRunId = 0; + mIteration = 0; + } + + @Override + public final void onGlobalLayout() { + if (!mCollectorThread.isAlive()) { + mCollectorThread.start(); + mWindow.getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this); + mReadyState.decrementAndGet(); + } + } + + @Override + public void onCollectorThreadReady() { + if (mReadyState.decrementAndGet() == 0) { + initHandler(); + postOnAutomate(); + } + } + + @Override + protected void onLooperPrepared() { + if (mReadyState.decrementAndGet() == 0) { + initHandler(); + postOnAutomate(); + } + } + + @Override + public void onPostInteraction(List<FrameMetrics> stats) { + Message m = mHandler.obtainMessage(AutomatorHandler.MSG_ON_POST_INTERACTION, stats); + mHandler.sendMessage(m); + } + + protected void postOnAutomate() { + final Message msg = mHandler.obtainMessage(AutomatorHandler.MSG_ON_AUTOMATE); + mHandler.sendMessage(msg); + } + + public void cancel() { + mHandler.removeMessages(AutomatorHandler.MSG_NEXT_INTERACTION); + mHandler.cancel(); + mHandler = null; + } + + public Automator(String testName, int runId, int iteration, + Window window, AutomateCallback callback) { + super("AutomatorThread"); + + mTestName = testName; + mRunId = runId; + mIteration = iteration; + mCallback = callback; + mWindow = window; + mWindow.getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(this); + mCollectorThread = new CollectorThread(this); + mReadyState = new AtomicInteger(PRE_READY_STATE_COUNT); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/CollectorThread.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/CollectorThread.java new file mode 100644 index 000000000000..806c704d8a09 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/CollectorThread.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 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.benchmark.ui.automation; + +import android.annotation.TargetApi; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.os.SystemClock; +import android.view.FrameMetrics; +import android.view.Window; + +import java.lang.ref.WeakReference; +import java.util.LinkedList; +import java.util.List; + +/** + * + */ +final class CollectorThread extends HandlerThread { + private FrameStatsCollector mCollector; + private Window mAttachedWindow; + private List<FrameMetrics> mFrameTimingStats; + private long mLastFrameTime; + private WatchdogHandler mWatchdog; + private WeakReference<CollectorListener> mListener; + + private volatile boolean mCollecting; + + + interface CollectorListener { + void onCollectorThreadReady(); + void onPostInteraction(List<FrameMetrics> stats); + } + + private final class WatchdogHandler extends Handler { + private static final long SCHEDULE_INTERVAL_MILLIS = 20 * Automator.FRAME_PERIOD_MILLIS; + + private static final int MSG_SCHEDULE = 0; + + @Override + public void handleMessage(Message msg) { + if (!mCollecting) { + return; + } + + long currentTime = SystemClock.uptimeMillis(); + if (mLastFrameTime + SCHEDULE_INTERVAL_MILLIS <= currentTime) { + // haven't seen a frame in a while, interaction is probably done + mCollecting = false; + CollectorListener listener = mListener.get(); + if (listener != null) { + listener.onPostInteraction(mFrameTimingStats); + } + } else { + schedule(); + } + } + + public void schedule() { + sendMessageDelayed(obtainMessage(MSG_SCHEDULE), SCHEDULE_INTERVAL_MILLIS); + } + + public void deschedule() { + removeMessages(MSG_SCHEDULE); + } + } + + static boolean tripleBuffered = false; + static int janks = 0; + static int total = 0; + @TargetApi(24) + private class FrameStatsCollector implements Window.OnFrameMetricsAvailableListener { + @Override + public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCount) { + if (!mCollecting) { + return; + } + mFrameTimingStats.add(new FrameMetrics(frameMetrics)); + mLastFrameTime = SystemClock.uptimeMillis(); + } + } + + public CollectorThread(CollectorListener listener) { + super("FrameStatsCollectorThread"); + mFrameTimingStats = new LinkedList<>(); + mListener = new WeakReference<>(listener); + } + + @TargetApi(24) + public void attachToWindow(Window window) { + if (mAttachedWindow != null) { + mAttachedWindow.removeOnFrameMetricsAvailableListener(mCollector); + } + + mAttachedWindow = window; + window.addOnFrameMetricsAvailableListener(mCollector, new Handler(getLooper())); + } + + @TargetApi(24) + public synchronized void detachFromWindow() { + if (mAttachedWindow != null) { + mAttachedWindow.removeOnFrameMetricsAvailableListener(mCollector); + } + + mAttachedWindow = null; + } + + @TargetApi(24) + @Override + protected void onLooperPrepared() { + super.onLooperPrepared(); + mCollector = new FrameStatsCollector(); + mWatchdog = new WatchdogHandler(); + + CollectorListener listener = mListener.get(); + if (listener != null) { + listener.onCollectorThreadReady(); + } + } + + public boolean quitCollector() { + stopCollecting(); + detachFromWindow(); + System.out.println("Jank Percentage: " + (100 * janks/ (double) total) + "%"); + tripleBuffered = false; + total = 0; + janks = 0; + return quit(); + } + + void stopCollecting() { + if (!mCollecting) { + return; + } + + mCollecting = false; + mWatchdog.deschedule(); + + + } + + public void markInteractionStart() { + mLastFrameTime = 0; + mFrameTimingStats.clear(); + mCollecting = true; + + mWatchdog.schedule(); + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/FrameTimingStats.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/FrameTimingStats.java new file mode 100644 index 000000000000..1fd0998f8dd6 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/FrameTimingStats.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 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.benchmark.ui.automation; + +import android.support.annotation.IntDef; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Arrays; + +public class FrameTimingStats { + @IntDef ({ + Index.FLAGS, + Index.INTENDED_VSYNC, + Index.VSYNC, + Index.OLDEST_INPUT_EVENT, + Index.NEWEST_INPUT_EVENT, + Index.HANDLE_INPUT_START, + Index.ANIMATION_START, + Index.PERFORM_TRAVERSALS_START, + Index.DRAW_START, + Index.SYNC_QUEUED, + Index.SYNC_START, + Index.ISSUE_DRAW_COMMANDS_START, + Index.SWAP_BUFFERS, + Index.FRAME_COMPLETED, + }) + public @interface Index { + int FLAGS = 0; + int INTENDED_VSYNC = 1; + int VSYNC = 2; + int OLDEST_INPUT_EVENT = 3; + int NEWEST_INPUT_EVENT = 4; + int HANDLE_INPUT_START = 5; + int ANIMATION_START = 6; + int PERFORM_TRAVERSALS_START = 7; + int DRAW_START = 8; + int SYNC_QUEUED = 9; + int SYNC_START = 10; + int ISSUE_DRAW_COMMANDS_START = 11; + int SWAP_BUFFERS = 12; + int FRAME_COMPLETED = 13; + + int FRAME_STATS_COUNT = 14; // must always be last + } + + private final long[] mStats; + + FrameTimingStats(long[] stats) { + mStats = Arrays.copyOf(stats, Index.FRAME_STATS_COUNT); + } + + public FrameTimingStats(DataInputStream inputStream) throws IOException { + mStats = new long[Index.FRAME_STATS_COUNT]; + update(inputStream); + } + + public void update(DataInputStream inputStream) throws IOException { + for (int i = 0; i < mStats.length; i++) { + mStats[i] = inputStream.readLong(); + } + } + + public long get(@Index int index) { + return mStats[index]; + } + + public long[] data() { + return mStats; + } +} diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/Interaction.java b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/Interaction.java new file mode 100644 index 000000000000..370fed28e1b2 --- /dev/null +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/ui/automation/Interaction.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 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.benchmark.ui.automation; + +import android.os.SystemClock; +import android.support.annotation.IntDef; +import android.view.MotionEvent; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encodes a UI interaction as a series of MotionEvents + */ +public class Interaction { + private static final int STEP_COUNT = 20; + // TODO: scale to device display density + private static final int DEFAULT_FLING_SIZE_PX = 500; + private static final int DEFAULT_FLING_DURATION_MS = 20; + private static final int DEFAULT_TAP_DURATION_MS = 20; + private List<MotionEvent> mEvents; + + // Interaction parameters + private final float[] mXPositions; + private final float[] mYPositions; + private final long mDuration; + private final int[] mKeyCodes; + private final @Interaction.Type int mType; + + @IntDef({ + Interaction.Type.TAP, + Interaction.Type.FLING, + Interaction.Type.PINCH, + Interaction.Type.KEY_EVENT}) + public @interface Type { + int TAP = 0; + int FLING = 1; + int PINCH = 2; + int KEY_EVENT = 3; + } + + public static Interaction newFling(float startX, float startY, + float endX, float endY, long duration) { + return new Interaction(Interaction.Type.FLING, new float[]{startX, endX}, + new float[]{startY, endY}, duration); + } + + public static Interaction newFlingDown(float startX, float startY) { + return new Interaction(Interaction.Type.FLING, + new float[]{startX, startX}, + new float[]{startY, startY + DEFAULT_FLING_SIZE_PX}, DEFAULT_FLING_DURATION_MS); + } + + public static Interaction newFlingUp(float startX, float startY) { + return new Interaction(Interaction.Type.FLING, + new float[]{startX, startX}, new float[]{startY, startY - DEFAULT_FLING_SIZE_PX}, + DEFAULT_FLING_DURATION_MS); + } + + public static Interaction newTap(float startX, float startY) { + return new Interaction(Interaction.Type.TAP, + new float[]{startX, startX}, new float[]{startY, startY}, + DEFAULT_FLING_DURATION_MS); + } + + public static Interaction newKeyInput(int[] keyCodes) { + return new Interaction(keyCodes); + } + + public List<MotionEvent> getEvents() { + switch (mType) { + case Type.FLING: + mEvents = createInterpolatedEventList(mXPositions, mYPositions, mDuration); + break; + case Type.PINCH: + break; + case Type.TAP: + mEvents = createInterpolatedEventList(mXPositions, mYPositions, mDuration); + break; + } + + return mEvents; + } + + public int getType() { + return mType; + } + + public int[] getKeyCodes() { + return mKeyCodes; + } + + private static List<MotionEvent> createInterpolatedEventList( + float[] xPos, float[] yPos, long duration) { + long startTime = SystemClock.uptimeMillis() + 100; + List<MotionEvent> result = new ArrayList<>(); + + float startX = xPos[0]; + float startY = yPos[0]; + + MotionEvent downEvent = MotionEvent.obtain( + startTime, startTime, MotionEvent.ACTION_DOWN, startX, startY, 0); + result.add(downEvent); + + for (int i = 1; i < xPos.length; i++) { + float endX = xPos[i]; + float endY = yPos[i]; + float stepX = (endX - startX) / STEP_COUNT; + float stepY = (endY - startY) / STEP_COUNT; + float stepT = duration / STEP_COUNT; + + for (int j = 0; j < STEP_COUNT; j++) { + long deltaT = Math.round(j * stepT); + long deltaX = Math.round(j * stepX); + long deltaY = Math.round(j * stepY); + MotionEvent moveEvent = MotionEvent.obtain(startTime, startTime + deltaT, + MotionEvent.ACTION_MOVE, startX + deltaX, startY + deltaY, 0); + result.add(moveEvent); + } + + startX = endX; + startY = endY; + } + + float lastX = xPos[xPos.length - 1]; + float lastY = yPos[yPos.length - 1]; + MotionEvent lastEvent = MotionEvent.obtain(startTime, startTime + duration, + MotionEvent.ACTION_UP, lastX, lastY, 0); + result.add(lastEvent); + + return result; + } + + private Interaction(@Interaction.Type int type, + float[] xPos, float[] yPos, long duration) { + mType = type; + mXPositions = xPos; + mYPositions = yPos; + mDuration = duration; + mKeyCodes = null; + } + + private Interaction(int[] codes) { + mKeyCodes = codes; + mType = Type.KEY_EVENT; + mYPositions = null; + mXPositions = null; + mDuration = 0; + } + + private Interaction(@Interaction.Type int type, + List<Float> xPositions, List<Float> yPositions, long duration) { + if (xPositions.size() != yPositions.size()) { + throw new IllegalArgumentException("must have equal number of x and y positions"); + } + + int current = 0; + mXPositions = new float[xPositions.size()]; + for (float p : xPositions) { + mXPositions[current++] = p; + } + + current = 0; + mYPositions = new float[yPositions.size()]; + for (float p : xPositions) { + mXPositions[current++] = p; + } + + mType = type; + mDuration = duration; + mKeyCodes = null; + } +} diff --git a/tests/JankBench/app/src/main/jni/Android.mk b/tests/JankBench/app/src/main/jni/Android.mk new file mode 100644 index 000000000000..8ba874de0e8a --- /dev/null +++ b/tests/JankBench/app/src/main/jni/Android.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2015 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. + +LOCAL_PATH := $(call my-dir) +LOCAL_SDK_VERSION := 26 + +include $(CLEAR_VARS) + +LOCAL_CFLAGS = -Wno-unused-parameter + +LOCAL_MODULE:= libnativebench + +LOCAL_SRC_FILES := \ + Bench.cpp \ + WorkerPool.cpp \ + test.cpp + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/tests/JankBench/app/src/main/jni/Application.mk b/tests/JankBench/app/src/main/jni/Application.mk new file mode 100644 index 000000000000..09bc0aca14f1 --- /dev/null +++ b/tests/JankBench/app/src/main/jni/Application.mk @@ -0,0 +1,17 @@ +# Copyright (C) 2015 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. + +APP_ABI := armeabi + +APP_MODULES := nativebench diff --git a/tests/JankBench/app/src/main/jni/Bench.cpp b/tests/JankBench/app/src/main/jni/Bench.cpp new file mode 100644 index 000000000000..fbb4f11fa988 --- /dev/null +++ b/tests/JankBench/app/src/main/jni/Bench.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2015 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. + */ + +#include <android/log.h> +#include <math.h> +#include <stdlib.h> +#include <unistd.h> + +#include "Bench.h" + + +Bench::Bench() +{ + mTimeBucket = NULL; + mTimeBuckets = 0; + mTimeBucketDivisor = 1; + + mMemLatencyLastSize = 0; + mMemDst = NULL; + mMemSrc = NULL; + mMemLoopCount = 0; +} + + +Bench::~Bench() +{ +} + +uint64_t Bench::getTimeNanos() const +{ + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000); +} + +uint64_t Bench::getTimeMillis() const +{ + return getTimeNanos() / 1000000; +} + + +void Bench::testWork(void *usr, uint32_t idx) +{ + Bench *b = (Bench *)usr; + //__android_log_print(ANDROID_LOG_INFO, "bench", "test %i %p", idx, b); + + float f1 = 0.f; + float f2 = 0.f; + float f3 = 0.f; + float f4 = 0.f; + + float *ipk = b->mIpKernel[idx]; + volatile float *src = b->mSrcBuf[idx]; + volatile float *out = b->mOutBuf[idx]; + + //__android_log_print(ANDROID_LOG_INFO, "bench", "test %p %p %p", ipk, src, out); + + do { + + for (int i = 0; i < 1024; i++) { + f1 += src[i * 4] * ipk[i]; + f2 += src[i * 4 + 1] * ipk[i]; + f3 += src[i * 4 + 2] * ipk[i]; + f4 += sqrtf(f1 + f2 + f3); + } + out[0] = f1; + out[1] = f2; + out[2] = f3; + out[3] = f4; + + } while (b->incTimeBucket()); +} + +bool Bench::initIP() { + int workers = mWorkers.getWorkerCount(); + + mIpKernel = new float *[workers]; + mSrcBuf = new float *[workers]; + mOutBuf = new float *[workers]; + + for (int i = 0; i < workers; i++) { + mIpKernel[i] = new float[1024]; + mSrcBuf[i] = new float[4096]; + mOutBuf[i] = new float[4]; + } + + return true; +} + +bool Bench::runPowerManagementTest(uint64_t options) { + //__android_log_print(ANDROID_LOG_INFO, "bench", "rpmt x %i", options); + + mTimeBucketDivisor = 1000 * 1000; // use ms + allocateBuckets(2 * 1000); + + usleep(2 * 1000 * 1000); + + //__android_log_print(ANDROID_LOG_INFO, "bench", "rpmt 2 b %i", mTimeBuckets); + + mTimeStartNanos = getTimeNanos(); + mTimeEndNanos = mTimeStartNanos + mTimeBuckets * mTimeBucketDivisor; + memset(mTimeBucket, 0, sizeof(uint32_t) * mTimeBuckets); + + bool useMT = false; + + //__android_log_print(ANDROID_LOG_INFO, "bench", "rpmt 2.1 b %i", mTimeBuckets); + mTimeEndGroupNanos = mTimeStartNanos; + do { + // Advance 8ms + mTimeEndGroupNanos += 8 * 1000 * 1000; + + int threads = useMT ? 1 : 0; + useMT = !useMT; + if ((options & 0x1f) != 0) { + threads = options & 0x1f; + } + + //__android_log_print(ANDROID_LOG_INFO, "bench", "threads %i", threads); + + mWorkers.launchWork(testWork, this, threads); + } while (mTimeEndGroupNanos <= mTimeEndNanos); + + return true; +} + +bool Bench::allocateBuckets(size_t bucketCount) { + if (bucketCount == mTimeBuckets) { + return true; + } + + if (mTimeBucket != NULL) { + delete[] mTimeBucket; + mTimeBucket = NULL; + } + + mTimeBuckets = bucketCount; + if (mTimeBuckets > 0) { + mTimeBucket = new uint32_t[mTimeBuckets]; + } + + return true; +} + +bool Bench::init() { + mWorkers.init(); + + initIP(); + //ALOGV("%p Launching thread(s), CPUs %i", mRSC, mWorkers.mCount + 1); + + return true; +} + +bool Bench::incTimeBucket() const { + uint64_t time = getTimeNanos(); + uint64_t bucket = (time - mTimeStartNanos) / mTimeBucketDivisor; + + if (bucket >= mTimeBuckets) { + return false; + } + + __sync_fetch_and_add(&mTimeBucket[bucket], 1); + + return time < mTimeEndGroupNanos; +} + +void Bench::getData(float *data, size_t count) const { + if (count > mTimeBuckets) { + count = mTimeBuckets; + } + for (size_t ct = 0; ct < count; ct++) { + data[ct] = (float)mTimeBucket[ct]; + } +} + +bool Bench::runCPUHeatSoak(uint64_t /* options */) +{ + mTimeBucketDivisor = 1000 * 1000; // use ms + allocateBuckets(1000); + + mTimeStartNanos = getTimeNanos(); + mTimeEndNanos = mTimeStartNanos + mTimeBuckets * mTimeBucketDivisor; + memset(mTimeBucket, 0, sizeof(uint32_t) * mTimeBuckets); + + mTimeEndGroupNanos = mTimeEndNanos; + mWorkers.launchWork(testWork, this, 0); + return true; +} + +float Bench::runMemoryBandwidthTest(uint64_t size) +{ + uint64_t t1 = getTimeMillis(); + for (size_t ct = mMemLoopCount; ct > 0; ct--) { + memcpy(mMemDst, mMemSrc, size); + } + double dt = getTimeMillis() - t1; + dt /= 1000; + + double bw = ((double)size) * mMemLoopCount / dt; + bw /= 1024 * 1024 * 1024; + + float targetTime = 0.2f; + if (dt > targetTime) { + mMemLoopCount = (size_t)((double)mMemLoopCount / (dt / targetTime)); + } + + return (float)bw; +} + +float Bench::runMemoryLatencyTest(uint64_t size) +{ + //__android_log_print(ANDROID_LOG_INFO, "bench", "latency %i", (int)size); + void ** sp = (void **)mMemSrc; + size_t maxIndex = size / sizeof(void *); + size_t loops = ((maxIndex / 2) & (~3)); + //loops = 10; + + if (size != mMemLatencyLastSize) { + __android_log_print(ANDROID_LOG_INFO, "bench", "latency build %i %i", (int)maxIndex, loops); + mMemLatencyLastSize = size; + memset((void *)mMemSrc, 0, mMemLatencyLastSize); + + size_t lastIdx = 0; + for (size_t ct = 0; ct < loops; ct++) { + size_t ni = rand() * rand(); + ni = ni % maxIndex; + while ((sp[ni] != NULL) || (ni == lastIdx)) { + ni++; + if (ni >= maxIndex) { + ni = 1; + } + // __android_log_print(ANDROID_LOG_INFO, "bench", "gen ni loop %i %i", lastIdx, ni); + } + // __android_log_print(ANDROID_LOG_INFO, "bench", "gen ct = %i %i %i %p %p", (int)ct, lastIdx, ni, &sp[lastIdx], &sp[ni]); + sp[lastIdx] = &sp[ni]; + lastIdx = ni; + } + sp[lastIdx] = 0; + } + //__android_log_print(ANDROID_LOG_INFO, "bench", "latency testing"); + + uint64_t t1 = getTimeNanos(); + for (size_t ct = mMemLoopCount; ct > 0; ct--) { + size_t lc = 1; + volatile void *p = sp[0]; + while (p != NULL) { + // Unroll once to minimize branching overhead. + void **pn = (void **)p; + p = pn[0]; + pn = (void **)p; + p = pn[0]; + } + } + //__android_log_print(ANDROID_LOG_INFO, "bench", "v %i %i", loops * mMemLoopCount, v); + + double dt = getTimeNanos() - t1; + double dts = dt / 1000000000; + double lat = dt / (loops * mMemLoopCount); + __android_log_print(ANDROID_LOG_INFO, "bench", "latency ret %f", lat); + + float targetTime = 0.2f; + if (dts > targetTime) { + mMemLoopCount = (size_t)((double)mMemLoopCount / (dts / targetTime)); + if (mMemLoopCount < 1) { + mMemLoopCount = 1; + } + } + + return (float)lat; +} + +bool Bench::startMemTests() +{ + mMemSrc = (uint8_t *)malloc(1024*1024*64); + mMemDst = (uint8_t *)malloc(1024*1024*64); + + memset(mMemSrc, 0, 1024*1024*16); + memset(mMemDst, 0, 1024*1024*16); + + mMemLoopCount = 1; + uint64_t start = getTimeMillis(); + while((getTimeMillis() - start) < 500) { + memcpy(mMemDst, mMemSrc, 1024); + mMemLoopCount++; + } + mMemLatencyLastSize = 0; + return true; +} + +void Bench::endMemTests() +{ + free(mMemSrc); + free(mMemDst); + mMemSrc = NULL; + mMemDst = NULL; + mMemLatencyLastSize = 0; +} + +void Bench::GflopKernelC() { + int halfKX = (mGFlop.kernelXSize / 2); + for (int x = halfKX; x < (mGFlop.imageXSize - halfKX - 1); x++) { + const float * krnPtr = mGFlop.kernelBuffer; + float sum = 0.f; + + int srcInc = mGFlop.imageXSize - mGFlop.kernelXSize; + const float * srcPtr = &mGFlop.srcBuffer[x - halfKX]; + + for (int ix = 0; ix < mGFlop.kernelXSize; ix++) { + sum += srcPtr[0] * krnPtr[0]; + krnPtr++; + srcPtr++; + } + + float * dstPtr = &mGFlop.dstBuffer[x]; + dstPtr[0] = sum; + + } + +} + +void Bench::GflopKernelC_y3() { +} + +float Bench::runGFlopsTest(uint64_t /* options */) +{ + mTimeBucketDivisor = 1000 * 1000; // use ms + allocateBuckets(1000); + + mTimeStartNanos = getTimeNanos(); + mTimeEndNanos = mTimeStartNanos + mTimeBuckets * mTimeBucketDivisor; + memset(mTimeBucket, 0, sizeof(uint32_t) * mTimeBuckets); + + mTimeEndGroupNanos = mTimeEndNanos; + mWorkers.launchWork(testWork, this, 0); + + // Simulate image convolve + mGFlop.kernelXSize = 27; + mGFlop.imageXSize = 1024 * 1024; + + mGFlop.srcBuffer = (float *)malloc(mGFlop.imageXSize * sizeof(float)); + mGFlop.dstBuffer = (float *)malloc(mGFlop.imageXSize * sizeof(float)); + mGFlop.kernelBuffer = (float *)malloc(mGFlop.kernelXSize * sizeof(float)); + + double ops = mGFlop.kernelXSize; + ops = ops * 2.f - 1.f; + ops *= mGFlop.imageXSize; + + uint64_t t1 = getTimeNanos(); + GflopKernelC(); + double dt = getTimeNanos() - t1; + + dt /= 1000.f * 1000.f * 1000.f; + + double gflops = ops / dt / 1000000000.f; + + __android_log_print(ANDROID_LOG_INFO, "bench", "v %f %f %f", dt, ops, gflops); + + return (float)gflops; +} + + diff --git a/tests/JankBench/app/src/main/jni/Bench.h b/tests/JankBench/app/src/main/jni/Bench.h new file mode 100644 index 000000000000..43a9066d6c2c --- /dev/null +++ b/tests/JankBench/app/src/main/jni/Bench.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_BENCH_H +#define ANDROID_BENCH_H + +#include <pthread.h> + +#include "WorkerPool.h" + +#include <string.h> + + + +class Bench { +public: + Bench(); + ~Bench(); + + struct GFlop { + int kernelXSize; + //int kernelYSize; + int imageXSize; + //int imageYSize; + + float *srcBuffer; + float *kernelBuffer; + float *dstBuffer; + + + }; + GFlop mGFlop; + + bool init(); + + bool runPowerManagementTest(uint64_t options); + bool runCPUHeatSoak(uint64_t options); + + bool startMemTests(); + void endMemTests(); + float runMemoryBandwidthTest(uint64_t options); + float runMemoryLatencyTest(uint64_t options); + + float runGFlopsTest(uint64_t options); + + void getData(float *data, size_t count) const; + + + void finish(); + + void setPriority(int32_t p); + void destroyWorkerThreadResources(); + + uint64_t getTimeNanos() const; + uint64_t getTimeMillis() const; + + // Adds a work unit completed to the timeline and returns + // true if the test is ongoing, false if time is up + bool incTimeBucket() const; + + +protected: + WorkerPool mWorkers; + + bool mExit; + bool mPaused; + + static void testWork(void *usr, uint32_t idx); + +private: + uint8_t * volatile mMemSrc; + uint8_t * volatile mMemDst; + size_t mMemLoopCount; + size_t mMemLatencyLastSize; + + + float ** mIpKernel; + float * volatile * mSrcBuf; + float * volatile * mOutBuf; + uint32_t * mTimeBucket; + + uint64_t mTimeStartNanos; + uint64_t mTimeEndNanos; + uint64_t mTimeBucketDivisor; + uint32_t mTimeBuckets; + + uint64_t mTimeEndGroupNanos; + + bool initIP(); + void GflopKernelC(); + void GflopKernelC_y3(); + + bool allocateBuckets(size_t); + + +}; + + +#endif diff --git a/tests/JankBench/app/src/main/jni/WorkerPool.cpp b/tests/JankBench/app/src/main/jni/WorkerPool.cpp new file mode 100644 index 000000000000..a92ac9152987 --- /dev/null +++ b/tests/JankBench/app/src/main/jni/WorkerPool.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "WorkerPool.h" +//#include <atomic> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <android/log.h> + + +//static pthread_key_t gThreadTLSKey = 0; +//static uint32_t gThreadTLSKeyCount = 0; +//static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER; + + +WorkerPool::Signal::Signal() { + mSet = true; +} + +WorkerPool::Signal::~Signal() { + pthread_mutex_destroy(&mMutex); + pthread_cond_destroy(&mCondition); +} + +bool WorkerPool::Signal::init() { + int status = pthread_mutex_init(&mMutex, NULL); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool mutex init failure"); + return false; + } + + status = pthread_cond_init(&mCondition, NULL); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool condition init failure"); + pthread_mutex_destroy(&mMutex); + return false; + } + + return true; +} + +void WorkerPool::Signal::set() { + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i locking for set condition.", status); + return; + } + + mSet = true; + + status = pthread_cond_signal(&mCondition); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i on set condition.", status); + } + + status = pthread_mutex_unlock(&mMutex); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i unlocking for set condition.", status); + } +} + +bool WorkerPool::Signal::wait(uint64_t timeout) { + int status; + bool ret = false; + + status = pthread_mutex_lock(&mMutex); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i locking for condition.", status); + return false; + } + + if (!mSet) { + if (!timeout) { + status = pthread_cond_wait(&mCondition, &mMutex); + } else { +#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE) + status = pthread_cond_timeout_np(&mCondition, &mMutex, timeout / 1000000); +#else + // This is safe it will just make things less reponsive + status = pthread_cond_wait(&mCondition, &mMutex); +#endif + } + } + + if (!status) { + mSet = false; + ret = true; + } else { +#ifndef RS_SERVER + if (status != ETIMEDOUT) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i waiting for condition.", status); + } +#endif + } + + status = pthread_mutex_unlock(&mMutex); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i unlocking for condition.", status); + } + + return ret; +} + + + +WorkerPool::WorkerPool() { + mExit = false; + mRunningCount = 0; + mLaunchCount = 0; + mCount = 0; + mThreadId = NULL; + mNativeThreadId = NULL; + mLaunchSignals = NULL; + mLaunchCallback = NULL; + + +} + + +WorkerPool::~WorkerPool() { +__android_log_print(ANDROID_LOG_INFO, "bench", "~wp"); + mExit = true; + mLaunchData = NULL; + mLaunchCallback = NULL; + mRunningCount = mCount; + + __sync_synchronize(); + for (uint32_t ct = 0; ct < mCount; ct++) { + mLaunchSignals[ct].set(); + } + void *res; + for (uint32_t ct = 0; ct < mCount; ct++) { + pthread_join(mThreadId[ct], &res); + } + //rsAssert(__sync_fetch_and_or(&mRunningCount, 0) == 0); + free(mThreadId); + free(mNativeThreadId); + delete[] mLaunchSignals; +} + +bool WorkerPool::init(int threadCount) { + int cpu = sysconf(_SC_NPROCESSORS_CONF); + if (threadCount > 0) { + cpu = threadCount; + } + if (cpu < 1) { + return false; + } + mCount = (uint32_t)cpu; + + __android_log_print(ANDROID_LOG_INFO, "Bench", "ThreadLaunch %i", mCount); + + mThreadId = (pthread_t *) calloc(mCount, sizeof(pthread_t)); + mNativeThreadId = (pid_t *) calloc(mCount, sizeof(pid_t)); + mLaunchSignals = new Signal[mCount]; + mLaunchCallback = NULL; + + mCompleteSignal.init(); + mRunningCount = mCount; + mLaunchCount = 0; + __sync_synchronize(); + + pthread_attr_t threadAttr; + int status = pthread_attr_init(&threadAttr); + if (status) { + __android_log_print(ANDROID_LOG_INFO, "bench", "Failed to init thread attribute."); + return false; + } + + for (uint32_t ct=0; ct < mCount; ct++) { + status = pthread_create(&mThreadId[ct], &threadAttr, helperThreadProc, this); + if (status) { + mCount = ct; + __android_log_print(ANDROID_LOG_INFO, "bench", "Created fewer than expected number of threads."); + return false; + } + } + while (__sync_fetch_and_or(&mRunningCount, 0) != 0) { + usleep(100); + } + + pthread_attr_destroy(&threadAttr); + return true; +} + +void * WorkerPool::helperThreadProc(void *vwp) { + WorkerPool *wp = (WorkerPool *)vwp; + + uint32_t idx = __sync_fetch_and_add(&wp->mLaunchCount, 1); + + wp->mLaunchSignals[idx].init(); + wp->mNativeThreadId[idx] = gettid(); + + while (!wp->mExit) { + wp->mLaunchSignals[idx].wait(); + if (wp->mLaunchCallback) { + // idx +1 is used because the calling thread is always worker 0. + wp->mLaunchCallback(wp->mLaunchData, idx); + } + __sync_fetch_and_sub(&wp->mRunningCount, 1); + wp->mCompleteSignal.set(); + } + + //ALOGV("RS helperThread exited %p idx=%i", dc, idx); + return NULL; +} + + +void WorkerPool::waitForAll() const { +} + +void WorkerPool::waitFor(uint64_t) const { +} + + + +uint64_t WorkerPool::launchWork(WorkerCallback_t cb, void *usr, int maxThreads) { + //__android_log_print(ANDROID_LOG_INFO, "bench", "lw 1"); + mLaunchData = usr; + mLaunchCallback = cb; + + if (maxThreads < 1) { + maxThreads = mCount; + } + if ((uint32_t)maxThreads > mCount) { + //__android_log_print(ANDROID_LOG_INFO, "bench", "launchWork max > count", maxThreads, mCount); + maxThreads = mCount; + } + + //__android_log_print(ANDROID_LOG_INFO, "bench", "lw 2 %i %i %i", maxThreads, mRunningCount, mCount); + mRunningCount = maxThreads; + __sync_synchronize(); + + for (int ct = 0; ct < maxThreads; ct++) { + mLaunchSignals[ct].set(); + } + + //__android_log_print(ANDROID_LOG_INFO, "bench", "lw 3 %i", mRunningCount); + while (__sync_fetch_and_or(&mRunningCount, 0) != 0) { + //__android_log_print(ANDROID_LOG_INFO, "bench", "lw 3.1 %i", mRunningCount); + mCompleteSignal.wait(); + } + + //__android_log_print(ANDROID_LOG_INFO, "bench", "lw 4 %i", mRunningCount); + return 0; + +} + + + diff --git a/tests/JankBench/app/src/main/jni/WorkerPool.h b/tests/JankBench/app/src/main/jni/WorkerPool.h new file mode 100644 index 000000000000..f8985d21be87 --- /dev/null +++ b/tests/JankBench/app/src/main/jni/WorkerPool.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_WORKER_POOL_H +#define ANDROID_WORKER_POOL_H + +#include <pthread.h> +#include <string.h> + + + +class WorkerPool { +public: + WorkerPool(); + ~WorkerPool(); + + typedef void (*WorkerCallback_t)(void *usr, uint32_t idx); + + bool init(int threadCount = -1); + int getWorkerCount() const {return mCount;} + + void waitForAll() const; + void waitFor(uint64_t) const; + uint64_t launchWork(WorkerCallback_t cb, void *usr, int maxThreads = -1); + + + + +protected: + class Signal { + public: + Signal(); + ~Signal(); + + bool init(); + void set(); + + // returns true if the signal occured + // false for timeout + bool wait(uint64_t timeout = 0); + + protected: + bool mSet; + pthread_mutex_t mMutex; + pthread_cond_t mCondition; + }; + + bool mExit; + volatile int mRunningCount; + volatile int mLaunchCount; + uint32_t mCount; + pthread_t *mThreadId; + pid_t *mNativeThreadId; + Signal mCompleteSignal; + Signal *mLaunchSignals; + WorkerCallback_t mLaunchCallback; + void *mLaunchData; + + + + +private: + //static void * threadProc(void *); + static void * helperThreadProc(void *); + + +}; + + +#endif diff --git a/tests/JankBench/app/src/main/jni/test.cpp b/tests/JankBench/app/src/main/jni/test.cpp new file mode 100644 index 000000000000..e163daa531c8 --- /dev/null +++ b/tests/JankBench/app/src/main/jni/test.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2015 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. + */ + +#include <stdlib.h> +#include <stdio.h> +//#include <fcntl.h> +//#include <unistd.h> +#include <math.h> +#include <inttypes.h> +#include <time.h> +#include <android/log.h> + +#include "jni.h" +#include "Bench.h" + +#define FUNC(name) Java_com_android_benchmark_synthetic_TestInterface_##name + +static uint64_t GetTime() { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000); +} + +extern "C" { + +jlong Java_com_android_benchmark_synthetic_TestInterface_nInit(JNIEnv *_env, jobject _this, jlong options) { + Bench *b = new Bench(); + bool ret = b->init(); + + if (ret) { + return (jlong)b; + } + + delete b; + return 0; +} + +void Java_com_android_benchmark_synthetic_TestInterface_nDestroy(JNIEnv *_env, jobject _this, jlong _b) { + Bench *b = (Bench *)_b; + + delete b; +} + +jboolean Java_com_android_benchmark_synthetic_TestInterface_nRunPowerManagementTest( + JNIEnv *_env, jobject _this, jlong _b, jlong options) { + Bench *b = (Bench *)_b; + return b->runPowerManagementTest(options); +} + +jboolean Java_com_android_benchmark_synthetic_TestInterface_nRunCPUHeatSoakTest( + JNIEnv *_env, jobject _this, jlong _b, jlong options) { + Bench *b = (Bench *)_b; + return b->runCPUHeatSoak(options); +} + +float Java_com_android_benchmark_synthetic_TestInterface_nGetData( + JNIEnv *_env, jobject _this, jlong _b, jfloatArray data) { + Bench *b = (Bench *)_b; + + jsize len = _env->GetArrayLength(data); + float * ptr = _env->GetFloatArrayElements(data, 0); + + b->getData(ptr, len); + + _env->ReleaseFloatArrayElements(data, (jfloat *)ptr, 0); + + return 0; +} + +jboolean Java_com_android_benchmark_synthetic_TestInterface_nMemTestStart( + JNIEnv *_env, jobject _this, jlong _b) { + Bench *b = (Bench *)_b; + return b->startMemTests(); +} + +float Java_com_android_benchmark_synthetic_TestInterface_nMemTestBandwidth( + JNIEnv *_env, jobject _this, jlong _b, jlong opt) { + Bench *b = (Bench *)_b; + return b->runMemoryBandwidthTest(opt); +} + +float Java_com_android_benchmark_synthetic_TestInterface_nGFlopsTest( + JNIEnv *_env, jobject _this, jlong _b, jlong opt) { + Bench *b = (Bench *)_b; + return b->runGFlopsTest(opt); +} + +float Java_com_android_benchmark_synthetic_TestInterface_nMemTestLatency( + JNIEnv *_env, jobject _this, jlong _b, jlong opt) { + Bench *b = (Bench *)_b; + return b->runMemoryLatencyTest(opt); +} + +void Java_com_android_benchmark_synthetic_TestInterface_nMemTestEnd( + JNIEnv *_env, jobject _this, jlong _b) { + Bench *b = (Bench *)_b; + b->endMemTests(); +} + +float Java_com_android_benchmark_synthetic_TestInterface_nMemoryTest( + JNIEnv *_env, jobject _this, jint subtest) { + + uint8_t * volatile m1 = (uint8_t *)malloc(1024*1024*64); + uint8_t * m2 = (uint8_t *)malloc(1024*1024*64); + + memset(m1, 0, 1024*1024*16); + memset(m2, 0, 1024*1024*16); + + //__android_log_print(ANDROID_LOG_INFO, "bench", "test %i %p %p", subtest, m1, m2); + + + size_t loopCount = 0; + uint64_t start = GetTime(); + while((GetTime() - start) < 1000000000) { + memcpy(m1, m2, subtest); + loopCount++; + } + if (loopCount == 0) { + loopCount = 1; + } + + size_t count = loopCount; + uint64_t t1 = GetTime(); + while (loopCount > 0) { + memcpy(m1, m2, subtest); + loopCount--; + } + uint64_t t2 = GetTime(); + + double dt = t2 - t1; + dt /= 1000 * 1000 * 1000; + double bw = ((double)subtest) * count / dt; + + bw /= 1024 * 1024 * 1024; + + __android_log_print(ANDROID_LOG_INFO, "bench", "size %i, bw %f", subtest, bw); + + free (m1); + free (m2); + return (float)bw; +} + +jlong Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestMalloc( + JNIEnv *_env, jobject _this, jint bytes) { + uint8_t *p = (uint8_t *)malloc(bytes); + memset(p, 0, bytes); + return (jlong)p; +} + +void Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestFree( + JNIEnv *_env, jobject _this, jlong ptr) { + free((void *)ptr); +} + +jlong Java_com_android_benchmark_synthetic_MemoryAvailableLoad2_nMemTestMalloc( + JNIEnv *_env, jobject _this, jint bytes) { + return Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestMalloc(_env, _this, bytes); +} + +void Java_com_android_benchmark_synthetic_MemoryAvailableLoad2_nMemTestFree( + JNIEnv *_env, jobject _this, jlong ptr) { + Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestFree(_env, _this, ptr); +} + +}; // extern "C" diff --git a/tests/JankBench/app/src/main/res/drawable/ic_play.png b/tests/JankBench/app/src/main/res/drawable/ic_play.png Binary files differnew file mode 100644 index 000000000000..13ed283cb5bd --- /dev/null +++ b/tests/JankBench/app/src/main/res/drawable/ic_play.png diff --git a/tests/JankBench/app/src/main/res/drawable/img1.jpg b/tests/JankBench/app/src/main/res/drawable/img1.jpg Binary files differnew file mode 100644 index 000000000000..33c1fedc0c6f --- /dev/null +++ b/tests/JankBench/app/src/main/res/drawable/img1.jpg diff --git a/tests/JankBench/app/src/main/res/drawable/img2.jpg b/tests/JankBench/app/src/main/res/drawable/img2.jpg Binary files differnew file mode 100644 index 000000000000..1ea97f2cc59c --- /dev/null +++ b/tests/JankBench/app/src/main/res/drawable/img2.jpg diff --git a/tests/JankBench/app/src/main/res/drawable/img3.jpg b/tests/JankBench/app/src/main/res/drawable/img3.jpg Binary files differnew file mode 100644 index 000000000000..ff992695621d --- /dev/null +++ b/tests/JankBench/app/src/main/res/drawable/img3.jpg diff --git a/tests/JankBench/app/src/main/res/drawable/img4.jpg b/tests/JankBench/app/src/main/res/drawable/img4.jpg Binary files differnew file mode 100644 index 000000000000..d9cbd2f42190 --- /dev/null +++ b/tests/JankBench/app/src/main/res/drawable/img4.jpg diff --git a/tests/JankBench/app/src/main/res/layout/activity_bitmap_upload.xml b/tests/JankBench/app/src/main/res/layout/activity_bitmap_upload.xml new file mode 100644 index 000000000000..6b3c8992d414 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/activity_bitmap_upload.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/upload_root" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="10dp" + android:clipToPadding="false"> + <android.support.v7.widget.CardView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + <view class="com.android.benchmark.ui.BitmapUploadActivity$UploadView" + android:id="@+id/upload_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </android.support.v7.widget.CardView> + + <android.support.v4.widget.Space + android:layout_height="10dp" + android:layout_width="match_parent" /> + + <android.support.v7.widget.CardView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + <android.support.v4.widget.Space + android:layout_height="10dp" + android:layout_width="match_parent" /> + + <android.support.v7.widget.CardView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/activity_home.xml b/tests/JankBench/app/src/main/res/layout/activity_home.xml new file mode 100644 index 000000000000..c4f429922aab --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + tools:context=".app.HomeActivity"> + + <android.support.design.widget.AppBarLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:theme="@style/AppTheme.AppBarOverlay"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + app:popupTheme="@style/AppTheme.PopupOverlay" /> + + </android.support.design.widget.AppBarLayout> + + <include layout="@layout/content_main" /> + +</android.support.design.widget.CoordinatorLayout> diff --git a/tests/JankBench/app/src/main/res/layout/activity_list_fragment.xml b/tests/JankBench/app/src/main/res/layout/activity_list_fragment.xml new file mode 100644 index 000000000000..0aaaddebc040 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/activity_list_fragment.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context=".app.HomeActivity" + android:orientation="vertical"> + + <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/list_fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout> diff --git a/tests/JankBench/app/src/main/res/layout/activity_memory.xml b/tests/JankBench/app/src/main/res/layout/activity_memory.xml new file mode 100644 index 000000000000..fd5cadc24984 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/activity_memory.xml @@ -0,0 +1,49 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context="com.android.benchmark.synthetic.MemoryActivity"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:text="Large Text" + android:id="@+id/textView_status" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="" + android:id="@+id/textView_min" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="" + android:id="@+id/textView_max" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="" + android:id="@+id/textView_typical" /> + + <view + android:layout_width="wrap_content" + android:layout_height="wrap_content" + class="com.android.benchmark.app.PerfTimeline" + android:id="@+id/mem_timeline" /> + + </LinearLayout> +</RelativeLayout> diff --git a/tests/JankBench/app/src/main/res/layout/activity_running_list.xml b/tests/JankBench/app/src/main/res/layout/activity_running_list.xml new file mode 100644 index 000000000000..7b7b93066f93 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/activity_running_list.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context=".app.HomeActivity" + android:orientation="vertical"> + + <TextView + android:id="@+id/score_text_view" + android:textSize="20sp" + android:textStyle="bold" + android:layout_width="match_parent" + android:layout_height="30dp" + /> + + <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/list_fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/benchmark_list_group_row.xml b/tests/JankBench/app/src/main/res/layout/benchmark_list_group_row.xml new file mode 100644 index 000000000000..5375dbca194b --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/benchmark_list_group_row.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/group_name" + android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft" + android:textSize="17dp" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/benchmark_list_item.xml b/tests/JankBench/app/src/main/res/layout/benchmark_list_item.xml new file mode 100644 index 000000000000..5282e14fddfc --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/benchmark_list_item.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:paddingLeft="?android:expandableListPreferredChildPaddingLeft" + android:layout_width="match_parent" + android:layout_height="55dip"> + + + <CheckBox + android:id="@+id/benchmark_enable_checkbox" + android:checked="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <TextView + android:id="@+id/benchmark_name" + android:textSize="17dp" + android:paddingLeft="10dp" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/card_row.xml b/tests/JankBench/app/src/main/res/layout/card_row.xml new file mode 100644 index 000000000000..215f9df9b7fd --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/card_row.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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 + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="100dp" + android:paddingStart="10dp" + android:paddingEnd="10dp" + android:paddingTop="5dp" + android:paddingBottom="5dp" + android:clipToPadding="false" + android:background="@null"> + <android.support.v7.widget.CardView + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"> + <TextView + android:id="@+id/card_text" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </android.support.v7.widget.CardView> + + <android.support.v4.widget.Space + android:layout_height="match_parent" + android:layout_width="10dp" /> + + <android.support.v7.widget.CardView + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" /> +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/content_main.xml b/tests/JankBench/app/src/main/res/layout/content_main.xml new file mode 100644 index 000000000000..201bd66a6589 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/content_main.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<fragment xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/fragment_start_button" + android:name="com.android.benchmark.app.BenchmarkDashboardFragment" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:layout="@layout/fragment_dashboard" /> + diff --git a/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml b/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml new file mode 100644 index 000000000000..f3100c7ea75d --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/main_content" + android:layout_width="match_parent" + android:layout_height="fill_parent" + android:fitsSystemWindows="true"> + + <android.support.design.widget.AppBarLayout + android:id="@+id/appbar" + android:layout_width="match_parent" + android:layout_height="@dimen/detail_backdrop_height" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" + android:fitsSystemWindows="true"> + + <android.support.design.widget.CollapsingToolbarLayout + android:id="@+id/collapsing_toolbar" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_scrollFlags="scroll" + android:fitsSystemWindows="true" + app:contentScrim="?attr/colorPrimary" + app:expandedTitleMarginStart="48dp" + app:expandedTitleMarginEnd="64dp"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + app:popupTheme="@style/ThemeOverlay.AppCompat.Light" + app:layout_collapseMode="parallax" /> + + <ImageView + android:id="@+id/backdrop" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@mipmap/ic_launcher" + android:scaleType="centerCrop" + android:fitsSystemWindows="true" + app:layout_collapseMode="parallax" /> + + </android.support.design.widget.CollapsingToolbarLayout> + + </android.support.design.widget.AppBarLayout> + + <android.support.v4.widget.NestedScrollView + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + + <ExpandableListView + android:id="@+id/test_list" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + </LinearLayout> + + </android.support.v4.widget.NestedScrollView> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/start_button" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_play" + app:layout_anchor="@id/appbar" + app:layout_anchorGravity="bottom|right|end" + android:layout_margin="@dimen/fab_margin" + android:clickable="true"/> + +</android.support.design.widget.CoordinatorLayout> diff --git a/tests/JankBench/app/src/main/res/layout/fragment_ui_results_detail.xml b/tests/JankBench/app/src/main/res/layout/fragment_ui_results_detail.xml new file mode 100644 index 000000000000..74d98910294c --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/fragment_ui_results_detail.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ExpandableListView + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/image_scroll_list_item.xml b/tests/JankBench/app/src/main/res/layout/image_scroll_list_item.xml new file mode 100644 index 000000000000..c1662ea76fd3 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/image_scroll_list_item.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/image_scroll_image" + android:scaleType="centerCrop" + android:layout_width="100dp" + android:layout_height="100dp" /> + + <TextView + android:id="@+id/image_scroll_text" + android:layout_gravity="right" + android:textSize="12sp" + android:paddingLeft="20dp" + android:layout_width="100dp" + android:layout_height="wrap_content" /> +</LinearLayout> diff --git a/tests/JankBench/app/src/main/res/layout/results_list_item.xml b/tests/JankBench/app/src/main/res/layout/results_list_item.xml new file mode 100644 index 000000000000..f38b147ac17e --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/results_list_item.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" android:layout_width="match_parent" + android:padding="8dp" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/result_name" + android:textSize="16sp" + android:layout_gravity="left" + android:layout_width="200dp" + android:layout_height="wrap_content" /> + + <TextView + android:id="@+id/result_value" + android:textSize="16sp" + android:layout_gravity="right" + android:layout_width="200dp" + android:layout_height="wrap_content" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/layout/running_benchmark_list_item.xml b/tests/JankBench/app/src/main/res/layout/running_benchmark_list_item.xml new file mode 100644 index 000000000000..8a9d015e6ad3 --- /dev/null +++ b/tests/JankBench/app/src/main/res/layout/running_benchmark_list_item.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/benchmark_name" + android:textSize="17sp" + android:paddingLeft="?android:listPreferredItemPaddingLeft" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/menu/menu_main.xml b/tests/JankBench/app/src/main/res/menu/menu_main.xml new file mode 100644 index 000000000000..1633acdaaf31 --- /dev/null +++ b/tests/JankBench/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".app.HomeActivity"> + <item + android:id="@+id/action_settings" + android:orderInCategory="100" + android:title="@string/action_export" + app:showAsAction="never" /> +</menu> diff --git a/tests/JankBench/app/src/main/res/menu/menu_memory.xml b/tests/JankBench/app/src/main/res/menu/menu_memory.xml new file mode 100644 index 000000000000..f2df7c9d7662 --- /dev/null +++ b/tests/JankBench/app/src/main/res/menu/menu_memory.xml @@ -0,0 +1,5 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" tools:context="com.android.benchmark.Memory"> + <item android:id="@+id/action_settings" android:title="@string/action_export" + android:orderInCategory="100" /> +</menu> diff --git a/tests/JankBench/app/src/main/res/mipmap-hdpi/ic_launcher.png b/tests/JankBench/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..cde69bcccec6 --- /dev/null +++ b/tests/JankBench/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/tests/JankBench/app/src/main/res/mipmap-mdpi/ic_launcher.png b/tests/JankBench/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..c133a0cbd379 --- /dev/null +++ b/tests/JankBench/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/tests/JankBench/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/tests/JankBench/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..bfa42f0e7b91 --- /dev/null +++ b/tests/JankBench/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/tests/JankBench/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tests/JankBench/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..324e72cdd748 --- /dev/null +++ b/tests/JankBench/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/tests/JankBench/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/tests/JankBench/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..aee44e138434 --- /dev/null +++ b/tests/JankBench/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/tests/JankBench/app/src/main/res/values-v21/styles.xml b/tests/JankBench/app/src/main/res/values-v21/styles.xml new file mode 100644 index 000000000000..99ed094a18cf --- /dev/null +++ b/tests/JankBench/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,25 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + + <style name="AppTheme.NoActionBar"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> + <item name="android:windowDrawsSystemBarBackgrounds">true</item> + <item name="android:statusBarColor">@android:color/transparent</item> + </style> +</resources> diff --git a/tests/JankBench/app/src/main/res/values-w820dp/dimens.xml b/tests/JankBench/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 000000000000..e783e5d10773 --- /dev/null +++ b/tests/JankBench/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,22 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + <!-- Example customization of dimensions originally defined in res/values/dimens.xml + (such as screen margins) for screens with more than 820dp of available width. This + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/tests/JankBench/app/src/main/res/values/attrs.xml b/tests/JankBench/app/src/main/res/values/attrs.xml new file mode 100644 index 000000000000..a4286f173b15 --- /dev/null +++ b/tests/JankBench/app/src/main/res/values/attrs.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + <!-- Root tag for benchmarks --> + <declare-styleable name="AndroidBenchmarks" /> + + <declare-styleable name="BenchmarkGroup"> + <attr name="name" format="reference|string" /> + <attr name="description" format="reference|string" /> + </declare-styleable> + + <declare-styleable name="Benchmark"> + <attr name="name" /> + <attr name="description" /> + <attr name="id" format="reference" /> + <attr name="category" format="enum"> + <enum name="generic" value="0" /> + <enum name="ui" value="1" /> + <enum name="compute" value="2" /> + </attr> + </declare-styleable> + + <declare-styleable name="PerfTimeline"><attr name="exampleString" format="string"/> + <attr name="exampleDimension" format="dimension"/> + <attr name="exampleColor" format="color"/> + <attr name="exampleDrawable" format="color|reference"/> + </declare-styleable> + +</resources> + diff --git a/tests/JankBench/app/src/main/res/values/colors.xml b/tests/JankBench/app/src/main/res/values/colors.xml new file mode 100644 index 000000000000..59156eef0067 --- /dev/null +++ b/tests/JankBench/app/src/main/res/values/colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> +</resources> diff --git a/tests/JankBench/app/src/main/res/values/dimens.xml b/tests/JankBench/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000000..9da649a15f37 --- /dev/null +++ b/tests/JankBench/app/src/main/res/values/dimens.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="detail_backdrop_height">256dp</dimen> + <dimen name="card_margin">16dp</dimen> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> + <dimen name="fab_margin">16dp</dimen> + <dimen name="app_bar_height">200dp</dimen> + <dimen name="item_width">200dp</dimen> + <dimen name="text_margin">16dp</dimen> +</resources> diff --git a/tests/JankBench/app/src/main/res/values/ids.xml b/tests/JankBench/app/src/main/res/values/ids.xml new file mode 100644 index 000000000000..6801fd9f61ec --- /dev/null +++ b/tests/JankBench/app/src/main/res/values/ids.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + <item name="benchmark_list_view_scroll" type="id" /> + <item name="benchmark_image_list_view_scroll" type="id" /> + <item name="benchmark_shadow_grid" type="id" /> + <item name="benchmark_text_high_hitrate" type="id" /> + <item name="benchmark_text_low_hitrate" type="id" /> + <item name="benchmark_edit_text_input" type="id" /> + <item name="benchmark_overdraw" type="id" /> + <item name="benchmark_memory_bandwidth" type="id" /> + <item name="benchmark_memory_latency" type="id" /> + <item name="benchmark_power_management" type="id" /> + <item name="benchmark_cpu_heat_soak" type="id" /> + <item name="benchmark_cpu_gflops" type="id" /> +</resources>
\ No newline at end of file diff --git a/tests/JankBench/app/src/main/res/values/strings.xml b/tests/JankBench/app/src/main/res/values/strings.xml new file mode 100644 index 000000000000..270adf89e4ed --- /dev/null +++ b/tests/JankBench/app/src/main/res/values/strings.xml @@ -0,0 +1,51 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + <string name="app_name">Benchmark</string> + + <string name="action_export">Export to CSV</string> + + <string name="list_view_scroll_name">List View Fling</string> + <string name="list_view_scroll_description">Tests list view fling performance</string> + <string name="image_list_view_scroll_name">Image List View Fling</string> + <string name="image_list_view_scroll_description">Tests list view fling performance with images</string> + <string name="shadow_grid_name">Shadow Grid Fling</string> + <string name="shadow_grid_description">Tests shadow grid fling performance with images</string> + <string name="text_high_hitrate_name">High-hitrate text render</string> + <string name="text_high_hitrate_description">Tests high hitrate text rendering</string> + <string name="text_low_hitrate_name">Low-hitrate text render</string> + <string name="text_low_hitrate_description">Tests low-hitrate text rendering</string> + <string name="edit_text_input_name">Edit Text Input</string> + <string name="edit_text_input_description">Tests edit text input</string> + <string name="overdraw_name">Overdraw Test</string> + <string name="overdraw_description">Tests how the device handles overdraw</string> + <string name="memory_bandwidth_name">Memory Bandwidth</string> + <string name="memory_bandwidth_description">Test device\'s memory bandwidth</string> + <string name="memory_latency_name">Memory Latency</string> + <string name="memory_latency_description">Test device\'s memory latency</string> + <string name="power_management_name">Power Management</string> + <string name="power_management_description">Test device\'s power management</string> + <string name="cpu_heat_soak_name">CPU Heat Soak</string> + <string name="cpu_heat_soak_description">How hot can we make it?</string> + <string name="cpu_gflops_name">CPU GFlops</string> + <string name="cpu_gflops_description">How many gigaflops can the device attain?</string> + + <string name="benchmark_category_ui">UI</string> + <string name="benchmark_category_compute">Compute</string> + <string name="benchmark_category_generic">Generic</string> + <string name="title_activity_image_list_view_scroll">ImageListViewScroll</string> +</resources> diff --git a/tests/JankBench/app/src/main/res/values/styles.xml b/tests/JankBench/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..25ce730ba2d1 --- /dev/null +++ b/tests/JankBench/app/src/main/res/values/styles.xml @@ -0,0 +1,43 @@ +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + </style> + + <style name="AppTheme.NoActionBar"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> + </style> + + <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> + + <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> + + <style name="Widget.CardContent" parent="android:Widget"> + <item name="android:paddingLeft">16dp</item> + <item name="android:paddingRight">16dp</item> + <item name="android:paddingTop">24dp</item> + <item name="android:paddingBottom">24dp</item> + <item name="android:orientation">vertical</item> + </style> +</resources> diff --git a/tests/JankBench/app/src/main/res/xml/benchmark.xml b/tests/JankBench/app/src/main/res/xml/benchmark.xml new file mode 100644 index 000000000000..07c453c25359 --- /dev/null +++ b/tests/JankBench/app/src/main/res/xml/benchmark.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 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. + ~ + --> + +<com.android.benchmark.BenchmarkGroup + xmlns:benchmark="http://schemas.android.com/apk/res-auto" + benchmark:description="Benchmarks of the Android system" + benchmark:name="Android Benchmarks"> + + <com.android.benchmark.Benchmark + benchmark:name="@string/list_view_scroll_name" + benchmark:id="@id/benchmark_list_view_scroll" + benchmark:category="ui" + benchmark:description="@string/list_view_scroll_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/image_list_view_scroll_name" + benchmark:id="@id/benchmark_image_list_view_scroll" + benchmark:category="ui" + benchmark:description="@string/image_list_view_scroll_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/shadow_grid_name" + benchmark:id="@id/benchmark_shadow_grid" + benchmark:category="ui" + benchmark:description="@string/shadow_grid_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/text_low_hitrate_name" + benchmark:id="@id/benchmark_text_low_hitrate" + benchmark:category="ui" + benchmark:description="@string/text_low_hitrate_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/text_high_hitrate_name" + benchmark:id="@id/benchmark_text_high_hitrate" + benchmark:category="ui" + benchmark:description="@string/text_high_hitrate_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/edit_text_input_name" + benchmark:id="@id/benchmark_edit_text_input" + benchmark:category="ui" + benchmark:description="@string/edit_text_input_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/overdraw_name" + benchmark:id="@id/benchmark_overdraw" + benchmark:category="ui" + benchmark:description="@string/overdraw_description" /> + + <!-- + <com.android.benchmark.Benchmark + benchmark:name="@string/memory_bandwidth_name" + benchmark:id="@id/benchmark_memory_bandwidth" + benchmark:category="compute" + benchmark:description="@string/memory_bandwidth_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/memory_latency_name" + benchmark:id="@id/benchmark_memory_latency" + benchmark:category="compute" + benchmark:description="@string/memory_latency_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/power_management_name" + benchmark:id="@id/benchmark_power_management" + benchmark:category="compute" + benchmark:description="@string/power_management_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/cpu_heat_soak_name" + benchmark:id="@id/benchmark_cpu_heat_soak" + benchmark:category="compute" + benchmark:description="@string/cpu_heat_soak_description" /> + + <com.android.benchmark.Benchmark + benchmark:name="@string/cpu_gflops_name" + benchmark:id="@id/benchmark_cpu_gflops" + benchmark:category="compute" + benchmark:description="@string/cpu_gflops_description" /> + --> + +</com.android.benchmark.BenchmarkGroup>
\ No newline at end of file diff --git a/tests/JankBench/app/src/test/java/com/android/benchmark/ExampleUnitTest.java b/tests/JankBench/app/src/test/java/com/android/benchmark/ExampleUnitTest.java new file mode 100644 index 000000000000..4464e87ef449 --- /dev/null +++ b/tests/JankBench/app/src/test/java/com/android/benchmark/ExampleUnitTest.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 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.benchmark; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/tests/JankBench/scripts/adbutil.py b/tests/JankBench/scripts/adbutil.py new file mode 100644 index 000000000000..ad9f7d9fa320 --- /dev/null +++ b/tests/JankBench/scripts/adbutil.py @@ -0,0 +1,62 @@ +import subprocess +import re +import threading + +ATRACE_PATH="/android/catapult/systrace/systrace/systrace.py" + +class AdbError(RuntimeError): + def __init__(self, arg): + self.args = arg + +def am(serial, cmd, args): + if not isinstance(args, list): + args = [args] + full_args = ["am"] + [cmd] + args + __call_adb(serial, full_args, False) + +def pm(serial, cmd, args): + if not isinstance(args, list): + args = [args] + full_args = ["pm"] + [cmd] + args + __call_adb(serial, full_args, False) + +def dumpsys(serial, topic): + return __call_adb(serial, ["dumpsys"] + [topic], True) + +def trace(serial, + tags = ["gfx", "sched", "view", "freq", "am", "wm", "power", "load", "memreclaim"], + time = "10"): + args = [ATRACE_PATH, "-e", serial, "-t" + time, "-b32768"] + tags + subprocess.call(args) + +def wake(serial): + output = dumpsys(serial, "power") + wakefulness = re.search('mWakefulness=([a-zA-Z]+)', output) + if wakefulness.group(1) != "Awake": + __call_adb(serial, ["input", "keyevent", "KEYCODE_POWER"], False) + +def root(serial): + subprocess.call(["adb", "-s", serial, "root"]) + +def pull(serial, path, dest): + subprocess.call(["adb", "-s", serial, "wait-for-device", "pull"] + [path] + [dest]) + +def shell(serial, cmd): + __call_adb(serial, cmd, False) + +def track_logcat(serial, awaited_string, callback): + threading.Thread(target=__track_logcat, name=serial + "-waiter", args=(serial, awaited_string, callback)).start() + +def __call_adb(serial, args, block): + full_args = ["adb", "-s", serial, "wait-for-device", "shell"] + args + print full_args + output = None + try: + if block: + output = subprocess.check_output(full_args) + else: + subprocess.call(full_args) + except subprocess.CalledProcessError: + raise AdbError("Error calling " + " ".join(args)) + + return output diff --git a/tests/JankBench/scripts/collect.py b/tests/JankBench/scripts/collect.py new file mode 100755 index 000000000000..87a059453b43 --- /dev/null +++ b/tests/JankBench/scripts/collect.py @@ -0,0 +1,240 @@ +#!/usr/bin/python + +import optparse +import sys +import sqlite3 +import scipy.stats +import numpy +from math import log10, floor +import matplotlib + +matplotlib.use("Agg") + +import matplotlib.pyplot as plt +import pylab + +import adbutil +from devices import DEVICES + +DB_PATH="/data/data/com.android.benchmark/databases/BenchmarkResults" +OUT_PATH = "db/" + +QUERY_BAD_FRAME = ("select run_id, name, iteration, total_duration from ui_results " + "where total_duration >= 16 order by run_id, name, iteration") +QUERY_PERCENT_JANK = ("select run_id, name, iteration, sum(jank_frame) as jank_count, count (*) as total " + "from ui_results group by run_id, name, iteration") + +SKIP_TESTS = [ + # "BMUpload", + # "Low-hitrate text render", + # "High-hitrate text render", + # "Edit Text Input", + # "List View Fling" +] + +INCLUDE_TESTS = [ + #"BMUpload" + #"Shadow Grid Fling" + #"Image List View Fling" + #"Edit Text Input" +] + +class IterationResult: + def __init__(self): + self.durations = [] + self.jank_count = 0 + self.total_count = 0 + + +def get_scoremap(dbpath): + db = sqlite3.connect(dbpath) + rows = db.execute(QUERY_BAD_FRAME) + + scoremap = {} + for row in rows: + run_id = row[0] + name = row[1] + iteration = row[2] + total_duration = row[3] + + if not run_id in scoremap: + scoremap[run_id] = {} + + if not name in scoremap[run_id]: + scoremap[run_id][name] = {} + + if not iteration in scoremap[run_id][name]: + scoremap[run_id][name][iteration] = IterationResult() + + scoremap[run_id][name][iteration].durations.append(float(total_duration)) + + for row in db.execute(QUERY_PERCENT_JANK): + run_id = row[0] + name = row[1] + iteration = row[2] + jank_count = row[3] + total_count = row[4] + + if run_id in scoremap.keys() and name in scoremap[run_id].keys() and iteration in scoremap[run_id][name].keys(): + scoremap[run_id][name][iteration].jank_count = long(jank_count) + scoremap[run_id][name][iteration].total_count = long(total_count) + + db.close() + return scoremap + +def round_to_2(val): + return val + if val == 0: + return val + return round(val , -int(floor(log10(abs(val)))) + 1) + +def score_device(name, serial, pull = False, verbose = False): + dbpath = OUT_PATH + name + ".db" + + if pull: + adbutil.root(serial) + adbutil.pull(serial, DB_PATH, dbpath) + + scoremap = None + try: + scoremap = get_scoremap(dbpath) + except sqlite3.DatabaseError: + print "Database corrupt, fetching..." + adbutil.root(serial) + adbutil.pull(serial, DB_PATH, dbpath) + scoremap = get_scoremap(dbpath) + + per_test_score = {} + per_test_sample_count = {} + global_overall = {} + + for run_id in iter(scoremap): + overall = [] + if len(scoremap[run_id]) < 1: + if verbose: + print "Skipping short run %s" % run_id + continue + print "Run: %s" % run_id + for test in iter(scoremap[run_id]): + if test in SKIP_TESTS: + continue + if INCLUDE_TESTS and test not in INCLUDE_TESTS: + continue + if verbose: + print "\t%s" % test + scores = [] + means = [] + stddevs = [] + pjs = [] + sample_count = 0 + hit_min_count = 0 + # try pooling together all iterations + for iteration in iter(scoremap[run_id][test]): + res = scoremap[run_id][test][iteration] + stddev = round_to_2(numpy.std(res.durations)) + mean = round_to_2(numpy.mean(res.durations)) + sample_count += len(res.durations) + pj = round_to_2(100 * res.jank_count / float(res.total_count)) + score = stddev * mean * pj + score = 100 * len(res.durations) / float(res.total_count) + if score == 0: + score = 1 + scores.append(score) + means.append(mean) + stddevs.append(stddev) + pjs.append(pj) + if verbose: + print "\t%s: Score = %f x %f x %f = %f (%d samples)" % (iteration, stddev, mean, pj, score, len(res.durations)) + + if verbose: + print "\tHit min: %d" % hit_min_count + print "\tMean Variation: %0.2f%%" % (100 * scipy.stats.variation(means)) + print "\tStdDev Variation: %0.2f%%" % (100 * scipy.stats.variation(stddevs)) + print "\tPJ Variation: %0.2f%%" % (100 * scipy.stats.variation(pjs)) + + geo_run = numpy.mean(scores) + if test not in per_test_score: + per_test_score[test] = [] + + if test not in per_test_sample_count: + per_test_sample_count[test] = [] + + sample_count /= len(scoremap[run_id][test]) + + per_test_score[test].append(geo_run) + per_test_sample_count[test].append(int(sample_count)) + overall.append(geo_run) + + if not verbose: + print "\t%s:\t%0.2f (%0.2f avg. sample count)" % (test, geo_run, sample_count) + else: + print "\tOverall:\t%0.2f (%0.2f avg. sample count)" % (geo_run, sample_count) + print "" + + global_overall[run_id] = scipy.stats.gmean(overall) + print "Run Overall: %f" % global_overall[run_id] + print "" + + print "" + print "Variability (CV) - %s:" % name + + worst_offender_test = None + worst_offender_variation = 0 + for test in per_test_score: + variation = 100 * scipy.stats.variation(per_test_score[test]) + if worst_offender_variation < variation: + worst_offender_test = test + worst_offender_variation = variation + print "\t%s:\t%0.2f%% (%0.2f avg sample count)" % (test, variation, numpy.mean(per_test_sample_count[test])) + + print "\tOverall: %0.2f%%" % (100 * scipy.stats.variation([x for x in global_overall.values()])) + print "" + + return { + "overall": global_overall.values(), + "worst_offender_test": (name, worst_offender_test, worst_offender_variation) + } + +def parse_options(argv): + usage = 'Usage: %prog [options]' + desc = 'Example: %prog' + parser = optparse.OptionParser(usage=usage, description=desc) + parser.add_option("-p", dest='pull', action="store_true") + parser.add_option("-d", dest='device', action="store") + parser.add_option("-v", dest='verbose', action="store_true") + options, categories = parser.parse_args(argv[1:]) + return options + +def main(): + options = parse_options(sys.argv) + if options.device != None: + score_device(options.device, DEVICES[options.device], options.pull, options.verbose) + else: + device_scores = [] + worst_offenders = [] + for name, serial in DEVICES.iteritems(): + print "======== %s =========" % name + result = score_device(name, serial, options.pull, options.verbose) + device_scores.append((name, result["overall"])) + worst_offenders.append(result["worst_offender_test"]) + + + device_scores.sort(cmp=(lambda x, y: cmp(x[1], y[1]))) + print "Ranking by max overall score:" + for name, score in device_scores: + plt.plot([0, 1, 2, 3, 4, 5], score, label=name) + print "\t%s: %s" % (name, score) + + plt.ylabel("Jank %") + plt.xlabel("Iteration") + plt.title("Jank Percentage") + plt.legend() + pylab.savefig("holy.png", bbox_inches="tight") + + print "Worst offender tests:" + for device, test, variation in worst_offenders: + print "\t%s: %s %.2f%%" % (device, test, variation) + +if __name__ == "__main__": + main() + diff --git a/tests/JankBench/scripts/devices.py b/tests/JankBench/scripts/devices.py new file mode 100644 index 000000000000..c8266c0ecef5 --- /dev/null +++ b/tests/JankBench/scripts/devices.py @@ -0,0 +1,11 @@ +#!/usr/bin/python + +DEVICES = { + 'bullhead': '00606a370e3ca155', + 'volantis': 'HT4BTWV00612', + 'angler': '84B5T15A29021748', + 'seed': '1285c85e', + 'ryu': '5A27000599', + 'shamu': 'ZX1G22W24R', +} + diff --git a/tests/JankBench/scripts/external/__init__.py b/tests/JankBench/scripts/external/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tests/JankBench/scripts/external/__init__.py diff --git a/tests/JankBench/scripts/external/statistics.py b/tests/JankBench/scripts/external/statistics.py new file mode 100644 index 000000000000..518f54654469 --- /dev/null +++ b/tests/JankBench/scripts/external/statistics.py @@ -0,0 +1,638 @@ +## Module statistics.py +## +## Copyright (c) 2013 Steven D'Aprano <steve+python@pearwood.info>. +## +## 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. + + +""" +Basic statistics module. + +This module provides functions for calculating statistics of data, including +averages, variance, and standard deviation. + +Calculating averages +-------------------- + +================== ============================================= +Function Description +================== ============================================= +mean Arithmetic mean (average) of data. +median Median (middle value) of data. +median_low Low median of data. +median_high High median of data. +median_grouped Median, or 50th percentile, of grouped data. +mode Mode (most common value) of data. +================== ============================================= + +Calculate the arithmetic mean ("the average") of data: + +>>> mean([-1.0, 2.5, 3.25, 5.75]) +2.625 + + +Calculate the standard median of discrete data: + +>>> median([2, 3, 4, 5]) +3.5 + + +Calculate the median, or 50th percentile, of data grouped into class intervals +centred on the data values provided. E.g. if your data points are rounded to +the nearest whole number: + +>>> median_grouped([2, 2, 3, 3, 3, 4]) #doctest: +ELLIPSIS +2.8333333333... + +This should be interpreted in this way: you have two data points in the class +interval 1.5-2.5, three data points in the class interval 2.5-3.5, and one in +the class interval 3.5-4.5. The median of these data points is 2.8333... + + +Calculating variability or spread +--------------------------------- + +================== ============================================= +Function Description +================== ============================================= +pvariance Population variance of data. +variance Sample variance of data. +pstdev Population standard deviation of data. +stdev Sample standard deviation of data. +================== ============================================= + +Calculate the standard deviation of sample data: + +>>> stdev([2.5, 3.25, 5.5, 11.25, 11.75]) #doctest: +ELLIPSIS +4.38961843444... + +If you have previously calculated the mean, you can pass it as the optional +second argument to the four "spread" functions to avoid recalculating it: + +>>> data = [1, 2, 2, 4, 4, 4, 5, 6] +>>> mu = mean(data) +>>> pvariance(data, mu) +2.5 + + +Exceptions +---------- + +A single exception is defined: StatisticsError is a subclass of ValueError. + +""" + +__all__ = [ 'StatisticsError', + 'pstdev', 'pvariance', 'stdev', 'variance', + 'median', 'median_low', 'median_high', 'median_grouped', + 'mean', 'mode', + ] + + +import collections +import math + +from fractions import Fraction +from decimal import Decimal +from itertools import groupby + + + +# === Exceptions === + +class StatisticsError(ValueError): + pass + + +# === Private utilities === + +def _sum(data, start=0): + """_sum(data [, start]) -> (type, sum, count) + + Return a high-precision sum of the given numeric data as a fraction, + together with the type to be converted to and the count of items. + + If optional argument ``start`` is given, it is added to the total. + If ``data`` is empty, ``start`` (defaulting to 0) is returned. + + + Examples + -------- + + >>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75) + (<class 'float'>, Fraction(11, 1), 5) + + Some sources of round-off error will be avoided: + + >>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero. + (<class 'float'>, Fraction(1000, 1), 3000) + + Fractions and Decimals are also supported: + + >>> from fractions import Fraction as F + >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) + (<class 'fractions.Fraction'>, Fraction(63, 20), 4) + + >>> from decimal import Decimal as D + >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] + >>> _sum(data) + (<class 'decimal.Decimal'>, Fraction(6963, 10000), 4) + + Mixed types are currently treated as an error, except that int is + allowed. + """ + count = 0 + n, d = _exact_ratio(start) + partials = {d: n} + partials_get = partials.get + T = _coerce(int, type(start)) + for typ, values in groupby(data, type): + T = _coerce(T, typ) # or raise TypeError + for n,d in map(_exact_ratio, values): + count += 1 + partials[d] = partials_get(d, 0) + n + if None in partials: + # The sum will be a NAN or INF. We can ignore all the finite + # partials, and just look at this special one. + total = partials[None] + assert not _isfinite(total) + else: + # Sum all the partial sums using builtin sum. + # FIXME is this faster if we sum them in order of the denominator? + total = sum(Fraction(n, d) for d, n in sorted(partials.items())) + return (T, total, count) + + +def _isfinite(x): + try: + return x.is_finite() # Likely a Decimal. + except AttributeError: + return math.isfinite(x) # Coerces to float first. + + +def _coerce(T, S): + """Coerce types T and S to a common type, or raise TypeError. + + Coercion rules are currently an implementation detail. See the CoerceTest + test class in test_statistics for details. + """ + # See http://bugs.python.org/issue24068. + assert T is not bool, "initial type T is bool" + # If the types are the same, no need to coerce anything. Put this + # first, so that the usual case (no coercion needed) happens as soon + # as possible. + if T is S: return T + # Mixed int & other coerce to the other type. + if S is int or S is bool: return T + if T is int: return S + # If one is a (strict) subclass of the other, coerce to the subclass. + if issubclass(S, T): return S + if issubclass(T, S): return T + # Ints coerce to the other type. + if issubclass(T, int): return S + if issubclass(S, int): return T + # Mixed fraction & float coerces to float (or float subclass). + if issubclass(T, Fraction) and issubclass(S, float): + return S + if issubclass(T, float) and issubclass(S, Fraction): + return T + # Any other combination is disallowed. + msg = "don't know how to coerce %s and %s" + raise TypeError(msg % (T.__name__, S.__name__)) + + +def _exact_ratio(x): + """Return Real number x to exact (numerator, denominator) pair. + + >>> _exact_ratio(0.25) + (1, 4) + + x is expected to be an int, Fraction, Decimal or float. + """ + try: + # Optimise the common case of floats. We expect that the most often + # used numeric type will be builtin floats, so try to make this as + # fast as possible. + if type(x) is float: + return x.as_integer_ratio() + try: + # x may be an int, Fraction, or Integral ABC. + return (x.numerator, x.denominator) + except AttributeError: + try: + # x may be a float subclass. + return x.as_integer_ratio() + except AttributeError: + try: + # x may be a Decimal. + return _decimal_to_ratio(x) + except AttributeError: + # Just give up? + pass + except (OverflowError, ValueError): + # float NAN or INF. + assert not math.isfinite(x) + return (x, None) + msg = "can't convert type '{}' to numerator/denominator" + raise TypeError(msg.format(type(x).__name__)) + + +# FIXME This is faster than Fraction.from_decimal, but still too slow. +def _decimal_to_ratio(d): + """Convert Decimal d to exact integer ratio (numerator, denominator). + + >>> from decimal import Decimal + >>> _decimal_to_ratio(Decimal("2.6")) + (26, 10) + + """ + sign, digits, exp = d.as_tuple() + if exp in ('F', 'n', 'N'): # INF, NAN, sNAN + assert not d.is_finite() + return (d, None) + num = 0 + for digit in digits: + num = num*10 + digit + if exp < 0: + den = 10**-exp + else: + num *= 10**exp + den = 1 + if sign: + num = -num + return (num, den) + + +def _convert(value, T): + """Convert value to given numeric type T.""" + if type(value) is T: + # This covers the cases where T is Fraction, or where value is + # a NAN or INF (Decimal or float). + return value + if issubclass(T, int) and value.denominator != 1: + T = float + try: + # FIXME: what do we do if this overflows? + return T(value) + except TypeError: + if issubclass(T, Decimal): + return T(value.numerator)/T(value.denominator) + else: + raise + + +def _counts(data): + # Generate a table of sorted (value, frequency) pairs. + table = collections.Counter(iter(data)).most_common() + if not table: + return table + # Extract the values with the highest frequency. + maxfreq = table[0][1] + for i in range(1, len(table)): + if table[i][1] != maxfreq: + table = table[:i] + break + return table + + +# === Measures of central tendency (averages) === + +def mean(data): + """Return the sample arithmetic mean of data. + + >>> mean([1, 2, 3, 4, 4]) + 2.8 + + >>> from fractions import Fraction as F + >>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)]) + Fraction(13, 21) + + >>> from decimal import Decimal as D + >>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")]) + Decimal('0.5625') + + If ``data`` is empty, StatisticsError will be raised. + """ + if iter(data) is data: + data = list(data) + n = len(data) + if n < 1: + raise StatisticsError('mean requires at least one data point') + T, total, count = _sum(data) + assert count == n + return _convert(total/n, T) + + +# FIXME: investigate ways to calculate medians without sorting? Quickselect? +def median(data): + """Return the median (middle value) of numeric data. + + When the number of data points is odd, return the middle data point. + When the number of data points is even, the median is interpolated by + taking the average of the two middle values: + + >>> median([1, 3, 5]) + 3 + >>> median([1, 3, 5, 7]) + 4.0 + + """ + data = sorted(data) + n = len(data) + if n == 0: + raise StatisticsError("no median for empty data") + if n%2 == 1: + return data[n//2] + else: + i = n//2 + return (data[i - 1] + data[i])/2 + + +def median_low(data): + """Return the low median of numeric data. + + When the number of data points is odd, the middle value is returned. + When it is even, the smaller of the two middle values is returned. + + >>> median_low([1, 3, 5]) + 3 + >>> median_low([1, 3, 5, 7]) + 3 + + """ + data = sorted(data) + n = len(data) + if n == 0: + raise StatisticsError("no median for empty data") + if n%2 == 1: + return data[n//2] + else: + return data[n//2 - 1] + + +def median_high(data): + """Return the high median of data. + + When the number of data points is odd, the middle value is returned. + When it is even, the larger of the two middle values is returned. + + >>> median_high([1, 3, 5]) + 3 + >>> median_high([1, 3, 5, 7]) + 5 + + """ + data = sorted(data) + n = len(data) + if n == 0: + raise StatisticsError("no median for empty data") + return data[n//2] + + +def median_grouped(data, interval=1): + """Return the 50th percentile (median) of grouped continuous data. + + >>> median_grouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5]) + 3.7 + >>> median_grouped([52, 52, 53, 54]) + 52.5 + + This calculates the median as the 50th percentile, and should be + used when your data is continuous and grouped. In the above example, + the values 1, 2, 3, etc. actually represent the midpoint of classes + 0.5-1.5, 1.5-2.5, 2.5-3.5, etc. The middle value falls somewhere in + class 3.5-4.5, and interpolation is used to estimate it. + + Optional argument ``interval`` represents the class interval, and + defaults to 1. Changing the class interval naturally will change the + interpolated 50th percentile value: + + >>> median_grouped([1, 3, 3, 5, 7], interval=1) + 3.25 + >>> median_grouped([1, 3, 3, 5, 7], interval=2) + 3.5 + + This function does not check whether the data points are at least + ``interval`` apart. + """ + data = sorted(data) + n = len(data) + if n == 0: + raise StatisticsError("no median for empty data") + elif n == 1: + return data[0] + # Find the value at the midpoint. Remember this corresponds to the + # centre of the class interval. + x = data[n//2] + for obj in (x, interval): + if isinstance(obj, (str, bytes)): + raise TypeError('expected number but got %r' % obj) + try: + L = x - interval/2 # The lower limit of the median interval. + except TypeError: + # Mixed type. For now we just coerce to float. + L = float(x) - float(interval)/2 + cf = data.index(x) # Number of values below the median interval. + # FIXME The following line could be more efficient for big lists. + f = data.count(x) # Number of data points in the median interval. + return L + interval*(n/2 - cf)/f + + +def mode(data): + """Return the most common data point from discrete or nominal data. + + ``mode`` assumes discrete data, and returns a single value. This is the + standard treatment of the mode as commonly taught in schools: + + >>> mode([1, 1, 2, 3, 3, 3, 3, 4]) + 3 + + This also works with nominal (non-numeric) data: + + >>> mode(["red", "blue", "blue", "red", "green", "red", "red"]) + 'red' + + If there is not exactly one most common value, ``mode`` will raise + StatisticsError. + """ + # Generate a table of sorted (value, frequency) pairs. + table = _counts(data) + if len(table) == 1: + return table[0][0] + elif table: + raise StatisticsError( + 'no unique mode; found %d equally common values' % len(table) + ) + else: + raise StatisticsError('no mode for empty data') + + +# === Measures of spread === + +# See http://mathworld.wolfram.com/Variance.html +# http://mathworld.wolfram.com/SampleVariance.html +# http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance +# +# Under no circumstances use the so-called "computational formula for +# variance", as that is only suitable for hand calculations with a small +# amount of low-precision data. It has terrible numeric properties. +# +# See a comparison of three computational methods here: +# http://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/ + +def _ss(data, c=None): + """Return sum of square deviations of sequence data. + + If ``c`` is None, the mean is calculated in one pass, and the deviations + from the mean are calculated in a second pass. Otherwise, deviations are + calculated from ``c`` as given. Use the second case with care, as it can + lead to garbage results. + """ + if c is None: + c = mean(data) + T, total, count = _sum((x-c)**2 for x in data) + # The following sum should mathematically equal zero, but due to rounding + # error may not. + U, total2, count2 = _sum((x-c) for x in data) + assert T == U and count == count2 + total -= total2**2/len(data) + assert not total < 0, 'negative sum of square deviations: %f' % total + return (T, total) + + +def variance(data, xbar=None): + """Return the sample variance of data. + + data should be an iterable of Real-valued numbers, with at least two + values. The optional argument xbar, if given, should be the mean of + the data. If it is missing or None, the mean is automatically calculated. + + Use this function when your data is a sample from a population. To + calculate the variance from the entire population, see ``pvariance``. + + Examples: + + >>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5] + >>> variance(data) + 1.3720238095238095 + + If you have already calculated the mean of your data, you can pass it as + the optional second argument ``xbar`` to avoid recalculating it: + + >>> m = mean(data) + >>> variance(data, m) + 1.3720238095238095 + + This function does not check that ``xbar`` is actually the mean of + ``data``. Giving arbitrary values for ``xbar`` may lead to invalid or + impossible results. + + Decimals and Fractions are supported: + + >>> from decimal import Decimal as D + >>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")]) + Decimal('31.01875') + + >>> from fractions import Fraction as F + >>> variance([F(1, 6), F(1, 2), F(5, 3)]) + Fraction(67, 108) + + """ + if iter(data) is data: + data = list(data) + n = len(data) + if n < 2: + raise StatisticsError('variance requires at least two data points') + T, ss = _ss(data, xbar) + return _convert(ss/(n-1), T) + + +def pvariance(data, mu=None): + """Return the population variance of ``data``. + + data should be an iterable of Real-valued numbers, with at least one + value. The optional argument mu, if given, should be the mean of + the data. If it is missing or None, the mean is automatically calculated. + + Use this function to calculate the variance from the entire population. + To estimate the variance from a sample, the ``variance`` function is + usually a better choice. + + Examples: + + >>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25] + >>> pvariance(data) + 1.25 + + If you have already calculated the mean of the data, you can pass it as + the optional second argument to avoid recalculating it: + + >>> mu = mean(data) + >>> pvariance(data, mu) + 1.25 + + This function does not check that ``mu`` is actually the mean of ``data``. + Giving arbitrary values for ``mu`` may lead to invalid or impossible + results. + + Decimals and Fractions are supported: + + >>> from decimal import Decimal as D + >>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")]) + Decimal('24.815') + + >>> from fractions import Fraction as F + >>> pvariance([F(1, 4), F(5, 4), F(1, 2)]) + Fraction(13, 72) + + """ + if iter(data) is data: + data = list(data) + n = len(data) + if n < 1: + raise StatisticsError('pvariance requires at least one data point') + ss = _ss(data, mu) + T, ss = _ss(data, mu) + return _convert(ss/n, T) + + +def stdev(data, xbar=None): + """Return the square root of the sample variance. + + See ``variance`` for arguments and other details. + + >>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]) + 1.0810874155219827 + + """ + var = variance(data, xbar) + try: + return var.sqrt() + except AttributeError: + return math.sqrt(var) + + +def pstdev(data, mu=None): + """Return the square root of the population variance. + + See ``pvariance`` for arguments and other details. + + >>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75]) + 0.986893273527251 + + """ + var = pvariance(data, mu) + try: + return var.sqrt() + except AttributeError: + return math.sqrt(var) diff --git a/tests/JankBench/scripts/itr_collect.py b/tests/JankBench/scripts/itr_collect.py new file mode 100755 index 000000000000..76499a4c9362 --- /dev/null +++ b/tests/JankBench/scripts/itr_collect.py @@ -0,0 +1,154 @@ +#!/usr/bin/python + +import optparse +import sys +import sqlite3 +import scipy.stats +import numpy + +import adbutil +from devices import DEVICES + +DB_PATH="/data/data/com.android.benchmark/databases/BenchmarkResults" +OUT_PATH = "db/" + +QUERY_BAD_FRAME = ("select run_id, name, total_duration from ui_results " + "where total_duration >=12 order by run_id, name") +QUERY_PERCENT_JANK = ("select run_id, name, sum(jank_frame) as jank_count, count (*) as total " + "from ui_results group by run_id, name") + +class IterationResult: + def __init__(self): + self.durations = [] + self.jank_count = 0 + self.total_count = 0 + + +def get_scoremap(dbpath): + db = sqlite3.connect(dbpath) + rows = db.execute(QUERY_BAD_FRAME) + + scoremap = {} + for row in rows: + run_id = row[0] + name = row[1] + total_duration = row[2] + + if not run_id in scoremap: + scoremap[run_id] = {} + + if not name in scoremap[run_id]: + scoremap[run_id][name] = IterationResult() + + + scoremap[run_id][name].durations.append(float(total_duration)) + + for row in db.execute(QUERY_PERCENT_JANK): + run_id = row[0] + name = row[1] + jank_count = row[2] + total_count = row[3] + + if run_id in scoremap.keys() and name in scoremap[run_id].keys(): + scoremap[run_id][name].jank_count = long(jank_count) + scoremap[run_id][name].total_count = long(total_count) + + + db.close() + return scoremap + +def score_device(name, serial, pull = False, verbose = False): + dbpath = OUT_PATH + name + ".db" + + if pull: + adbutil.root(serial) + adbutil.pull(serial, DB_PATH, dbpath) + + scoremap = None + try: + scoremap = get_scoremap(dbpath) + except sqlite3.DatabaseError: + print "Database corrupt, fetching..." + adbutil.root(serial) + adbutil.pull(serial, DB_PATH, dbpath) + scoremap = get_scoremap(dbpath) + + per_test_score = {} + per_test_sample_count = {} + global_overall = {} + + for run_id in iter(scoremap): + overall = [] + if len(scoremap[run_id]) < 1: + if verbose: + print "Skipping short run %s" % run_id + continue + print "Run: %s" % run_id + for test in iter(scoremap[run_id]): + if verbose: + print "\t%s" % test + scores = [] + sample_count = 0 + res = scoremap[run_id][test] + stddev = numpy.std(res.durations) + mean = numpy.mean(res.durations) + sample_count = len(res.durations) + pj = 100 * res.jank_count / float(res.total_count) + score = stddev * mean *pj + if score == 0: + score = 1 + scores.append(score) + if verbose: + print "\tScore = %f x %f x %f = %f (%d samples)" % (stddev, mean, pj, score, len(res.durations)) + + geo_run = scipy.stats.gmean(scores) + if test not in per_test_score: + per_test_score[test] = [] + + if test not in per_test_sample_count: + per_test_sample_count[test] = [] + + per_test_score[test].append(geo_run) + per_test_sample_count[test].append(int(sample_count)) + overall.append(geo_run) + + if not verbose: + print "\t%s:\t%0.2f (%0.2f avg. sample count)" % (test, geo_run, sample_count) + else: + print "\tOverall:\t%0.2f (%0.2f avg. sample count)" % (geo_run, sample_count) + print "" + + global_overall[run_id] = scipy.stats.gmean(overall) + print "Run Overall: %f" % global_overall[run_id] + print "" + + print "" + print "Variability (CV) - %s:" % name + + for test in per_test_score: + print "\t%s:\t%0.2f%% (%0.2f avg sample count)" % (test, 100 * scipy.stats.variation(per_test_score[test]), numpy.mean(per_test_sample_count[test])) + + print "\tOverall: %0.2f%%" % (100 * scipy.stats.variation([x for x in global_overall.values()])) + print "" + +def parse_options(argv): + usage = 'Usage: %prog [options]' + desc = 'Example: %prog' + parser = optparse.OptionParser(usage=usage, description=desc) + parser.add_option("-p", dest='pull', action="store_true") + parser.add_option("-d", dest='device', action="store") + parser.add_option("-v", dest='verbose', action="store_true") + options, categories = parser.parse_args(argv[1:]) + return options + +def main(): + options = parse_options(sys.argv) + if options.device != None: + score_device(options.device, DEVICES[options.device], options.pull, options.verbose) + else: + for name, serial in DEVICES.iteritems(): + print "======== %s =========" % name + score_device(name, serial, options.pull, options.verbose) + +if __name__ == "__main__": + main() diff --git a/tests/JankBench/scripts/runall.py b/tests/JankBench/scripts/runall.py new file mode 100755 index 000000000000..d9a06628f2b0 --- /dev/null +++ b/tests/JankBench/scripts/runall.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +import optparse +import sys +import time + +import adbutil +from devices import DEVICES + +def parse_options(argv): + usage = 'Usage: %prog [options]' + desc = 'Example: %prog' + parser = optparse.OptionParser(usage=usage, description=desc) + parser.add_option("-c", dest='clear', action="store_true") + parser.add_option("-d", dest='device', action="store",) + parser.add_option("-t", dest='trace', action="store_true") + options, categories = parser.parse_args(argv[1:]) + return (options, categories) + +def clear_data(device = None): + if device != None: + dev = DEVICES[device] + adbutil.root(dev) + adbutil.pm(dev, "clear", "com.android.benchmark") + else: + for name, dev in DEVICES.iteritems(): + print("Clearing " + name) + adbutil.root(dev) + adbutil.pm(dev, "clear", "com.android.benchmark") + +def start_device(name, dev): + print("Go " + name + "!") + try: + adbutil.am(dev, "force-stop", "com.android.benchmark") + adbutil.wake(dev) + adbutil.am(dev, "start", + ["-n", "\"com.android.benchmark/.app.RunLocalBenchmarksActivity\"", + "--eia", "\"com.android.benchmark.EXTRA_ENABLED_BENCHMARK_IDS\"", "\"0,1,2,3,4,5,6\"", + "--ei", "\"com.android.benchmark.EXTRA_RUN_COUNT\"", "\"5\""]) + except adbutil.AdbError: + print "Couldn't launch " + name + "." + +def start_benchmark(device, trace): + if device != None: + start_device(device, DEVICES[device]) + if trace: + time.sleep(3) + adbutil.trace(DEVICES[device]) + else: + if trace: + print("Note: -t only valid with -d, can't trace") + for name, dev in DEVICES.iteritems(): + start_device(name, dev) + +def main(): + options, categories = parse_options(sys.argv) + if options.clear: + print options.device + clear_data(options.device) + else: + start_benchmark(options.device, options.trace) + + +if __name__ == "__main__": + main() diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk index 578e628987f1..e186e9fb03c9 100644 --- a/tests/MemoryUsage/Android.mk +++ b/tests/MemoryUsage/Android.mk @@ -9,8 +9,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := MemoryUsage LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit include $(BUILD_PACKAGE) diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk index dd9ff11971eb..6fb60259a2fc 100644 --- a/tests/NetworkSecurityConfigTest/Android.mk +++ b/tests/NetworkSecurityConfigTest/Android.mk @@ -5,8 +5,13 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + bouncycastle \ + conscrypt \ + android.test.base \ + +LOCAL_STATIC_JAVA_LIBRARIES := junit # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java index 25bfa53b0cf2..047be162e642 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java @@ -17,6 +17,7 @@ package android.security.net.config; import android.app.Activity; +import android.content.pm.ApplicationInfo; import android.os.Build; import android.test.ActivityUnitTestCase; import android.util.ArraySet; @@ -227,7 +228,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { public void testConfigBuilderUsesParents() throws Exception { // Check that a builder with a parent uses the parent's values when non is set. NetworkSecurityConfig config = new NetworkSecurityConfig.Builder() - .setParent(NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.N, 1)) + .setParent(NetworkSecurityConfig + .getDefaultBuilder(TestUtils.makeApplicationInfo())) .build(); assert(!config.getTrustAnchors().isEmpty()); } @@ -268,11 +270,22 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { // Install the test CA. store.installCertificate(TEST_CA_CERT); NetworkSecurityConfig preNConfig = - NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.M, 1).build(); + NetworkSecurityConfig + .getDefaultBuilder(TestUtils.makeApplicationInfo(Build.VERSION_CODES.M)) + .build(); NetworkSecurityConfig nConfig = - NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.N, 1).build(); + NetworkSecurityConfig + .getDefaultBuilder(TestUtils.makeApplicationInfo(Build.VERSION_CODES.N)) + .build(); + ApplicationInfo privInfo = TestUtils.makeApplicationInfo(Build.VERSION_CODES.M); + privInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + NetworkSecurityConfig privConfig = + NetworkSecurityConfig + .getDefaultBuilder(privInfo) + .build(); Set<TrustAnchor> preNAnchors = preNConfig.getTrustAnchors(); Set<TrustAnchor> nAnchors = nConfig.getTrustAnchors(); + Set<TrustAnchor> privAnchors = privConfig.getTrustAnchors(); Set<X509Certificate> preNCerts = new HashSet<X509Certificate>(); for (TrustAnchor anchor : preNAnchors) { preNCerts.add(anchor.certificate); @@ -281,8 +294,13 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { for (TrustAnchor anchor : nAnchors) { nCerts.add(anchor.certificate); } + Set<X509Certificate> privCerts = new HashSet<X509Certificate>(); + for (TrustAnchor anchor : privAnchors) { + privCerts.add(anchor.certificate); + } assertTrue(preNCerts.contains(TEST_CA_CERT)); assertFalse(nCerts.contains(TEST_CA_CERT)); + assertFalse(privCerts.contains(TEST_CA_CERT)); } finally { // Delete the user added CA. We don't know the alias so just delete them all. for (String alias : store.aliases()) { diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java index f7590fd6ff12..9dec21be7f37 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java @@ -16,6 +16,8 @@ package android.security.net.config; +import android.content.pm.ApplicationInfo; +import android.os.Build; import java.net.Socket; import java.net.URL; import javax.net.ssl.HttpsURLConnection; @@ -77,4 +79,17 @@ public final class TestUtils extends Assert { context.init(null, tmf.getTrustManagers(), null); return context; } + + public static ApplicationInfo makeApplicationInfo() { + ApplicationInfo info = new ApplicationInfo(); + info.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + info.targetSandboxVersion = 1; + return info; + } + + public static ApplicationInfo makeApplicationInfo(int targetSdkVersion) { + ApplicationInfo info = makeApplicationInfo(); + info.targetSdkVersion = targetSdkVersion; + return info; + } } diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java index f7066a6f45f6..4b7a014f25dc 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java @@ -17,6 +17,7 @@ package android.security.net.config; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.test.AndroidTestCase; import android.test.MoreAsserts; import android.util.ArraySet; @@ -44,7 +45,8 @@ public class XmlConfigTests extends AndroidTestCase { private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA"; public void testEmptyConfigFile() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertFalse(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); @@ -63,7 +65,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testEmptyAnchors() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertFalse(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); @@ -81,7 +84,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testBasicDomainConfig() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); @@ -117,7 +121,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testBasicPinning() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Check android.com. @@ -132,7 +137,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testExpiredPin() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Check android.com. @@ -146,7 +152,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testOverridesPins() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Check android.com. @@ -160,7 +167,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testBadPin() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Check android.com. @@ -175,7 +183,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testMultipleDomains() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); @@ -196,7 +205,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testMultipleDomainConfigs() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Should be two different config objects @@ -211,7 +221,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testIncludeSubdomains() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Try connections. @@ -224,7 +235,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testAttributes() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertFalse(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); @@ -233,7 +245,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testResourcePemCertificateSource() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); // Check android.com. NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); @@ -249,7 +262,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testResourceDerCertificateSource() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); // Check android.com. NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); @@ -265,7 +279,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testNestedDomainConfigs() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); @@ -283,7 +298,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testNestedDomainConfigsOverride() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); @@ -294,7 +310,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testDebugOverridesDisabled() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, false); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); Set<TrustAnchor> anchors = config.getTrustAnchors(); @@ -305,7 +322,9 @@ public class XmlConfigTests extends AndroidTestCase { } public void testBasicDebugOverrides() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, true); + ApplicationInfo info = TestUtils.makeApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, info); ApplicationConfig appConfig = new ApplicationConfig(source); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); Set<TrustAnchor> anchors = config.getTrustAnchors(); @@ -319,7 +338,9 @@ public class XmlConfigTests extends AndroidTestCase { } public void testDebugOverridesWithDomain() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true); + ApplicationInfo info = TestUtils.makeApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, info); ApplicationConfig appConfig = new ApplicationConfig(source); NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); Set<TrustAnchor> anchors = config.getTrustAnchors(); @@ -337,7 +358,9 @@ public class XmlConfigTests extends AndroidTestCase { } public void testDebugInherit() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true); + ApplicationInfo info = TestUtils.makeApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, info); ApplicationConfig appConfig = new ApplicationConfig(source); NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); Set<TrustAnchor> anchors = config.getTrustAnchors(); @@ -357,7 +380,8 @@ public class XmlConfigTests extends AndroidTestCase { private void testBadConfig(int configId) throws Exception { try { - XmlConfigSource source = new XmlConfigSource(getContext(), configId); + XmlConfigSource source = new XmlConfigSource(getContext(), configId, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); appConfig.getConfigForHostname("android.com"); fail("Bad config " + getContext().getResources().getResourceName(configId) @@ -393,7 +417,8 @@ public class XmlConfigTests extends AndroidTestCase { } public void testTrustManagerKeystore() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, true); + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); Provider provider = new NetworkSecurityConfigProvider(); TrustManagerFactory tmf = @@ -415,7 +440,9 @@ public class XmlConfigTests extends AndroidTestCase { } public void testDebugDedup() throws Exception { - XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_dedup, true); + ApplicationInfo info = TestUtils.makeApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_dedup, info); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); // Check android.com. @@ -433,15 +460,18 @@ public class XmlConfigTests extends AndroidTestCase { } public void testExtraDebugResource() throws Exception { + ApplicationInfo info = TestUtils.makeApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_DEBUGGABLE; XmlConfigSource source = - new XmlConfigSource(getContext(), R.xml.extra_debug_resource, true); + new XmlConfigSource(getContext(), R.xml.extra_debug_resource, info); ApplicationConfig appConfig = new ApplicationConfig(source); assertFalse(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig config = appConfig.getConfigForHostname(""); MoreAsserts.assertNotEmpty(config.getTrustAnchors()); // Check that the _debug file is ignored if debug is false. - source = new XmlConfigSource(getContext(), R.xml.extra_debug_resource, false); + source = new XmlConfigSource(getContext(), R.xml.extra_debug_resource, + TestUtils.makeApplicationInfo()); appConfig = new ApplicationConfig(source); assertFalse(appConfig.hasPerDomainConfigs()); config = appConfig.getConfigForHostname(""); @@ -451,12 +481,15 @@ public class XmlConfigTests extends AndroidTestCase { public void testExtraDebugResourceIgnored() throws Exception { // Verify that parsing the extra debug config resource fails only when debugging is true. XmlConfigSource source = - new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, false); + new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); // Force parsing the config file. appConfig.getConfigForHostname(""); - source = new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, true); + ApplicationInfo info = TestUtils.makeApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + source = new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, info); appConfig = new ApplicationConfig(source); try { appConfig.getConfigForHostname(""); @@ -467,7 +500,8 @@ public class XmlConfigTests extends AndroidTestCase { public void testDomainWhitespaceTrimming() throws Exception { XmlConfigSource source = - new XmlConfigSource(getContext(), R.xml.domain_whitespace, false); + new XmlConfigSource(getContext(), R.xml.domain_whitespace, + TestUtils.makeApplicationInfo()); ApplicationConfig appConfig = new ApplicationConfig(source); NetworkSecurityConfig defaultConfig = appConfig.getConfigForHostname(""); MoreAsserts.assertNotEqual(defaultConfig, appConfig.getConfigForHostname("developer.android.com")); diff --git a/tests/ServiceCrashTest/Android.mk b/tests/ServiceCrashTest/Android.mk index d1f845623be8..f7b34523c086 100644 --- a/tests/ServiceCrashTest/Android.mk +++ b/tests/ServiceCrashTest/Android.mk @@ -9,7 +9,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := ServiceCrashTest LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.base LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk index 359484ee63d7..030d5f4738dc 100644 --- a/tests/SoundTriggerTests/Android.mk +++ b/tests/SoundTriggerTests/Android.mk @@ -27,8 +27,8 @@ else LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java endif -LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := mockito-target +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := SoundTriggerTests diff --git a/tests/SurfaceComposition/Android.mk b/tests/SurfaceComposition/Android.mk index d97c3f4cd66f..f59458d48c68 100644 --- a/tests/SurfaceComposition/Android.mk +++ b/tests/SurfaceComposition/Android.mk @@ -27,7 +27,9 @@ LOCAL_PROGUARD_ENABLED := disabled LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit +LOCAL_STATIC_JAVA_LIBRARIES := junit + +LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs LOCAL_PACKAGE_NAME := SurfaceComposition diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk index 3c3cd77813f7..2fa19508c32c 100644 --- a/tests/TtsTests/Android.mk +++ b/tests/TtsTests/Android.mk @@ -20,8 +20,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := mockito-target +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := TtsTests diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk index e6af4b024700..c8e6c2091d8f 100644 --- a/tests/UiBench/Android.mk +++ b/tests/UiBench/Android.mk @@ -10,24 +10,12 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src) # use appcompat/support lib from the tree, so improvements/ # regressions are reflected in test data -LOCAL_RESOURCE_DIR := \ - $(LOCAL_PATH)/res \ - frameworks/support/design/res \ - frameworks/support/v7/appcompat/res \ - frameworks/support/v7/cardview/res \ - frameworks/support/v7/recyclerview/res \ - frameworks/support/v17/leanback/res - -LOCAL_AAPT_FLAGS := \ - --auto-add-overlay \ - --extra-packages android.support.design \ - --extra-packages android.support.v7.appcompat \ - --extra-packages android.support.v7.cardview \ - --extra-packages android.support.v7.recyclerview \ - --extra-packages android.support.v17.leanback - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-design \ +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_USE_AAPT2 := true + +LOCAL_STATIC_ANDROID_LIBRARIES := \ + $(ANDROID_SUPPORT_DESIGN_TARGETS) \ android-support-v4 \ android-support-v7-appcompat \ android-support-v7-cardview \ diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml index 2521dc9f82ae..c6b4a54f3b0b 100644 --- a/tests/UiBench/AndroidManifest.xml +++ b/tests/UiBench/AndroidManifest.xml @@ -92,6 +92,15 @@ </intent-filter> </activity> <activity + android:name=".TrivialAnimationActivityWideGamut" + android:label="General/Trivial Animation (Wide Gamut)" + android:colorMode="wideColorGamut"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity> + <activity android:name=".TrivialListActivity" android:label="General/Trivial ListView" > <intent-filter> @@ -201,6 +210,36 @@ </intent-filter> </activity> + <activity-alias + android:name=".InflatingEmojiListActivity" + android:label="Inflation/Inflating ListView with Emoji" + android:targetActivity=".InflatingListActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity-alias> + + <activity-alias + android:name=".InflatingHanListActivity" + android:label="Inflation/Inflating ListView with Han Characters" + android:targetActivity=".InflatingListActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity-alias> + + <activity-alias + android:name=".InflatingLongStringListActivity" + android:label="Inflation/Inflating ListView with long string" + android:targetActivity=".InflatingListActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity-alias> + <!-- Text --> <activity android:name=".EditTextTypeActivity" diff --git a/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java b/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java index 603244eb2a43..2b84f2c49e67 100644 --- a/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/InflatingListActivity.java @@ -15,6 +15,7 @@ */ package com.android.test.uibench; +import android.content.ComponentName; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -23,10 +24,33 @@ import android.widget.ListAdapter; import com.android.test.uibench.listview.CompatListActivity; public class InflatingListActivity extends CompatListActivity { + private static final String PACKAGE_NAME = "com.android.test.uibench"; + private static final ComponentName LATIN_WORDS = + ComponentName.createRelative(PACKAGE_NAME, ".InflatingListActivity"); + private static final ComponentName EMOJI = + ComponentName.createRelative(PACKAGE_NAME, ".InflatingEmojiListActivity"); + private static final ComponentName HAN = + ComponentName.createRelative(PACKAGE_NAME, ".InflatingHanListActivity"); + private static final ComponentName LONG_STRING = + ComponentName.createRelative(PACKAGE_NAME, ".InflatingLongStringListActivity"); @Override protected ListAdapter createListAdapter() { - return new ArrayAdapter<String>(this, - android.R.layout.simple_list_item_1, TextUtils.buildSimpleStringList()) { + final ComponentName targetComponent = getIntent().getComponent(); + + final String[] testStrings; + if (targetComponent.equals(LATIN_WORDS)) { + testStrings = TextUtils.buildSimpleStringList(); + } else if (targetComponent.equals(EMOJI)) { + testStrings = TextUtils.buildEmojiStringList(); + } else if (targetComponent.equals(HAN)) { + testStrings = TextUtils.buildHanStringList(); + } else if (targetComponent.equals(LONG_STRING)) { + testStrings = TextUtils.buildLongStringList(); + } else { + throw new RuntimeException("Unknown Component: " + targetComponent); + } + + return new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, testStrings) { @Override public View getView(int position, View convertView, ViewGroup parent) { // pathological getView behavior: drop convertView on the floor to force inflation diff --git a/tests/UiBench/src/com/android/test/uibench/TextUtils.java b/tests/UiBench/src/com/android/test/uibench/TextUtils.java index 32a59868e6c0..2df91047cdf8 100644 --- a/tests/UiBench/src/com/android/test/uibench/TextUtils.java +++ b/tests/UiBench/src/com/android/test/uibench/TextUtils.java @@ -15,11 +15,24 @@ */ package com.android.test.uibench; +import android.icu.text.UnicodeSet; +import android.icu.text.UnicodeSetIterator; + +import java.util.ArrayList; import java.util.Random; public class TextUtils { private static final int STRING_COUNT = 200; - private static final int SIMPLE_STRING_LENGTH = 10; + private static final int SIMPLE_STRING_LENGTH = 10; // in code points + + private static String[] UnicodeSetToArray(UnicodeSet set) { + final UnicodeSetIterator iterator = new UnicodeSetIterator(set); + final ArrayList<String> out = new ArrayList<>(set.size()); + while (iterator.next()) { + out.add(iterator.getString()); + } + return out.toArray(new String[out.size()]); + } /** * Create word of random assortment of lower/upper case letters @@ -34,10 +47,34 @@ public class TextUtils { return result; } + /** + * Create word from a random assortment of a given set of codepoints, given as strings. + */ + private static String randomWordFromStringSet(Random random, int length, String[] stringSet) { + final StringBuilder sb = new StringBuilder(length); + final int setLength = stringSet.length; + for (int j = 0; j < length; j++) { + sb.append(stringSet[random.nextInt(setLength)]); + } + return sb.toString(); + } + public static String[] buildSimpleStringList() { return buildSimpleStringList(SIMPLE_STRING_LENGTH); } + public static String[] buildEmojiStringList() { + return buildEmojiStringList(SIMPLE_STRING_LENGTH); + } + + public static String[] buildHanStringList() { + return buildHanStringList(SIMPLE_STRING_LENGTH); + } + + public static String[] buildLongStringList() { + return buildLongStringList(SIMPLE_STRING_LENGTH); + } + public static String[] buildSimpleStringList(int stringLength) { String[] strings = new String[STRING_COUNT]; Random random = new Random(0); @@ -47,6 +84,41 @@ public class TextUtils { return strings; } + private static String[] buildStringListFromUnicodeSet(int stringLength, UnicodeSet set) { + final String[] strings = new String[STRING_COUNT]; + final Random random = new Random(0); + final String[] stringSet = UnicodeSetToArray(set); + for (int i = 0; i < strings.length; i++) { + strings[i] = randomWordFromStringSet(random, stringLength, stringSet); + } + return strings; + } + + public static String[] buildEmojiStringList(int stringLength) { + return buildStringListFromUnicodeSet(stringLength, new UnicodeSet("[:emoji:]")); + } + + public static String[] buildHanStringList(int stringLength) { + return buildStringListFromUnicodeSet(stringLength, new UnicodeSet("[\\u4E00-\\u9FA0]")); + } + + public static String[] buildLongStringList(int stringLength) { + final int WORD_COUNT = 100; + final String[] strings = new String[STRING_COUNT]; + final Random random = new Random(0); + for (int i = 0; i < strings.length; i++) { + final StringBuilder sb = new StringBuilder((stringLength + 1) * WORD_COUNT); + for (int j = 0; j < WORD_COUNT; ++j) { + if (j != 0) { + sb.append(' '); + } + sb.append(randomWord(random, stringLength)); + } + strings[i] = sb.toString(); + } + return strings; + } + // a small number of strings reused frequently, expected to hit // in the word-granularity text layout cache static final String[] CACHE_HIT_STRINGS = new String[] { diff --git a/tests/UiBench/src/com/android/test/uibench/TrivialAnimationActivityWideGamut.java b/tests/UiBench/src/com/android/test/uibench/TrivialAnimationActivityWideGamut.java new file mode 100644 index 000000000000..c492753d8b7a --- /dev/null +++ b/tests/UiBench/src/com/android/test/uibench/TrivialAnimationActivityWideGamut.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 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.test.uibench; + +public class TrivialAnimationActivityWideGamut extends TrivialAnimationActivity { } diff --git a/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java b/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java index 11ea36132dcc..4ab38a66d504 100644 --- a/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java +++ b/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java @@ -24,6 +24,7 @@ public class BrowseFragment extends android.support.v17.leanback.app.BrowseSuppo @Override public void onCreate(Bundle savedInstanceState) { + TestHelper.initHeaderState(this); super.onCreate(savedInstanceState); BitmapLoader.clear(); TestHelper.initBackground(getActivity()); diff --git a/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java b/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java index 2bf388501ba0..bf408f7475ac 100644 --- a/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java +++ b/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java @@ -40,6 +40,7 @@ public class TestHelper { public static final String EXTRA_CARD_ROUND_RECT = "extra_card_round_rect"; public static final String EXTRA_ENTRANCE_TRANSITION = "extra_entrance_transition"; public static final String EXTRA_BITMAP_UPLOAD = "extra_bitmap_upload"; + public static final String EXTRA_SHOW_FAST_LANE = "extra_show_fast_lane"; /** * Dont change the default values, they gave baseline for measuring the performance @@ -53,6 +54,7 @@ public class TestHelper { static final boolean DEFAULT_CARD_SHADOW = true; static final boolean DEFAULT_CARD_ROUND_RECT = true; static final boolean DEFAULT_BITMAP_UPLOAD = true; + static final boolean DEFAULT_SHOW_FAST_LANE = true; static long sCardIdSeed = 0; static long sRowIdSeed = 0; @@ -235,4 +237,11 @@ public class TestHelper { manager.setBitmap(bitmap); } } + + public static void initHeaderState(BrowseFragment fragment) { + if (!fragment.getActivity().getIntent() + .getBooleanExtra(EXTRA_SHOW_FAST_LANE, DEFAULT_SHOW_FAST_LANE)) { + fragment.setHeadersState(BrowseFragment.HEADERS_HIDDEN); + } + } } diff --git a/tests/UsbTests/Android.mk b/tests/UsbTests/Android.mk new file mode 100644 index 000000000000..a04f32a6d714 --- /dev/null +++ b/tests/UsbTests/Android.mk @@ -0,0 +1,44 @@ +# +# Copyright (C) 2018 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. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + frameworks-base-testutils \ + android-support-test \ + mockito-target-inline-minus-junit4 \ + platform-test-annotations \ + services.core \ + services.net \ + services.usb \ + truth-prebuilt \ + +LOCAL_JNI_SHARED_LIBRARIES := \ + libdexmakerjvmtiagent \ + +LOCAL_CERTIFICATE := platform + +LOCAL_PACKAGE_NAME := UsbTests + +LOCAL_COMPATIBILITY_SUITE := device-tests + +include $(BUILD_PACKAGE) diff --git a/tests/UsbTests/AndroidManifest.xml b/tests/UsbTests/AndroidManifest.xml new file mode 100644 index 000000000000..5d606951bb5e --- /dev/null +++ b/tests/UsbTests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.usb" > + + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.usb" + android:label="UsbTests"/> +</manifest> diff --git a/tests/UsbTests/AndroidTest.xml b/tests/UsbTests/AndroidTest.xml new file mode 100644 index 000000000000..0b623fbf2015 --- /dev/null +++ b/tests/UsbTests/AndroidTest.xml @@ -0,0 +1,29 @@ +<!-- Copyright (C) 2018 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. +--> +<configuration description="Runs sample instrumentation test."> + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="UsbTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="UsbTests"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.usb"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration>
\ No newline at end of file diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java new file mode 100644 index 000000000000..c491b4658999 --- /dev/null +++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2018 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.server.usb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.hardware.usb.UsbManager; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.FgThread; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * Tests for UsbHandler state changes. + */ +@RunWith(AndroidJUnit4.class) +public class UsbHandlerTest { + private static final String TAG = UsbHandlerTest.class.getSimpleName(); + + @Mock + private UsbDeviceManager mUsbDeviceManager; + @Mock + private UsbDebuggingManager mUsbDebuggingManager; + @Mock + private UsbAlsaManager mUsbAlsaManager; + @Mock + private UsbSettingsManager mUsbSettingsManager; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private SharedPreferences.Editor mEditor; + + private MockUsbHandler mUsbHandler; + + private static final int MSG_UPDATE_STATE = 0; + private static final int MSG_ENABLE_ADB = 1; + private static final int MSG_SET_CURRENT_FUNCTIONS = 2; + private static final int MSG_SYSTEM_READY = 3; + private static final int MSG_BOOT_COMPLETED = 4; + private static final int MSG_USER_SWITCHED = 5; + private static final int MSG_UPDATE_USER_RESTRICTIONS = 6; + private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12; + private static final int MSG_UPDATE_SCREEN_LOCK = 13; + + private Map<String, String> mMockProperties; + private Map<String, Integer> mMockGlobalSettings; + + private class MockUsbHandler extends UsbDeviceManager.UsbHandler { + boolean mIsUsbTransferAllowed; + Intent mBroadcastedIntent; + + MockUsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager, + UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager, + UsbSettingsManager settingsManager) { + super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager); + mUseUsbNotification = false; + mIsUsbTransferAllowed = true; + mCurrentUsbFunctionsReceived = true; + } + + @Override + protected void setEnabledFunctions(long functions, boolean force) { + mCurrentFunctions = functions; + } + + @Override + protected void setSystemProperty(String property, String value) { + mMockProperties.put(property, value); + } + + @Override + protected void putGlobalSettings(ContentResolver resolver, String setting, int val) { + mMockGlobalSettings.put(setting, val); + } + + @Override + protected String getSystemProperty(String property, String def) { + if (mMockProperties.containsKey(property)) { + return mMockProperties.get(property); + } + return def; + } + + @Override + protected boolean isUsbTransferAllowed() { + return mIsUsbTransferAllowed; + } + + @Override + protected SharedPreferences getPinnedSharedPrefs(Context context) { + return mSharedPreferences; + } + + @Override + protected void sendStickyBroadcast(Intent intent) { + mBroadcastedIntent = intent; + } + } + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + mMockProperties = new HashMap<>(); + mMockGlobalSettings = new HashMap<>(); + when(mSharedPreferences.edit()).thenReturn(mEditor); + + mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(), + InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager, + mUsbAlsaManager, mUsbSettingsManager); + } + + @SmallTest + public void setFunctionsMtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + } + + @SmallTest + public void setFunctionsPtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_PTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_PTP, 0); + } + + @SmallTest + public void setFunctionsMidi() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MIDI)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MIDI, 0); + } + + @SmallTest + public void setFunctionsRndis() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_RNDIS)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_RNDIS, 0); + } + + @SmallTest + public void enableAdb() { + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 1)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + assertTrue(mUsbHandler.mAdbEnabled); + assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler + .USB_PERSISTENT_CONFIG_PROPERTY), UsbManager.USB_FUNCTION_ADB); + verify(mUsbDebuggingManager).setAdbEnabled(true); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1)); + + assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_CONFIGURED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false)); + } + + @SmallTest + public void disableAdb() { + mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_ADB); + mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(), + InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager, + mUsbAlsaManager, mUsbSettingsManager); + + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 0)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + assertFalse(mUsbHandler.mAdbEnabled); + assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler + .USB_PERSISTENT_CONFIG_PROPERTY), ""); + verify(mUsbDebuggingManager).setAdbEnabled(false); + } + + @SmallTest + public void bootCompletedCharging() { + sendBootCompleteMessages(mUsbHandler); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @Test + @SmallTest + public void bootCompletedAdbEnabled() { + mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb"); + mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(), + InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager, + mUsbAlsaManager, mUsbSettingsManager); + + sendBootCompleteMessages(mUsbHandler); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + assertEquals(mMockGlobalSettings.get(Settings.Global.ADB_ENABLED).intValue(), 1); + assertTrue(mUsbHandler.mAdbEnabled); + verify(mUsbDebuggingManager).setAdbEnabled(true); + } + + @SmallTest + public void userSwitchedDisablesMtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_USER_SWITCHED, + UserHandle.getCallingUserId() + 1)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @SmallTest + public void changedRestrictionsDisablesMtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.mIsUsbTransferAllowed = false; + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_USER_RESTRICTIONS)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @SmallTest + public void disconnectResetsCharging() { + sendBootCompleteMessages(mUsbHandler); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 0, 0)); + + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @SmallTest + public void configuredSendsBroadcast() { + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1)); + + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_CONFIGURED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_FUNCTION_MTP, false)); + } + + @SmallTest + public void setScreenUnlockedFunctions() { + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0)); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + verify(mEditor).putString(String.format(Locale.ENGLISH, + UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), + UsbManager.USB_FUNCTION_MTP); + } + + @SmallTest + public void unlockScreen() { + when(mSharedPreferences.getString(String.format(Locale.ENGLISH, + UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), "")) + .thenReturn(UsbManager.USB_FUNCTION_MTP); + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0)); + + assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + } + + private static void sendBootCompleteMessages(Handler handler) { + handler.handleMessage(handler.obtainMessage(MSG_BOOT_COMPLETED)); + handler.handleMessage(handler.obtainMessage(MSG_SYSTEM_READY)); + } +}
\ No newline at end of file diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk index f356afb799a2..7800a8078d2a 100644 --- a/tests/WindowAnimationJank/Android.mk +++ b/tests/WindowAnimationJank/Android.mk @@ -27,9 +27,10 @@ LOCAL_PACKAGE_NAME := WindowAnimationJank LOCAL_STATIC_JAVA_LIBRARIES := \ ub-uiautomator \ ub-janktesthelper \ - legacy-android-test \ junit +LOCAL_JAVA_LIBRARIES := android.test.base.stubs + LOCAL_SDK_VERSION := current include $(BUILD_PACKAGE) diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java index 6b9bb31a75c7..9174014dedf4 100644 --- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java +++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java @@ -24,6 +24,7 @@ import android.os.SystemClock; import android.util.Log; import android.util.MergedConfiguration; import android.view.Display; +import android.view.DisplayCutout; import android.view.IWindowSession; import android.view.Surface; import android.view.View; @@ -103,7 +104,8 @@ public class MainActivity extends Activity { WindowManagerGlobal.getWindowSession().relayout(window, window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, - new MergedConfiguration(), new Surface()); + new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(), + new Surface()); } catch (RemoteException e) { e.printStackTrace(); } diff --git a/tests/libs-permissions/Android.mk b/tests/libs-permissions/Android.mk new file mode 100644 index 000000000000..eb3862390338 --- /dev/null +++ b/tests/libs-permissions/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := com.android.test.libs.product +LOCAL_PRODUCT_MODULE := true +LOCAL_SRC_FILES := $(call all-java-files-under, product/java) +LOCAL_REQUIRED_MODULES := com.android.test.libs.product.xml +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := com.android.test.libs.product.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions +LOCAL_SRC_FILES:= product/com.android.test.libs.product.xml +include $(BUILD_PREBUILT) diff --git a/tests/libs-permissions/product/com.android.test.libs.product.xml b/tests/libs-permissions/product/com.android.test.libs.product.xml new file mode 100644 index 000000000000..0a955e9df7fd --- /dev/null +++ b/tests/libs-permissions/product/com.android.test.libs.product.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 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. +--> + +<permissions> + <library name="com.android.test.libs.product" + file="/product/framework/com.android.test.libs.product.jar" /> +</permissions> diff --git a/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java b/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java new file mode 100644 index 000000000000..f49b46e72c25 --- /dev/null +++ b/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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.test.libs.product; + +/** + * Test class for product libs. + */ +public class LibsProductTest { + + /** + * Dummpy method for testing. + */ + public static void test() { + } +} diff --git a/tests/net/Android.mk b/tests/net/Android.mk index 49078940d474..9130e7db80a1 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -21,7 +21,9 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ services.net LOCAL_JAVA_LIBRARIES := \ - android.test.runner + android.test.runner \ + android.test.base \ + android.test.mock LOCAL_PACKAGE_NAME := FrameworksNetTests LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java index 4c6a64464b6e..2e1519b8717b 100644 --- a/tests/net/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java @@ -18,7 +18,6 @@ package android.net; import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; @@ -147,14 +146,6 @@ public class NetworkCapabilitiesTest { assertEquals("", nc1.describeImmutableDifferences(nc2)); assertEquals("", nc1.describeImmutableDifferences(nc1)); - // DUN changing (http://b/65257223) - nc1 = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_DUN) - .addCapability(NET_CAPABILITY_INTERNET); - nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET); - assertEquals("", nc1.describeImmutableDifferences(nc2)); - assertEquals("", nc1.describeImmutableDifferences(nc1)); - // Immutable capability changing nc1 = new NetworkCapabilities() .addCapability(NET_CAPABILITY_INTERNET) diff --git a/tests/net/java/android/net/NetworkStatsHistoryTest.java b/tests/net/java/android/net/NetworkStatsHistoryTest.java index 1c0c14eac08b..301d04d5cf6f 100644 --- a/tests/net/java/android/net/NetworkStatsHistoryTest.java +++ b/tests/net/java/android/net/NetworkStatsHistoryTest.java @@ -32,9 +32,14 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static android.text.format.DateUtils.YEAR_IN_MILLIS; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.Suppress; import android.util.Log; @@ -46,25 +51,31 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.Random; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) @SmallTest -public class NetworkStatsHistoryTest extends AndroidTestCase { +public class NetworkStatsHistoryTest { private static final String TAG = "NetworkStatsHistoryTest"; private static final long TEST_START = 1194220800000L; private NetworkStatsHistory stats; - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { if (stats != null) { assertConsistent(stats); } } + @Test public void testReadOriginalVersion() throws Exception { - final DataInputStream in = new DataInputStream( - getContext().getResources().openRawResource(R.raw.history_v1)); + final Context context = InstrumentationRegistry.getContext(); + final DataInputStream in = + new DataInputStream(context.getResources().openRawResource(R.raw.history_v1)); NetworkStatsHistory.Entry entry = null; try { @@ -88,6 +99,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { } } + @Test public void testRecordSingleBucket() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -100,6 +112,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L); } + @Test public void testRecordEqualBuckets() throws Exception { final long bucketDuration = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(bucketDuration); @@ -114,6 +127,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 1, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); } + @Test public void testRecordTouchingBuckets() throws Exception { final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -134,6 +148,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 2, 4 * MINUTE_IN_MILLIS, 200L, 400L, 1000L, 2000L, 20L); } + @Test public void testRecordGapBuckets() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -165,6 +180,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 3, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); } + @Test public void testRecordOverlapBuckets() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -182,6 +198,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 1, (HOUR_IN_MILLIS / 2), 512L, 5L, 512L, 5L, 5L); } + @Test public void testRecordEntireGapIdentical() throws Exception { // first, create two separate histories far apart final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); @@ -206,6 +223,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 3, 500L, 250L); } + @Test public void testRecordEntireOverlapVaryingBuckets() throws Exception { // create history just over hour bucket boundary final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); @@ -247,6 +265,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertValues(stats, 3, 150L, 150L); } + @Test public void testRemove() throws Exception { stats = new NetworkStatsHistory(HOUR_IN_MILLIS); @@ -280,6 +299,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertEquals(0, stats.size()); } + @Test public void testTotalData() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -304,7 +324,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { } - @Suppress + @Test public void testFuzzing() throws Exception { try { // fuzzing with random events, looking for crashes @@ -341,6 +361,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { return value < 0 ? -value : value; } + @Test public void testIgnoreFields() throws Exception { final NetworkStatsHistory history = new NetworkStatsHistory( MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES); @@ -353,6 +374,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN); } + @Test public void testIgnoreFieldsRecordIn() throws Exception { final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); final NetworkStatsHistory partial = new NetworkStatsHistory( @@ -365,6 +387,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L); } + @Test public void testIgnoreFieldsRecordOut() throws Exception { final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); final NetworkStatsHistory partial = new NetworkStatsHistory( @@ -377,6 +400,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertFullValues(full, MINUTE_IN_MILLIS, 0L, 10L, 0L, 0L, 4L); } + @Test public void testSerialize() throws Exception { final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL); before.recordData(0, 4 * MINUTE_IN_MILLIS, @@ -396,6 +420,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertFullValues(after, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); } + @Test public void testVarLong() throws Exception { assertEquals(0L, performVarLong(0L)); assertEquals(-1L, performVarLong(-1L)); @@ -409,6 +434,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertEquals(Long.MAX_VALUE - 40, performVarLong(Long.MAX_VALUE - 40)); } + @Test public void testIndexBeforeAfter() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -451,6 +477,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertIndexBeforeAfter(stats, 4, 4, Long.MAX_VALUE); } + @Test public void testIntersects() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; stats = new NetworkStatsHistory(BUCKET_SIZE); @@ -485,6 +512,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1)); } + @Test public void testSetValues() throws Exception { stats = new NetworkStatsHistory(HOUR_IN_MILLIS); stats.recordData(TEST_START, TEST_START + 1, diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java index 7a2344317223..90a373a9c11e 100644 --- a/tests/net/java/com/android/internal/util/RingBufferTest.java +++ b/tests/net/java/com/android/internal/util/RingBufferTest.java @@ -17,6 +17,7 @@ package com.android.internal.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import android.support.test.filters.SmallTest; @@ -129,6 +130,55 @@ public class RingBufferTest { assertArraysEqual(expected2, buffer.toArray()); } + @Test + public void testGetNextSlot() { + int capacity = 100; + RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity); + + final DummyClass1[] actual = new DummyClass1[capacity]; + final DummyClass1[] expected = new DummyClass1[capacity]; + for (int i = 0; i < capacity; ++i) { + final DummyClass1 obj = buffer.getNextSlot(); + obj.x = capacity * i; + actual[i] = obj; + expected[i] = new DummyClass1(); + expected[i].x = capacity * i; + } + assertArraysEqual(expected, buffer.toArray()); + + for (int i = 0; i < capacity; ++i) { + if (actual[i] != buffer.getNextSlot()) { + fail("getNextSlot() should re-use objects if available"); + } + } + + RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity); + assertNull("getNextSlot() should return null if the object can't be initiated " + + "(No nullary constructor)", buffer2.getNextSlot()); + + RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity); + assertNull("getNextSlot() should return null if the object can't be initiated " + + "(Inaccessible class)", buffer3.getNextSlot()); + } + + public static final class DummyClass1 { + int x; + + public boolean equals(Object o) { + if (o instanceof DummyClass1) { + final DummyClass1 other = (DummyClass1) o; + return other.x == this.x; + } + return false; + } + } + + public static final class DummyClass2 { + public DummyClass2(int x) {} + } + + private static final class DummyClass3 {} + static <T> void assertArraysEqual(T[] expected, T[] got) { if (expected.length != got.length) { fail(Arrays.toString(expected) + " and " + Arrays.toString(got) diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index a115146486a4..e692652c7ea4 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -40,6 +40,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -59,12 +60,14 @@ import android.net.NetworkRequest; import android.net.util.SharedLog; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; +import android.os.Bundle; import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.test.TestLooper; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -308,7 +311,7 @@ public class TetheringTest { // Emulate pressing the USB tethering button in Settings UI. mTethering.startTethering(TETHERING_USB, null, false); mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); // Pretend we receive a USB connected broadcast. Here we also pretend // that the RNDIS function is somehow enabled, so that we see if we @@ -558,6 +561,90 @@ public class TetheringTest { verifyNoMoreInteractions(mNMService); } + private void userRestrictionsListenerBehaviour( + boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList, + int expectedInteractionsWithShowNotification) throws Exception { + final int userId = 0; + final Bundle currRestrictions = new Bundle(); + final Bundle newRestrictions = new Bundle(); + Tethering tethering = mock(Tethering.class); + Tethering.TetheringUserRestrictionListener turl = + new Tethering.TetheringUserRestrictionListener(tethering); + + currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow); + newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); + when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); + + turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions); + + verify(tethering, times(expectedInteractionsWithShowNotification)) + .showTetheredNotification(anyInt(), eq(false)); + + verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll(); + } + + @Test + public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { + final String[] emptyActiveIfacesList = new String[]{}; + final boolean currDisallow = false; + final boolean nextDisallow = true; + final int expectedInteractionsWithShowNotification = 0; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname}; + final boolean currDisallow = false; + final boolean nextDisallow = true; + final int expectedInteractionsWithShowNotification = 1; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{}; + final boolean currDisallow = true; + final boolean nextDisallow = false; + final int expectedInteractionsWithShowNotification = 0; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname}; + final boolean currDisallow = true; + final boolean nextDisallow = false; + final int expectedInteractionsWithShowNotification = 0; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + @Test + public void testDisallowTetheringUnchanged() throws Exception { + final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname}; + final int expectedInteractionsWithShowNotification = 0; + boolean currDisallow = true; + boolean nextDisallow = true; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + + currDisallow = false; + nextDisallow = false; + + userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + expectedInteractionsWithShowNotification); + } + + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index da0a48a8ddef..6f1433286ca4 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -26,6 +26,9 @@ import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.os.Process.myUid; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import static com.android.server.net.NetworkStatsCollection.multiplySafe; @@ -37,11 +40,12 @@ import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.os.Process; import android.os.UserHandle; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; -import android.test.AndroidTestCase; import android.test.MoreAsserts; -import android.test.suitebuilder.annotation.SmallTest; import android.text.format.DateUtils; import android.util.RecurrenceRule; @@ -64,11 +68,17 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + /** * Tests for {@link NetworkStatsCollection}. */ +@RunWith(AndroidJUnit4.class) @SmallTest -public class NetworkStatsCollectionTest extends AndroidTestCase { +public class NetworkStatsCollectionTest { private static final String TEST_FILE = "test.bin"; private static final String TEST_IMSI = "310260000000000"; @@ -79,18 +89,15 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { private static Clock sOriginalClock; - @Override + @Before public void setUp() throws Exception { - super.setUp(); sOriginalClock = RecurrenceRule.sClock; - // ignore any device overlay while testing NetworkTemplate.forceAllNetworkTypes(); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { RecurrenceRule.sClock = sOriginalClock; } @@ -98,8 +105,10 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault()); } + @Test public void testReadLegacyNetwork() throws Exception { - final File testFile = new File(getContext().getFilesDir(), TEST_FILE); + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); stageFile(R.raw.netstats_v1, testFile); final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); @@ -124,8 +133,10 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); } + @Test public void testReadLegacyUid() throws Exception { - final File testFile = new File(getContext().getFilesDir(), TEST_FILE); + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); stageFile(R.raw.netstats_uid_v4, testFile); final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); @@ -150,8 +161,10 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); } + @Test public void testReadLegacyUidTags() throws Exception { - final File testFile = new File(getContext().getFilesDir(), TEST_FILE); + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); stageFile(R.raw.netstats_uid_v4, testFile); final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); @@ -176,6 +189,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { 77017831L, 100995L, 35436758L, 92344L); } + @Test public void testStartEndAtomicBuckets() throws Exception { final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); @@ -190,6 +204,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis()); } + @Test public void testAccessLevels() throws Exception { final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); final NetworkStats.Entry entry = new NetworkStats.Entry(); @@ -250,8 +265,10 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { 0, NetworkStatsAccess.Level.DEVICE); } + @Test public void testAugmentPlan() throws Exception { - final File testFile = new File(getContext().getFilesDir(), TEST_FILE); + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); stageFile(R.raw.netstats_v1, testFile); final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); @@ -439,6 +456,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { } } + @Test public void testAugmentPlanGigantic() throws Exception { // We're in the future, but not that far off setClock(Instant.parse("2012-06-01T00:00:00.00Z")); @@ -461,6 +479,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { assertEquals(4_939_212_386L, getHistory(large, plan, TIME_A, TIME_C).getTotalBytes()); } + @Test public void testRounding() throws Exception { final NetworkStatsCollection coll = new NetworkStatsCollection(HOUR_IN_MILLIS); @@ -482,6 +501,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { assertEquals(TIME_A - HOUR_IN_MILLIS, coll.roundDown(TIME_A - 1)); } + @Test public void testMultiplySafe() { assertEquals(25, multiplySafe(50, 1, 2)); assertEquals(100, multiplySafe(50, 2, 1)); @@ -510,7 +530,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { InputStream in = null; OutputStream out = null; try { - in = getContext().getResources().openRawResource(rawId); + in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); out = new FileOutputStream(file); Streams.copy(in, out); } finally { diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 47c345540973..0f26edb28009 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -89,8 +89,8 @@ import android.os.Messenger; import android.os.PowerManager; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; +import android.support.test.filters.SmallTest; import android.telephony.TelephonyManager; -import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.util.TrustedTime; @@ -211,7 +211,6 @@ public class NetworkStatsServiceTest { ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); - } @After diff --git a/tests/notification/Android.mk b/tests/notification/Android.mk index 0669553bf03e..255e6e70a921 100644 --- a/tests/notification/Android.mk +++ b/tests/notification/Android.mk @@ -7,7 +7,7 @@ LOCAL_MODULE_TAGS := tests # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner.stubs LOCAL_PACKAGE_NAME := NotificationTests LOCAL_SDK_VERSION := 21 diff --git a/tests/permission/Android.mk b/tests/permission/Android.mk index 54688c891046..7f81d9a3a894 100644 --- a/tests/permission/Android.mk +++ b/tests/permission/Android.mk @@ -7,8 +7,8 @@ LOCAL_MODULE_TAGS := tests # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := FrameworkPermissionTests include $(BUILD_PACKAGE) diff --git a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java index 93aa555ca46b..b39db61794de 100644 --- a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java @@ -71,18 +71,6 @@ public class PmPermissionsTests extends AndroidTestCase { private class TestInstallObserver extends PackageInstallObserver { } - @SmallTest - public void testInstallPackage() { - TestInstallObserver observer = new TestInstallObserver(); - try { - mPm.installPackage(null, observer, 0, null); - fail("PackageManager.installPackage" + - "did not throw SecurityException as expected"); - } catch (SecurityException e) { - // expected - } - } - /* * This test verifies that PackageManger.freeStorage * enforces permission android.permission.CLEAR_APP_CACHE diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java index 4098b989a16b..0504c79c55bd 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java @@ -62,6 +62,11 @@ public class ServiceManagerPermissionTests extends TestCase { public boolean isRuntimePermission(String permission) { return false; } + + @Override + public int getPackageUid(String packageName, int flags) { + return -1; + } }; ServiceManagerNative.asInterface(BinderInternal.getContextObject()) .setPermissionController(pc); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 4aeae70ec979..9c4da1f72e38 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -24,7 +24,6 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.IWindowManager; import junit.framework.TestCase; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.view.Display.DEFAULT_DISPLAY; /** diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk new file mode 100644 index 000000000000..3c80ad8587af --- /dev/null +++ b/tests/privapp-permissions/Android.mk @@ -0,0 +1,45 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_PACKAGE_NAME := PrivAppPermissionTest +LOCAL_PRIVILEGED_MODULE := true +LOCAL_MANIFEST_FILE := system/AndroidManifest.xml +LOCAL_REQUIRED_MODULES := privapp-permissions-test.xml +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) +LOCAL_MODULE := privapp-permissions-test.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions +LOCAL_SRC_FILES:= system/privapp-permissions-test.xml +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_PACKAGE_NAME := VendorPrivAppPermissionTest +LOCAL_PRIVILEGED_MODULE := true +LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml +LOCAL_VENDOR_MODULE := true +LOCAL_REQUIRED_MODULES := vendorprivapp-permissions-test.xml +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) +LOCAL_MODULE := vendorprivapp-permissions-test.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/permissions +LOCAL_SRC_FILES:= vendor/privapp-permissions-test.xml +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_PACKAGE_NAME := ProductPrivAppPermissionTest +LOCAL_PRIVILEGED_MODULE := true +LOCAL_MANIFEST_FILE := product/AndroidManifest.xml +LOCAL_PRODUCT_MODULE := true +LOCAL_REQUIRED_MODULES := productprivapp-permissions-test.xml +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) +LOCAL_MODULE := productprivapp-permissions-test.xml +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions +LOCAL_SRC_FILES:= product/privapp-permissions-test.xml +include $(BUILD_PREBUILT) diff --git a/tests/privapp-permissions/product/AndroidManifest.xml b/tests/privapp-permissions/product/AndroidManifest.xml new file mode 100644 index 000000000000..3d9415c3df41 --- /dev/null +++ b/tests/privapp-permissions/product/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2018 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.permission.privapp.tests.product"> + + <!-- MANAGE_USB is signature|privileged --> + <uses-permission android:name="android.permission.MANAGE_USB"/> +</manifest> diff --git a/tests/privapp-permissions/product/privapp-permissions-test.xml b/tests/privapp-permissions/product/privapp-permissions-test.xml new file mode 100644 index 000000000000..f298f9da41b7 --- /dev/null +++ b/tests/privapp-permissions/product/privapp-permissions-test.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<permissions> + <privapp-permissions package="com.android.framework.permission.privapp.tests.product"> + <permission name="android.permission.MANAGE_USB"/> + </privapp-permissions> +</permissions> diff --git a/tests/privapp-permissions/system/AndroidManifest.xml b/tests/privapp-permissions/system/AndroidManifest.xml new file mode 100644 index 000000000000..2099e31bd2c1 --- /dev/null +++ b/tests/privapp-permissions/system/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2017 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.permission.privapp.tests.system"> + + <!-- MANAGE_USB is signature|privileged --> + <uses-permission android:name="android.permission.MANAGE_USB"/> +</manifest> diff --git a/tests/privapp-permissions/system/privapp-permissions-test.xml b/tests/privapp-permissions/system/privapp-permissions-test.xml new file mode 100644 index 000000000000..a0cb6bc74195 --- /dev/null +++ b/tests/privapp-permissions/system/privapp-permissions-test.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<permissions> + <privapp-permissions package="com.android.framework.permission.privapp.tests.system"> + <permission name="android.permission.MANAGE_USB"/> + </privapp-permissions> +</permissions> diff --git a/tests/privapp-permissions/vendor/AndroidManifest.xml b/tests/privapp-permissions/vendor/AndroidManifest.xml new file mode 100644 index 000000000000..78dedc5f199b --- /dev/null +++ b/tests/privapp-permissions/vendor/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2017 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.permission.privapp.tests.vendor"> + + <!-- BIND_IMS_SERVICE is signature|privileged|vendorPrivileged --> + <uses-permission android:name="android.permission.BIND_IMS_SERVICE"/> + <!-- MANAGE_USB is signature|privileged and thus cannot be granted to this app --> + <uses-permission android:name="android.permission.MANAGE_USB"/> +</manifest> diff --git a/tests/privapp-permissions/vendor/privapp-permissions-test.xml b/tests/privapp-permissions/vendor/privapp-permissions-test.xml new file mode 100644 index 000000000000..51c588f0dea7 --- /dev/null +++ b/tests/privapp-permissions/vendor/privapp-permissions-test.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<permissions> + <privapp-permissions package="com.android.framework.permission.privapp.tests.vendor"> + <permission name="android.permission.BIND_IMS_SERVICE"/> + <permission name="android.permission.MANAGE_USB"/> + </privapp-permissions> +</permissions> diff --git a/tests/testables/Android.mk b/tests/testables/Android.mk index a58ce784e916..4c4d2b4186dc 100644 --- a/tests/testables/Android.mk +++ b/tests/testables/Android.mk @@ -24,10 +24,9 @@ LOCAL_MODULE_TAG := tests LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ - legacy-android-test + android-support-test -LOCAL_JAVA_LIBRARIES := android.test.runner mockito-target-minus-junit4 +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.mock mockito-target-minus-junit4 include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tests/testables/src/android/testing/PollingCheck.java b/tests/testables/src/android/testing/PollingCheck.java new file mode 100644 index 000000000000..5a31450f048f --- /dev/null +++ b/tests/testables/src/android/testing/PollingCheck.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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 android.testing; + +import junit.framework.Assert; +import java.util.concurrent.Callable; + +public abstract class PollingCheck { + private static final long TIME_SLICE = 50; + private long mTimeout = 3000; + + public static interface PollingCheckCondition { + boolean canProceed(); + } + + public PollingCheck() { + } + + public PollingCheck(long timeout) { + mTimeout = timeout; + } + + protected abstract boolean check(); + + public void run() { + if (check()) { + return; + } + + long timeout = mTimeout; + while (timeout > 0) { + try { + Thread.sleep(TIME_SLICE); + } catch (InterruptedException e) { + Assert.fail("unexpected InterruptedException"); + } + + if (check()) { + return; + } + + timeout -= TIME_SLICE; + } + + Assert.fail("unexpected timeout"); + } + + public static void check(CharSequence message, long timeout, Callable<Boolean> condition) + throws Exception { + while (timeout > 0) { + if (condition.call()) { + return; + } + + Thread.sleep(TIME_SLICE); + timeout -= TIME_SLICE; + } + + Assert.fail(message.toString()); + } + + public static void waitFor(final PollingCheckCondition condition) { + new PollingCheck() { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } + + public static void waitFor(long timeout, final PollingCheckCondition condition) { + new PollingCheck(timeout) { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } +} + diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java index 498d517c104b..ffe721993a37 100644 --- a/tests/testables/src/android/testing/TestableContext.java +++ b/tests/testables/src/android/testing/TestableContext.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; @@ -69,6 +70,7 @@ public class TestableContext extends ContextWrapper implements TestRule { private LeakCheck.Tracker mService; private LeakCheck.Tracker mComponent; private TestableResources mTestableResources; + private TestablePermissions mTestablePermissions; public TestableContext(Context base) { this(base, null); @@ -302,6 +304,159 @@ public class TestableContext extends ContextWrapper implements TestRule { super.unregisterComponentCallbacks(callback); } + public TestablePermissions getTestablePermissions() { + if (mTestablePermissions == null) { + mTestablePermissions = new TestablePermissions(); + } + return mTestablePermissions; + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkCallingOrSelfPermission(permission); + } + + @Override + public int checkCallingPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkCallingPermission(permission); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkPermission(permission, pid, uid); + } + + @Override + public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkPermission(permission, pid, uid, callerToken); + } + + @Override + public int checkSelfPermission(String permission) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + return mTestablePermissions.check(permission); + } + return super.checkSelfPermission(permission); + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforceCallingOrSelfPermission(permission, message); + } + } + + @Override + public void enforceCallingPermission(String permission, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforceCallingPermission(permission, message); + } + } + + @Override + public void enforcePermission(String permission, int pid, int uid, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(permission)) { + mTestablePermissions.enforce(permission); + } else { + super.enforcePermission(permission, pid, uid, message); + } + } + + @Override + public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkCallingOrSelfUriPermission(uri, modeFlags); + } + + @Override + public int checkCallingUriPermission(Uri uri, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkCallingUriPermission(uri, modeFlags); + } + + @Override + public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceCallingOrSelfUriPermission(uri, modeFlags, message); + } + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, pid, uid, modeFlags); + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, pid, uid, modeFlags, callerToken); + } + + @Override + public int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, + int uid, int modeFlags) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + return mTestablePermissions.check(uri, modeFlags); + } + return super.checkUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags); + } + + @Override + public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceCallingUriPermission(uri, modeFlags, message); + } + } + + @Override + public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceUriPermission(uri, pid, uid, modeFlags, message); + } + } + + @Override + public void enforceUriPermission(Uri uri, String readPermission, String writePermission, + int pid, int uid, int modeFlags, String message) { + if (mTestablePermissions != null && mTestablePermissions.wantsCall(uri)) { + mTestablePermissions.enforce(uri, modeFlags); + } else { + super.enforceUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags, + message); + } + } + @Override public Statement apply(Statement base, Description description) { return new TestWatcher() { diff --git a/tests/testables/src/android/testing/TestablePermissions.java b/tests/testables/src/android/testing/TestablePermissions.java new file mode 100644 index 000000000000..4f009e406ca7 --- /dev/null +++ b/tests/testables/src/android/testing/TestablePermissions.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 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 android.testing; + +import android.content.pm.PackageManager; +import android.net.Uri; +import android.util.ArrayMap; + +/** + * Simple class for simulating basic permission states for tests. + * + * All enforce* and check* calls on TestableContext are considered the same + * and routed through the same check here. If more fine-grained control is + * required, then either a sub-class or spy on TestableContext is recommended. + */ +public class TestablePermissions { + + private final ArrayMap<String, Integer> mPermissions = new ArrayMap<>(); + private final ArrayMap<Uri, Integer> mUris = new ArrayMap<>(); + + /** + * Sets the return value for checkPermission* calls on TestableContext + * for a specific permission value. For all enforcePermission* calls + * they will throw a security exception if value != PERMISSION_GRANTED. + */ + public void setPermission(String permission, int value) { + mPermissions.put(permission, value); + } + + /** + * Sets the return value for checkUriPermission* calls on TestableContext + * for a specific permission value. For all enforceUriPermission* calls + * they will throw a security exception if value != PERMISSION_GRANTED. + */ + public void setPermission(Uri uri, int value) { + // TODO: Support modeFlags + mUris.put(uri, value); + } + + boolean wantsCall(String permission) { + return mPermissions.containsKey(permission); + } + + boolean wantsCall(Uri uri) { + return mUris.containsKey(uri); + } + + int check(String permission) { + return mPermissions.get(permission); + } + + int check(Uri uri, int modeFlags) { + // TODO: Support modeFlags + return mUris.get(uri); + } + + public void enforce(String permission) { + if (check(permission) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(); + } + } + + public void enforce(Uri uri, int modeFlags) { + if (check(uri, modeFlags) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(); + } + } +} diff --git a/tests/testables/src/android/testing/TestableResources.java b/tests/testables/src/android/testing/TestableResources.java index a2fa95deaa60..c60f07d56d92 100644 --- a/tests/testables/src/android/testing/TestableResources.java +++ b/tests/testables/src/android/testing/TestableResources.java @@ -39,7 +39,8 @@ public class TestableResources { private final Resources mResources; private final SparseArray<Object> mOverrides = new SparseArray<>(); - TestableResources(Resources realResources) { + /** Creates a TestableResources instance that calls through to the given real Resources. */ + public TestableResources(Resources realResources) { mResources = mock(Resources.class, withSettings() .spiedInstance(realResources) .defaultAnswer(this::answer)); diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk index 16fe5351c161..6e20d797fb5d 100644 --- a/tests/testables/tests/Android.mk +++ b/tests/testables/tests/Android.mk @@ -28,10 +28,9 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ mockito-target-minus-junit4 \ - legacy-android-test \ testables -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock LOCAL_CERTIFICATE := platform diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml index f6006b0a7fa9..6435ad971476 100644 --- a/tests/testables/tests/AndroidManifest.xml +++ b/tests/testables/tests/AndroidManifest.xml @@ -15,9 +15,11 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.testables"> + package="com.android.testables" android:sharedUserId="android.uid.system"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/tests/testables/tests/src/android/testing/TestablePermissionsTest.java b/tests/testables/tests/src/android/testing/TestablePermissionsTest.java new file mode 100644 index 000000000000..c56146e19a40 --- /dev/null +++ b/tests/testables/tests/src/android/testing/TestablePermissionsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 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 android.testing; + +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static org.junit.Assert.assertEquals; + +import android.Manifest.permission; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.testing.TestableLooper.RunWithLooper; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TestablePermissionsTest { + + private static final Uri URI_1 = Uri.parse("content://my.authority/path1"); + private static final Uri URI_2 = Uri.parse("content://my.authority/path2"); + + @Rule + public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext()); + + @Test + public void testCheck() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS_FULL, + PERMISSION_DENIED); + assertEquals(PERMISSION_GRANTED, + mContext.checkPermission(permission.INTERACT_ACROSS_USERS, 0, 0)); + assertEquals(PERMISSION_DENIED, + mContext.checkPermission(permission.INTERACT_ACROSS_USERS_FULL, 0, 0)); + } + + @Test + public void testCheckUri() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(URI_2, PERMISSION_DENIED); + + assertEquals(PERMISSION_GRANTED, mContext.checkUriPermission(URI_1, 0, 0, 0)); + assertEquals(PERMISSION_DENIED, mContext.checkUriPermission(URI_2, 0, 0, 0)); + } + + @Test + public void testEnforceNoException() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_GRANTED); + mContext.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, ""); + } + + @Test(expected = SecurityException.class) + public void testEnforceWithException() { + mContext.getTestablePermissions().setPermission(permission.INTERACT_ACROSS_USERS, + PERMISSION_DENIED); + mContext.enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS, ""); + } + + @Test + public void testEnforceUriNoException() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_GRANTED); + mContext.enforceUriPermission(URI_1, 0, 0, 0, ""); + } + + @Test(expected = SecurityException.class) + public void testEnforceUriWithException() { + mContext.getTestablePermissions().setPermission(URI_1, PERMISSION_DENIED); + mContext.enforceUriPermission(URI_1, 0, 0, 0, ""); + } + +}
\ No newline at end of file diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk index 700b7cb90c96..a76616f179dd 100644 --- a/tests/utils/testutils/Android.mk +++ b/tests/utils/testutils/Android.mk @@ -24,11 +24,12 @@ LOCAL_MODULE_TAG := tests LOCAL_SRC_FILES := $(call all-java-files-under,java) LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ - legacy-android-test + android-support-test LOCAL_JAVA_LIBRARIES := \ android.test.runner \ - mockito-target-minus-junit4 + android.test.base \ + android.test.mock \ + mockito-target-minus-junit4 \ include $(BUILD_STATIC_JAVA_LIBRARY) |