Add performance test for OomAdjuster
Bug: 140254153
Test: atest -c ActivityManagerPerfTests:OomAdjPerfTest#testOomAdj
Change-Id: Id12667c71300e6fe4dc063c83807834bbdb5e62a
diff --git a/tests/ActivityManagerPerfTests/stub-app/Android.bp b/tests/ActivityManagerPerfTests/stub-app/Android.bp
new file mode 100644
index 0000000..a3c1f5b
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/Android.bp
@@ -0,0 +1,68 @@
+// Copyright (C) 2019 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_test_helper_app {
+ name: "ActivityManagerPerfTestsStubApp1",
+ static_libs: ["ActivityManagerPerfTestsUtils"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: [
+ "app1/res",
+ "res",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--rename-manifest-package com.android.stubs.am1",
+ "--auto-add-overlay",
+ ],
+}
+
+android_test_helper_app {
+ name: "ActivityManagerPerfTestsStubApp2",
+ static_libs: ["ActivityManagerPerfTestsUtils"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: [
+ "app2/res",
+ "res",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--rename-manifest-package com.android.stubs.am2",
+ "--auto-add-overlay",
+ ],
+}
+
+android_test_helper_app {
+ name: "ActivityManagerPerfTestsStubApp3",
+ static_libs: ["ActivityManagerPerfTestsUtils"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: [
+ "app3/res",
+ "res",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--rename-manifest-package com.android.stubs.am3",
+ "--auto-add-overlay",
+ ],
+}
+
diff --git a/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml
new file mode 100644
index 0000000..a57f64c
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.stubs.am">
+
+ <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ <application android:label="Android TestCase" >
+ <provider
+ android:authorities="@string/authority"
+ android:name=".TestContentProvider"
+ android:exported="true" />
+ <receiver
+ android:name=".TestBroadcastReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.stubs.am.ACTION_BROADCAST_TEST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
+ <service
+ android:name=".InitService"
+ android:exported="true" />
+ <service
+ android:name=".TestService"
+ android:exported="true" />
+ <activity
+ android:name=".TestActivity"
+ android:excludeFromRecents="true"
+ android:turnScreenOn="true"
+ android:launchMode="singleTask">
+ <intent-filter>
+ <action android:name="com.android.stubs.am.ACTION_START_TEST_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
diff --git a/tests/ActivityManagerPerfTests/stub-app/app1/res/values/config.xml b/tests/ActivityManagerPerfTests/stub-app/app1/res/values/config.xml
new file mode 100644
index 0000000..667472d
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/app1/res/values/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="authority" translatable="false">com.android.stubs.am1.testapp</string>
+</resources>
diff --git a/tests/ActivityManagerPerfTests/stub-app/app2/res/values/config.xml b/tests/ActivityManagerPerfTests/stub-app/app2/res/values/config.xml
new file mode 100644
index 0000000..0852735
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/app2/res/values/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="authority" translatable="false">com.android.stubs.am2.testapp</string>
+</resources>
diff --git a/tests/ActivityManagerPerfTests/stub-app/app3/res/values/config.xml b/tests/ActivityManagerPerfTests/stub-app/app3/res/values/config.xml
new file mode 100644
index 0000000..6895d72
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/app3/res/values/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="authority" translatable="false">com.android.stubs.am3.testapp</string>
+</resources>
diff --git a/tests/ActivityManagerPerfTests/stub-app/res/layout/activity_content.xml b/tests/ActivityManagerPerfTests/stub-app/res/layout/activity_content.xml
new file mode 100644
index 0000000..f79f006
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/res/layout/activity_content.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/InitService.java b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/InitService.java
new file mode 100644
index 0000000..18fdc44
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/InitService.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2019 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.stubs.am;
+
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_ACQUIRE_CONTENT_PROVIDER;
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_BIND_SERVICE;
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_RELEASE_CONTENT_PROVIDER;
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_SEND_BROADCAST;
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_START_ACTIVITY;
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_STOP_ACTIVITY;
+import static com.android.frameworks.perftests.am.util.Constants.COMMAND_UNBIND_SERVICE;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.ICommandReceiver;
+
+public class InitService extends Service {
+ private static final String TAG = "InitService";
+ public static final boolean VERBOSE = false;
+
+ private static class Stub extends ICommandReceiver.Stub {
+ private final Context mContext;
+ private final Messenger mCallback;
+ private final Handler mHandler;
+ private final Messenger mMessenger;
+ final ArrayMap<String, MyServiceConnection> mServices =
+ new ArrayMap<String, MyServiceConnection>();
+ final ArrayMap<Uri, IContentProvider> mProviders =
+ new ArrayMap<Uri, IContentProvider>();
+
+ Stub(Context context, Messenger callback) {
+ mContext = context;
+ mCallback = callback;
+ HandlerThread thread = new HandlerThread("result handler");
+ thread.start();
+ mHandler = new H(thread.getLooper());
+ mMessenger = new Messenger(mHandler);
+ }
+
+ private class H extends Handler {
+ H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == Constants.MSG_DEFAULT) {
+ if (VERBOSE) {
+ Log.i(TAG, "H: received seq=" + msg.arg1 + ", result=" + msg.arg2);
+ }
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, msg.arg1, msg.arg2, null);
+ } else if (msg.what == Constants.MSG_UNBIND_DONE) {
+ if (VERBOSE) {
+ Log.i(TAG, "H: received unbind=" + msg.obj);
+ }
+ synchronized (InitService.sStub) {
+ Bundle b = (Bundle) msg.obj;
+ String pkg = b.getString(Constants.EXTRA_SOURCE_PACKAGE, "");
+ MyServiceConnection c = mServices.remove(pkg);
+ if (c != null) {
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, c.mSeq,
+ Constants.RESULT_NO_ERROR, null);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void sendCommand(int command, int seq, String sourcePackage, String targetPackage,
+ int flags, Bundle bundle) {
+ if (VERBOSE) {
+ Log.i(TAG, "Received command=" + command + ", seq=" + seq + ", from="
+ + sourcePackage + ", to=" + targetPackage + ", flags=" + flags);
+ }
+ switch (command) {
+ case COMMAND_BIND_SERVICE:
+ handleBindService(seq, targetPackage, flags, bundle);
+ break;
+ case COMMAND_UNBIND_SERVICE:
+ handleUnbindService(seq, targetPackage);
+ break;
+ case COMMAND_ACQUIRE_CONTENT_PROVIDER:
+ acquireProvider(seq, bundle.getParcelable(Constants.EXTRA_URI));
+ break;
+ case COMMAND_RELEASE_CONTENT_PROVIDER:
+ releaseProvider(seq, bundle.getParcelable(Constants.EXTRA_URI));
+ break;
+ case COMMAND_SEND_BROADCAST:
+ sendBroadcast(seq, targetPackage);
+ break;
+ case COMMAND_START_ACTIVITY:
+ startActivity(seq, targetPackage);
+ break;
+ case COMMAND_STOP_ACTIVITY:
+ stopActivity(seq, targetPackage);
+ break;
+ }
+ }
+
+ private void handleBindService(int seq, String targetPackage, int flags, Bundle bundle) {
+ Intent intent = new Intent();
+ intent.setClassName(targetPackage, "com.android.stubs.am.TestService");
+ intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger);
+ if (bundle != null) {
+ intent.putExtras(bundle);
+ }
+ synchronized (this) {
+ if (!mServices.containsKey(targetPackage)) {
+ MyServiceConnection c = new MyServiceConnection(mCallback);
+ c.mSeq = seq;
+ if (!mContext.bindService(intent, c, flags)) {
+ Log.e(TAG, "Unable to bind to service in " + targetPackage);
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
+ Constants.RESULT_ERROR, null);
+ } else {
+ if (VERBOSE) {
+ Log.i(TAG, "Bind to service " + intent);
+ }
+ mServices.put(targetPackage, c);
+ }
+ } else {
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
+ Constants.RESULT_NO_ERROR, null);
+ }
+ }
+ }
+
+ private void handleUnbindService(int seq, String target) {
+ MyServiceConnection c = null;
+ synchronized (this) {
+ c = mServices.get(target);
+ }
+ if (c != null) {
+ c.mSeq = seq;
+ mContext.unbindService(c);
+ }
+ }
+
+ private void acquireProvider(int seq, Uri uri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider != null) {
+ synchronized (mProviders) {
+ mProviders.put(uri, provider);
+ }
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
+ Constants.RESULT_NO_ERROR, null);
+ } else {
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
+ Constants.RESULT_ERROR, null);
+ }
+ }
+
+ private void releaseProvider(int seq, Uri uri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider;
+ synchronized (mProviders) {
+ provider = mProviders.get(uri);
+ }
+ if (provider != null) {
+ resolver.releaseProvider(provider);
+ synchronized (mProviders) {
+ mProviders.remove(uri);
+ }
+ }
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
+ Constants.RESULT_NO_ERROR, null);
+ }
+
+ private void sendBroadcast(final int seq, String targetPackage) {
+ Intent intent = new Intent(Constants.STUB_ACTION_BROADCAST);
+ intent.setPackage(targetPackage);
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
+ Constants.RESULT_NO_ERROR, null);
+ }
+ }, null, 0, null, null);
+ }
+
+ private void startActivity(int seq, String targetPackage) {
+ Intent intent = new Intent(Constants.STUB_ACTION_ACTIVITY);
+ intent.setClassName(targetPackage, "com.android.stubs.am.TestActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(Constants.EXTRA_ARG1, seq);
+ intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger);
+ mContext.startActivity(intent);
+ }
+
+ private void stopActivity(int seq, String targetPackage) {
+ Intent intent = new Intent(Constants.STUB_ACTION_ACTIVITY);
+ intent.setClassName(targetPackage, "com.android.stubs.am.TestActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(Constants.EXTRA_REQ_FINISH_ACTIVITY, true);
+ intent.putExtra(Constants.EXTRA_ARG1, seq);
+ intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger);
+ mContext.startActivity(intent);
+ }
+ };
+
+ private static void sendResult(Messenger callback, int what, int seq, int result, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = seq;
+ msg.arg2 = result;
+ msg.obj = obj;
+ try {
+ if (VERBOSE) {
+ Log.i(TAG, "Sending result seq=" + seq + ", result=" + result);
+ }
+ callback.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sending result back", e);
+ }
+ msg.recycle();
+ }
+
+ private static class MyServiceConnection implements ServiceConnection {
+ private Messenger mCallback;
+ int mSeq;
+
+ MyServiceConnection(Messenger callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, mSeq,
+ Constants.RESULT_NO_ERROR, null);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (sStub) {
+ MyServiceConnection c = sStub.mServices.remove(name.getPackageName());
+ if (c != null) {
+ sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, c.mSeq,
+ Constants.RESULT_NO_ERROR, null);
+ }
+ }
+ }
+ }
+
+ private static Stub sStub = null;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new Binder();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Messenger callback = intent.getParcelableExtra(Constants.EXTRA_RECEIVER_CALLBACK);
+ if (sStub == null) {
+ sStub = new Stub(getApplicationContext(), callback);
+ }
+
+ Bundle extras = new Bundle();
+ extras.putString(Constants.EXTRA_SOURCE_PACKAGE, getPackageName());
+ extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, sStub);
+ sendResult(callback, Constants.REPLY_PACKAGE_START_RESULT,
+ intent.getIntExtra(Constants.EXTRA_SEQ, -1), 0, extras);
+ return START_NOT_STICKY;
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestActivity.java b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestActivity.java
new file mode 100644
index 0000000..f7ea356
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.stubs.am;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+
+public class TestActivity extends Activity {
+ private static final String TAG = "TestActivity";
+ private static final boolean VERBOSE = InitService.VERBOSE;
+
+ private Messenger mResultTo;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " onCreate()");
+ }
+ setContentView(R.layout.activity_content);
+ mResultTo = getIntent().getParcelableExtra(Constants.EXTRA_RECEIVER_CALLBACK);
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ mResultTo = intent.getParcelableExtra(Constants.EXTRA_RECEIVER_CALLBACK);
+ if (intent.getBooleanExtra(Constants.EXTRA_REQ_FINISH_ACTIVITY, false)) {
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " finishing activity");
+ }
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " onResume()");
+ }
+ sendResult(Constants.RESULT_NO_ERROR);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " onDestroy()");
+ }
+ sendResult(Constants.RESULT_NO_ERROR);
+ }
+
+ private void sendResult(int result) {
+ Message msg = Message.obtain();
+ msg.arg1 = getIntent().getIntExtra(Constants.EXTRA_ARG1, -1);
+ msg.arg2 = result;
+ try {
+ mResultTo.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sending result back", e);
+ }
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestBroadcastReceiver.java b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestBroadcastReceiver.java
new file mode 100644
index 0000000..36c7a0a
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestBroadcastReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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.stubs.am;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class TestBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = "TestBroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, context.getPackageName() + " received broadcast: " + intent);
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestContentProvider.java b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestContentProvider.java
new file mode 100644
index 0000000..4fdbf1f
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestContentProvider.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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.stubs.am;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+
+public class TestContentProvider extends ContentProvider {
+ private static final String TAG = "TestContentProvider";
+ private static final boolean VERBOSE = InitService.VERBOSE;
+
+ @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() {
+ if (VERBOSE) {
+ Log.i(TAG, getContext().getPackageName() + " onCreate()");
+ }
+ return false;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestService.java b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestService.java
new file mode 100644
index 0000000..ba220e0
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/stub-app/src/com/android/stubs/am/TestService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.stubs.am;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.frameworks.perftests.am.util.Constants;
+
+public class TestService extends Service {
+ private static final String TAG = "TestService";
+ private static final boolean VERBOSE = InitService.VERBOSE;
+
+ private Binder mStub = new Binder();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " onBind()");
+ }
+ return mStub;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " onStartCommand()");
+ }
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ if (VERBOSE) {
+ Log.i(TAG, getPackageName() + " onUnbind()");
+ }
+ Messenger messenger = intent.getParcelableExtra(Constants.EXTRA_RECEIVER_CALLBACK);
+ Message msg = Message.obtain();
+ msg.what = Constants.MSG_UNBIND_DONE;
+ Bundle b = new Bundle();
+ b.putString(Constants.EXTRA_SOURCE_PACKAGE, getPackageName());
+ msg.obj = b;
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sending result back", e);
+ }
+ return false;
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
index 76c40b2..475bb82 100644
--- a/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
+++ b/tests/ActivityManagerPerfTests/tests/AndroidTest.xml
@@ -17,6 +17,9 @@
<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="test-file-name" value="ActivityManagerPerfTestsStubApp1.apk"/>
+ <option name="test-file-name" value="ActivityManagerPerfTestsStubApp2.apk"/>
+ <option name="test-file-name" value="ActivityManagerPerfTestsStubApp3.apk"/>
<option name="cleanup-apks" value="true"/>
</target_preparer>
@@ -26,4 +29,4 @@
<option name="package" value="com.android.frameworks.perftests.amtests"/>
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
</test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java
new file mode 100644
index 0000000..1d3ff06
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2019 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.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkLine;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import com.android.frameworks.perftests.am.util.AtraceUtils;
+import com.android.frameworks.perftests.am.util.TargetPackageUtils;
+import com.android.frameworks.perftests.am.util.Utils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This benchmark test basically manipulates 3 test packages, let them bind to
+ * each other, send broadcast to each other, etc. All of these actions essentially
+ * triggers OomAdjuster to update the oom_adj scores and proc state of them.
+ * In the meanwhile it'll also monitor the atrace output, extract duration between
+ * the start and exit entries of the updateOomAdjLocked, include each of them
+ * into the stats; when there are enough samples in the stats, the test will
+ * stop and output the mean/stddev time spent on the updateOomAdjLocked.
+ */
+@RunWith(JUnit4.class)
+@LargeTest
+public final class OomAdjPerfTest {
+ private static final String TAG = "OomAdjPerfTest";
+ private static final boolean VERBOSE = true;
+
+ private static final String STUB_PACKAGE1_NAME = "com.android.stubs.am1";
+ private static final String STUB_PACKAGE2_NAME = "com.android.stubs.am2";
+ private static final String STUB_PACKAGE3_NAME = "com.android.stubs.am3";
+
+ private static final Uri STUB_PACKAGE1_URI = new Uri.Builder().scheme(
+ ContentResolver.SCHEME_CONTENT).authority("com.android.stubs.am1.testapp").build();
+ private static final Uri STUB_PACKAGE2_URI = new Uri.Builder().scheme(
+ ContentResolver.SCHEME_CONTENT).authority("com.android.stubs.am2.testapp").build();
+ private static final Uri STUB_PACKAGE3_URI = new Uri.Builder().scheme(
+ ContentResolver.SCHEME_CONTENT).authority("com.android.stubs.am3.testapp").build();
+ private static final long NANOS_PER_MICROSECOND = 1000L;
+
+ private static final String ATRACE_CATEGORY = "am";
+ private static final String ATRACE_OOMADJ_PREFIX = "updateOomAdj_";
+
+ @Rule
+ public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+ private TraceMarkParser mTraceMarkParser = new TraceMarkParser(this::shouldFilterTraceLine);
+ private final ArrayList<Long> mDurations = new ArrayList<Long>();
+ private Context mContext;
+ private HandlerThread mHandlerThread;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mHandlerThread = new HandlerThread("command receiver");
+ mHandlerThread.start();
+ TargetPackageUtils.initCommandResultReceiver(mHandlerThread.getLooper());
+
+ Utils.runShellCommand("cmd deviceidle whitelist +" + STUB_PACKAGE1_NAME);
+ Utils.runShellCommand("cmd deviceidle whitelist +" + STUB_PACKAGE2_NAME);
+ Utils.runShellCommand("cmd deviceidle whitelist +" + STUB_PACKAGE3_NAME);
+ TargetPackageUtils.startStubPackage(mContext, STUB_PACKAGE1_NAME);
+ TargetPackageUtils.startStubPackage(mContext, STUB_PACKAGE2_NAME);
+ TargetPackageUtils.startStubPackage(mContext, STUB_PACKAGE3_NAME);
+ }
+
+ @After
+ public void tearDown() {
+ TargetPackageUtils.stopStubPackage(mContext, STUB_PACKAGE1_NAME);
+ TargetPackageUtils.stopStubPackage(mContext, STUB_PACKAGE2_NAME);
+ TargetPackageUtils.stopStubPackage(mContext, STUB_PACKAGE3_NAME);
+ Utils.runShellCommand("cmd deviceidle whitelist -" + STUB_PACKAGE1_NAME);
+ Utils.runShellCommand("cmd deviceidle whitelist -" + STUB_PACKAGE2_NAME);
+ Utils.runShellCommand("cmd deviceidle whitelist -" + STUB_PACKAGE3_NAME);
+ mHandlerThread.quitSafely();
+ }
+
+ @Test
+ public void testOomAdj() {
+ final AtraceUtils atraceUtils = AtraceUtils.getInstance(
+ InstrumentationRegistry.getInstrumentation());
+ final ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();
+ atraceUtils.startTrace(ATRACE_CATEGORY);
+ while (state.keepRunning(mDurations)) {
+ runCUJWithOomComputationOnce();
+
+ // Now kick off the trace dump
+ mDurations.clear();
+ atraceUtils.performDump(mTraceMarkParser, this::handleTraceMarkSlices);
+ }
+ atraceUtils.stopTrace();
+ }
+
+ private boolean shouldFilterTraceLine(final TraceMarkLine line) {
+ return line.name.startsWith(ATRACE_OOMADJ_PREFIX);
+ }
+
+ private void handleTraceMarkSlices(String key, List<TraceMarkSlice> slices) {
+ for (TraceMarkSlice slice: slices) {
+ mDurations.add(slice.getDurationInMicroseconds() * NANOS_PER_MICROSECOND);
+ }
+ }
+
+ /**
+ * This tries to mimic a user journey, involes multiple activity/service starts/stop,
+ * the time spent on oom adj computation would be different between all these samples,
+ * but with enough samples, we'll be able to know the overall distribution of the time
+ * spent on it.
+ */
+ private void runCUJWithOomComputationOnce() {
+ // Start activity from package1
+ TargetPackageUtils.startActivity(STUB_PACKAGE1_NAME, STUB_PACKAGE1_NAME);
+ // Start activity from package2
+ TargetPackageUtils.startActivity(STUB_PACKAGE2_NAME, STUB_PACKAGE2_NAME);
+ // Start activity from package3
+ TargetPackageUtils.startActivity(STUB_PACKAGE3_NAME, STUB_PACKAGE3_NAME);
+
+ // Stop activity in package1
+ TargetPackageUtils.stopActivity(STUB_PACKAGE1_NAME, STUB_PACKAGE1_NAME);
+ // Stop activity in package2
+ TargetPackageUtils.stopActivity(STUB_PACKAGE2_NAME, STUB_PACKAGE2_NAME);
+ // Stop activity in package3
+ TargetPackageUtils.stopActivity(STUB_PACKAGE3_NAME, STUB_PACKAGE3_NAME);
+
+ // Bind from package1 to package2
+ TargetPackageUtils.bindService(STUB_PACKAGE1_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE);
+ // Acquire content provider from package 1 to package3
+ TargetPackageUtils.acquireProvider(STUB_PACKAGE1_NAME, STUB_PACKAGE3_NAME,
+ STUB_PACKAGE3_URI);
+ // Start activity from package1
+ TargetPackageUtils.startActivity(STUB_PACKAGE1_NAME, STUB_PACKAGE1_NAME);
+ // Bind from package2 to package3
+ TargetPackageUtils.bindService(STUB_PACKAGE2_NAME, STUB_PACKAGE3_NAME,
+ Context.BIND_AUTO_CREATE);
+ // Unbind from package 1 to package 2
+ TargetPackageUtils.unbindService(STUB_PACKAGE1_NAME, STUB_PACKAGE2_NAME, 0);
+ // Stop activity in package1
+ TargetPackageUtils.stopActivity(STUB_PACKAGE1_NAME, STUB_PACKAGE1_NAME);
+
+ // Send broadcast to all of them
+ TargetPackageUtils.sendBroadcast(STUB_PACKAGE1_NAME, STUB_PACKAGE1_NAME);
+ TargetPackageUtils.sendBroadcast(STUB_PACKAGE2_NAME, STUB_PACKAGE2_NAME);
+ TargetPackageUtils.sendBroadcast(STUB_PACKAGE3_NAME, STUB_PACKAGE3_NAME);
+
+ // Bind from package1 to package2 again
+ TargetPackageUtils.bindService(STUB_PACKAGE1_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE);
+ // Create a cycle: package3 to package1
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE1_NAME,
+ Context.BIND_AUTO_CREATE);
+
+ // Send broadcast to all of them again
+ TargetPackageUtils.sendBroadcast(STUB_PACKAGE1_NAME, STUB_PACKAGE1_NAME);
+ TargetPackageUtils.sendBroadcast(STUB_PACKAGE2_NAME, STUB_PACKAGE2_NAME);
+ TargetPackageUtils.sendBroadcast(STUB_PACKAGE3_NAME, STUB_PACKAGE3_NAME);
+ // Start activity in package3
+ TargetPackageUtils.startActivity(STUB_PACKAGE3_NAME, STUB_PACKAGE3_NAME);
+
+ // Break the cycle: unbind from package3 to package1
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE1_NAME, 0);
+
+ // Bind from package3 to package1 with waive priority
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE1_NAME,
+ Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
+ // Release provider connection
+ TargetPackageUtils.releaseProvider(STUB_PACKAGE1_NAME, STUB_PACKAGE3_NAME,
+ STUB_PACKAGE3_URI);
+ // Unbind from package1 to package2
+ TargetPackageUtils.unbindService(STUB_PACKAGE1_NAME, STUB_PACKAGE2_NAME, 0);
+ // Unbind from package2 to packagae3
+ TargetPackageUtils.unbindService(STUB_PACKAGE2_NAME, STUB_PACKAGE3_NAME, 0);
+
+ // Bind from package3 to package2 with BIND_ABOVE_CLIENT
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE | Context.BIND_ABOVE_CLIENT);
+ // Unbind from package3 to packagae2
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME, 0);
+
+ // Bind from package3 to package2 with BIND_ALLOW_OOM_MANAGEMENT
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ // Unbind from package3 to packagae2
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME, 0);
+
+ // Bind from package3 to package2 with BIND_IMPORTANT
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
+ // Unbind from package3 to packagae2
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME, 0);
+
+ // Bind from package3 to package2 with BIND_NOT_FOREGROUND
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND);
+ // Unbind from package3 to packagae2
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME, 0);
+
+ // Bind from package3 to package2 with BIND_NOT_PERCEPTIBLE
+ TargetPackageUtils.bindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_PERCEPTIBLE);
+ // Unbind from package3 to packagae2
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE2_NAME, 0);
+
+ // Stop activity in package3
+ TargetPackageUtils.stopActivity(STUB_PACKAGE3_NAME, STUB_PACKAGE3_NAME);
+ // Unbind from package3 to package1
+ TargetPackageUtils.unbindService(STUB_PACKAGE3_NAME, STUB_PACKAGE1_NAME, 0);
+ }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/AtraceUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/AtraceUtils.java
new file mode 100644
index 0000000..fcccfce
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/AtraceUtils.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 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.Instrumentation;
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.function.BiConsumer;
+
+// Simplified version of AtraceLogger.
+public class AtraceUtils {
+ private static final String TAG = "AtraceUtils";
+ private static final boolean VERBOSE = true;
+
+ private static final String ATRACE_START = "atrace --async_start -b %d -c %s";
+ private static final String ATRACE_DUMP = "atrace --async_dump";
+ private static final String ATRACE_STOP = "atrace --async_stop";
+ private static final int DEFAULT_ATRACE_BUF_SIZE = 1024;
+
+ private UiAutomation mAutomation;
+ private static AtraceUtils sUtils = null;
+ private boolean mStarted = false;
+
+ private AtraceUtils(Instrumentation instrumentation) {
+ mAutomation = instrumentation.getUiAutomation();
+ }
+
+ public static AtraceUtils getInstance(Instrumentation instrumentation) {
+ if (sUtils == null) {
+ sUtils = new AtraceUtils(instrumentation);
+ }
+ return sUtils;
+ }
+
+ /**
+ * @param categories The list of the categories to trace, separated with space.
+ */
+ public void startTrace(String categories) {
+ synchronized (this) {
+ if (mStarted) {
+ throw new IllegalStateException("atrace already started");
+ }
+ Utils.runShellCommand(String.format(
+ ATRACE_START, DEFAULT_ATRACE_BUF_SIZE, categories));
+ mStarted = true;
+ }
+ }
+
+ public void stopTrace() {
+ synchronized (this) {
+ mStarted = false;
+ Utils.runShellCommand(ATRACE_STOP);
+ }
+ }
+
+ /**
+ * @param parser The function that can accept the buffer of atrace dump and parse it.
+ * @param handler The parse result handler
+ */
+ public void performDump(TraceMarkParser parser,
+ BiConsumer<String, List<TraceMarkSlice>> handler) {
+ parser.reset();
+ try {
+ if (VERBOSE) {
+ Log.i(TAG, "Collecting atrace dump...");
+ }
+ writeDataToBuf(mAutomation.executeShellCommand(ATRACE_DUMP), parser);
+ } catch (IOException e) {
+ Log.e(TAG, "Error in reading dump", e);
+ }
+ parser.forAllSlices(handler);
+ }
+
+ // The given file descriptor here will be closed by this function
+ private void writeDataToBuf(ParcelFileDescriptor pfDescriptor,
+ TraceMarkParser parser) throws IOException {
+ InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor);
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ parser.visit(line);
+ }
+ }
+ }
+}
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
index 046dd6b..d7f4d9d 100644
--- 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
@@ -22,12 +22,18 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.ResultReceiver;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
import org.junit.Assert;
@@ -36,6 +42,7 @@
public class TargetPackageUtils {
private static final String TAG = TargetPackageUtils.class.getSimpleName();
+ public static final boolean VERBOSE = true;
public static final String PACKAGE_NAME = "com.android.frameworks.perftests.amteststestapp";
public static final String ACTIVITY_NAME = PACKAGE_NAME + ".TestActivity";
@@ -48,6 +55,12 @@
// Cache for test app's uid, so we only have to query it once.
private static int sTestAppUid = -1;
+ private static final ArrayMap<String, ICommandReceiver> sStubPackages =
+ new ArrayMap<String, ICommandReceiver>();
+ private static final ArrayMap<Integer, CountDownLatch> sCommandLatches =
+ new ArrayMap<Integer, CountDownLatch>();
+ private static int sSeqNum = 0;
+
/**
* Kills the test package synchronously.
*/
@@ -145,5 +158,160 @@
}
}
+ private static boolean isUidRunning(int uid) {
+ return !Utils.runShellCommand(String.format("cmd activity get-uid-state %d", uid))
+ .contains("(NONEXISTENT)");
+ }
+
+ public static void startStubPackage(Context context, String pkgName) {
+ stopStubPackage(context, pkgName);
+ try {
+ Pair<Integer, CountDownLatch> pair = obtainLatch();
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(pkgName, Constants.STUB_INIT_SERVICE_NAME));
+ intent.putExtra(Constants.EXTRA_SOURCE_PACKAGE, context.getPackageName());
+ intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, sMessenger);
+ intent.putExtra(Constants.EXTRA_SEQ, pair.first);
+ context.startService(intent);
+ Assert.assertTrue("Timeout when waiting for starting package " + pkgName,
+ pair.second.await(AWAIT_SERVICE_CONNECT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void stopStubPackage(Context context, String pkgName) {
+ final PackageManager pm = context.getPackageManager();
+ try {
+ final int uid = pm.getPackageUid(pkgName, 0);
+ if (isUidRunning(uid)) {
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ am.forceStopPackage(pkgName);
+ while (isUidRunning(uid)) {
+ SystemClock.sleep(WAIT_TIME_MS);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void initCommandResultReceiver(Looper looper) {
+ if (sMessenger == null) {
+ sMessenger = new Messenger(new H(looper));
+ }
+ }
+
+ private static void onPackageStartResult(int seq, Bundle bundle) {
+ ICommandReceiver receiver = ICommandReceiver.Stub.asInterface(
+ bundle.getBinder(Constants.EXTRA_RECEIVER_CALLBACK));
+ String sourcePkg = bundle.getString(Constants.EXTRA_SOURCE_PACKAGE);
+ sStubPackages.put(sourcePkg, receiver);
+ releaseLatch(seq);
+ }
+
+ private static void onCommandResult(int seq, int result) {
+ Assert.assertTrue("Error in command seq " + seq, result == Constants.RESULT_NO_ERROR);
+ releaseLatch(seq);
+ }
+
+ private static Messenger sMessenger = null;
+ private static class H extends Handler {
+ H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case Constants.REPLY_PACKAGE_START_RESULT:
+ onPackageStartResult(msg.arg1 /* seq */, (Bundle) msg.obj);
+ break;
+ case Constants.REPLY_COMMAND_RESULT:
+ onCommandResult(msg.arg1, msg.arg2);
+ break;
+ }
+ }
+ }
+
+ private static Pair<Integer, CountDownLatch> obtainLatch() {
+ CountDownLatch latch = new CountDownLatch(1);
+ int seq;
+ synchronized (sCommandLatches) {
+ seq = sSeqNum++;
+ sCommandLatches.put(seq, latch);
+ }
+ return new Pair<>(seq, latch);
+ }
+
+ private static void releaseLatch(int seq) {
+ synchronized (sCommandLatches) {
+ CountDownLatch latch = sCommandLatches.get(seq);
+ if (latch != null) {
+ latch.countDown();
+ sCommandLatches.remove(seq);
+ }
+ }
+ }
+
+ public static void sendCommand(int command, String sourcePackage, String targetPackage,
+ int flags, Bundle bundle, boolean waitForResult) {
+ ICommandReceiver receiver = sStubPackages.get(sourcePackage);
+ Assert.assertTrue("Package hasn't been started: " + sourcePackage, receiver != null);
+ try {
+ Pair<Integer, CountDownLatch> pair = null;
+ if (waitForResult) {
+ pair = obtainLatch();
+ }
+ if (VERBOSE) {
+ Log.i(TAG, "Sending command=" + command + ", seq=" + pair.first + ", from="
+ + sourcePackage + ", to=" + targetPackage + ", flags=" + flags);
+ }
+ receiver.sendCommand(command, pair.first, sourcePackage, targetPackage, flags, bundle);
+ if (waitForResult) {
+ Assert.assertTrue("Timeout when waiting for command " + command + " from "
+ + sourcePackage + " to " + targetPackage,
+ pair.second.await(AWAIT_SERVICE_CONNECT_MS, TimeUnit.MILLISECONDS));
+ }
+ } catch (RemoteException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void bindService(String sourcePackage, String targetPackage, int flags) {
+ sendCommand(Constants.COMMAND_BIND_SERVICE, sourcePackage, targetPackage, flags, null,
+ true);
+ }
+
+ public static void unbindService(String sourcePackage, String targetPackage, int flags) {
+ sendCommand(Constants.COMMAND_UNBIND_SERVICE, sourcePackage, targetPackage, flags, null,
+ true);
+ }
+
+ public static void acquireProvider(String sourcePackage, String targetPackage, Uri uri) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Constants.EXTRA_URI, uri);
+ sendCommand(Constants.COMMAND_ACQUIRE_CONTENT_PROVIDER, sourcePackage, targetPackage, 0,
+ bundle, true);
+ }
+
+ public static void releaseProvider(String sourcePackage, String targetPackage, Uri uri) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Constants.EXTRA_URI, uri);
+ sendCommand(Constants.COMMAND_RELEASE_CONTENT_PROVIDER, sourcePackage, targetPackage, 0,
+ bundle, true);
+ }
+
+ public static void sendBroadcast(String sourcePackage, String targetPackage) {
+ sendCommand(Constants.COMMAND_SEND_BROADCAST, sourcePackage, targetPackage, 0, null, true);
+ }
+
+ public static void startActivity(String sourcePackage, String targetPackage) {
+ sendCommand(Constants.COMMAND_START_ACTIVITY, sourcePackage, targetPackage, 0, null, true);
+ }
+
+ public static void stopActivity(String sourcePackage, String targetPackage) {
+ sendCommand(Constants.COMMAND_STOP_ACTIVITY, sourcePackage, targetPackage, 0, null, true);
+ }
}
diff --git a/tests/ActivityManagerPerfTests/utils/Android.bp b/tests/ActivityManagerPerfTests/utils/Android.bp
index 300b7ea..766c3ac 100644
--- a/tests/ActivityManagerPerfTests/utils/Android.bp
+++ b/tests/ActivityManagerPerfTests/utils/Android.bp
@@ -18,6 +18,7 @@
srcs: [
"src/**/*.java",
"src/com/android/frameworks/perftests/am/util/ITimeReceiverCallback.aidl",
+ "src/com/android/frameworks/perftests/am/util/ICommandReceiver.aidl",
],
static_libs: [
"androidx.test.rules",
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
index 9b076c5..8e58665 100644
--- 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
@@ -30,4 +30,33 @@
public static final String EXTRA_RECEIVER_CALLBACK = "receiver_callback_binder";
public static final String EXTRA_LOOPER_IDLE_CALLBACK = "looper_idle_callback_binder";
+ public static final String EXTRA_SOURCE_PACKAGE = "source_package";
+ public static final String EXTRA_URI = "uri";
+ public static final String EXTRA_REQ_FINISH_ACTIVITY = "req_finish_activity";
+ public static final String EXTRA_SEQ = "seq";
+ public static final String EXTRA_ARG1 = "arg1";
+ public static final String EXTRA_ARG2 = "arg2";
+
+ public static final int RESULT_NO_ERROR = 0;
+ public static final int RESULT_ERROR = 1;
+ public static final String STUB_INIT_SERVICE_NAME = "com.android.stubs.am.InitService";
+
+ public static final int COMMAND_BIND_SERVICE = 1;
+ public static final int COMMAND_UNBIND_SERVICE = 2;
+ public static final int COMMAND_ACQUIRE_CONTENT_PROVIDER = 3;
+ public static final int COMMAND_RELEASE_CONTENT_PROVIDER = 4;
+ public static final int COMMAND_SEND_BROADCAST = 5;
+ public static final int COMMAND_START_ACTIVITY = 6;
+ public static final int COMMAND_STOP_ACTIVITY = 7;
+
+ public static final int MSG_DEFAULT = 0;
+ public static final int MSG_UNBIND_DONE = 1;
+
+ public static final int REPLY_PACKAGE_START_RESULT = 0;
+ public static final int REPLY_COMMAND_RESULT = 1;
+
+ public static final String STUB_ACTION_ACTIVITY =
+ "com.android.stubs.am.ACTION_START_TEST_ACTIVITY";
+ public static final String STUB_ACTION_BROADCAST =
+ "com.android.stubs.am.ACTION_BROADCAST_TEST";
}
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ICommandReceiver.aidl b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ICommandReceiver.aidl
new file mode 100644
index 0000000..59ea761
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/ICommandReceiver.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 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;
+
+interface ICommandReceiver {
+ oneway void sendCommand(int command, int seq, String sourcePackage, String targetPackage,
+ int flags, in Bundle bundle);
+}