diff options
author | Jing Ji <jji@google.com> | 2019-10-17 15:36:57 -0700 |
---|---|---|
committer | Jing Ji <jji@google.com> | 2019-12-13 14:51:41 -0800 |
commit | 84121313620345199b56816c579df31d68567581 (patch) | |
tree | 4bd8e5d711cabbc48f7bfdfd8db5771ff66d5e92 /tests/ActivityManagerPerfTests | |
parent | 81826ce75f7d2c507a997a041bd14d23c91fd73b (diff) |
Add performance test for OomAdjuster
Bug: 140254153
Test: atest -c ActivityManagerPerfTests:OomAdjPerfTest#testOomAdj
Change-Id: Id12667c71300e6fe4dc063c83807834bbdb5e62a
Diffstat (limited to 'tests/ActivityManagerPerfTests')
18 files changed, 1330 insertions, 2 deletions
diff --git a/tests/ActivityManagerPerfTests/stub-app/Android.bp b/tests/ActivityManagerPerfTests/stub-app/Android.bp new file mode 100644 index 000000000000..a3c1f5b2f17d --- /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 000000000000..a57f64c320c8 --- /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 000000000000..667472db5f83 --- /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 000000000000..085273574d95 --- /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 000000000000..6895d7258dad --- /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 000000000000..f79f006087d7 --- /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 000000000000..18fdc442bfbf --- /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 000000000000..f7ea35672f0a --- /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 000000000000..36c7a0a3a405 --- /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 000000000000..4fdbf1f29036 --- /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 000000000000..ba220e003203 --- /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 76c40b2e3dc6..475bb82a9856 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 000000000000..1d3ff06e6bf1 --- /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 000000000000..fcccfce8bd0e --- /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 046dd6bb7dce..d7f4d9de6735 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.Context; 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 @@ import java.util.concurrent.TimeUnit; 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 @@ public class TargetPackageUtils { // 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 @@ public class TargetPackageUtils { } } + 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 300b7ea998fa..766c3acf3c09 100644 --- a/tests/ActivityManagerPerfTests/utils/Android.bp +++ b/tests/ActivityManagerPerfTests/utils/Android.bp @@ -18,6 +18,7 @@ java_test { 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 9b076c507ff8..8e58665f5352 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 class Constants { 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 000000000000..59ea7616a59e --- /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); +} |