diff options
author | 2023-03-24 10:46:30 +1100 | |
---|---|---|
committer | 2023-04-05 16:14:34 +1000 | |
commit | f6a884076c64aa2772970805c39e8fae1e5ee914 (patch) | |
tree | 2a6c3d675a0b427385ce4e156c431523d63029d3 | |
parent | 1c715ceb84b124baa528dc7c080a89e9d87850c9 (diff) |
Add performance tests for @EnforcePermission
The performance tests reuse the service from the end-to-end tests (which
provides some basic permission methods). Two methods are added: one
without permission check and one with manual permission checks (i.e.,
calling Context.enforceCallingPermission directly).
A perfetto configuration is added (as opposed to using the default
trace_config_detailed.textproto). This configuration samples the service
and test processes at regular interval. It helps confirming the actual
call stack within the binder calls.
Bug: 269684922
Bug: 269721152
Test: atest EnforcePermissionPerfTests
Change-Id: I7c1bc6e178b083ac663df3b0372cdd22e377248e
8 files changed, 473 insertions, 0 deletions
diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl index 87aad108f31f..6e59b042d6ff 100644 --- a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl +++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl @@ -52,4 +52,10 @@ interface IProtected { @EnforcePermission(anyOf={"INTERNET", "VIBRATE"}) void ProtectedByInternetOrVibrate(); + + @RequiresNoPermission + void NotProtected(); + + @PermissionManuallyEnforced + void ManuallyProtected(); } diff --git a/tests/EnforcePermission/perf-app/Android.bp b/tests/EnforcePermission/perf-app/Android.bp new file mode 100644 index 000000000000..b494bb754370 --- /dev/null +++ b/tests/EnforcePermission/perf-app/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 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 { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "EnforcePermissionPerfTests", + srcs: [ + "src/**/*.java", + ":frameworks-enforce-permission-test-aidl", + ], + static_libs: [ + "EnforcePermissionTestLib", + "androidx.benchmark_benchmark-common", + "androidx.benchmark_benchmark-junit4", + "apct-perftests-utils", + "collector-device-lib", + "androidx.test.rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + data: [ + ":EnforcePermissionTestHelper", + ":perfetto_artifacts", + "perfetto.textproto", + ], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], +} diff --git a/tests/EnforcePermission/perf-app/AndroidManifest.xml b/tests/EnforcePermission/perf-app/AndroidManifest.xml new file mode 100644 index 000000000000..900270d27304 --- /dev/null +++ b/tests/EnforcePermission/perf-app/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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="android.tests.enforcepermission.tests"> + + <uses-permission android:name="android.permission.INTERNET" /> + + <!-- Required by perfetto --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <queries> + <package android:name="android.tests.enforcepermission.service" /> + </queries> + + <application> + <uses-library android:name="android.test.runner" /> + <profileable android:shell="true" /> + <!-- Instance of the Service within the app. This is to test performance for same-process calls. --> + <service android:name=".TestService" /> + </application> + <instrumentation android:name="androidx.benchmark.junit4.AndroidBenchmarkRunner" + android:targetPackage="android.tests.enforcepermission.tests"/> +</manifest> diff --git a/tests/EnforcePermission/perf-app/AndroidTest.xml b/tests/EnforcePermission/perf-app/AndroidTest.xml new file mode 100644 index 000000000000..3bc1d2dc2eeb --- /dev/null +++ b/tests/EnforcePermission/perf-app/AndroidTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs EnforcePermission Perf Tests"> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="perfetto.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="EnforcePermissionTestHelper.apk"/> + <option name="test-file-name" value="EnforcePermissionPerfTests.apk"/> + <option name="cleanup-apks" value="true" /> + </target_preparer> + + <option name="isolated-storage" value="false" /> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path" /> + <option name="collect-on-run-ended-only" value="false" /> + </metrics_collector> + + <option name="test-tag" value="EnforcePermissionTests"/> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="android.tests.enforcepermission.tests"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener" /> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + </test> +</configuration> diff --git a/tests/EnforcePermission/perf-app/perfetto.textproto b/tests/EnforcePermission/perf-app/perfetto.textproto new file mode 100644 index 000000000000..8a3eea4ef402 --- /dev/null +++ b/tests/EnforcePermission/perf-app/perfetto.textproto @@ -0,0 +1,154 @@ +# Copyright (C) 2023 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. + +# Based on trace_config_detailed.textproto +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 1000 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 10000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers { + size_kb: 32768 + fill_policy: RING_BUFFER +} + +# procfs polling +buffers { + size_kb: 8192 + fill_policy: RING_BUFFER +} + +# perf memory +buffers { + size_kb: 65536 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.ftrace" + target_buffer: 0 + ftrace_config { + throttle_rss_stat: true + # These parameters affect only the kernel trace buffer size and how + # frequently it gets moved into the userspace buffer defined above. + buffer_size_kb: 16384 + drain_period_ms: 250 + + # Store certain high-volume "sched" ftrace events in a denser format + # (falling back to the default format if not supported by the tracer). + compact_sched { + enabled: true + } + + # Enables symbol name resolution against /proc/kallsyms + symbolize_ksyms: true + # Parse kallsyms before acknowledging that the ftrace data source has been started. In + # combination with "perfetto --background-wait" as the consumer, it lets us defer the + # test we're tracing until after the cpu has quieted down from the cpu-bound kallsyms parsing. + initialize_ksyms_synchronously_for_testing: true + # Avoid re-parsing kallsyms on every test run, as it takes 200-500ms per run. See b/239951079 + ksyms_mem_policy: KSYMS_RETAIN + + # We need to do process tracking to ensure kernel ftrace events targeted at short-lived + # threads are associated correctly + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + ftrace_events: "sched/sched_process_exit" + ftrace_events: "sched/sched_process_free" + + # Memory events + ftrace_events: "rss_stat" + ftrace_events: "ion_heap_shrink" + ftrace_events: "ion_heap_grow" + ftrace_events: "ion/ion_stat" + ftrace_events: "dmabuf_heap/dma_heap_stat" + ftrace_events: "oom_score_adj_update" + ftrace_events: "gpu_mem/gpu_mem_total" + ftrace_events: "fastrpc/fastrpc_dma_stat" + + # Power events + ftrace_events: "power/suspend_resume" + ftrace_events: "power/cpu_frequency" + ftrace_events: "power/cpu_idle" + ftrace_events: "power/gpu_frequency" + + # Old (kernel) LMK + ftrace_events: "lowmemorykiller/lowmemory_kill" + + atrace_apps: "*" + + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "bionic" + atrace_categories: "camera" + atrace_categories: "wm" + atrace_categories: "dalvik" + atrace_categories: "sched" + atrace_categories: "freq" + atrace_categories: "gfx" + atrace_categories: "view" + atrace_categories: "webview" + atrace_categories: "input" + atrace_categories: "hal" + atrace_categories: "binder_driver" + atrace_categories: "sync" + atrace_categories: "workq" + atrace_categories: "res" + atrace_categories: "power" + + } + } +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 1 + process_stats_config { + proc_stats_poll_ms: 10000 + } + } +} + +data_sources { + config { + name: "linux.perf" + target_buffer: 2 + perf_event_config { + timebase { + frequency: 80 + } + callstack_sampling { + scope { + target_cmdline: "android.tests.enforcepermission.tests" + target_cmdline: "android.tests.enforcepermission.service" + target_cmdline: "system_server" + } + kernel_frames: true + } + } + } +} diff --git a/tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java b/tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java new file mode 100644 index 000000000000..7cbf5674c29a --- /dev/null +++ b/tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java @@ -0,0 +1,169 @@ +/** + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.tests.enforcepermission.tests; + +import static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.tests.enforcepermission.IProtected; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** Performance tests for EnforcePermission annotation. + * + * Permission check results are cached on the service side as it relies on + * PermissionManager. It means that only the first request will trigger a + * lookup to system_server. Subsequent requests will use the cached result. As + * this timing is similar to a permission check for a service hosted in + * system_server, we keep this cache active for the tests. The BenchmarkState + * used by PerfStatusReporter includes a warm-up stage. It means that the extra + * time taken by the first request will not be reflected in the outcome of the + * test. + */ +@RunWith(AndroidJUnit4.class) +public class ServicePerfTest { + + private static final String TAG = "EnforcePermission.PerfTests"; + private static final String SERVICE_PACKAGE = "android.tests.enforcepermission.service"; + private static final String LOCAL_SERVICE_PACKAGE = "android.tests.enforcepermission.tests"; + private static final int SERVICE_TIMEOUT_SEC = 5; + + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private Context mContext; + private volatile ServiceConnection mServiceConnection; + + private void bindService(Intent intent) throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + mServiceConnection = new ServiceConnection(); + assertTrue(mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)); + } + + public void bindRemoteService() throws Exception { + Log.d(TAG, "bindRemoteService"); + Intent intent = new Intent(); + intent.setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".TestService"); + bindService(intent); + } + + public void bindLocalService() throws Exception { + Log.d(TAG, "bindLocalService"); + Intent intent = new Intent(); + intent.setClassName(LOCAL_SERVICE_PACKAGE, SERVICE_PACKAGE + ".TestService"); + bindService(intent); + } + + @After + public void unbindTestService() throws Exception { + mContext.unbindService(mServiceConnection); + } + + private static final class ServiceConnection implements android.content.ServiceConnection { + private volatile CompletableFuture<IProtected> mFuture = new CompletableFuture<>(); + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mFuture.complete(IProtected.Stub.asInterface(service)); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mFuture = new CompletableFuture<>(); + } + + public IProtected get() { + try { + return mFuture.get(SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + throw new RuntimeException("Unable to reach TestService: " + e.toString()); + } + } + } + + @Test + public void testAnnotatedPermission() throws Exception { + bindRemoteService(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mServiceConnection.get().ProtectedByInternet(); + } + } + + @Test + public void testNoPermission() throws Exception { + bindRemoteService(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mServiceConnection.get().NotProtected(); + } + } + + @Test + public void testManuallyProtected() throws Exception { + bindRemoteService(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mServiceConnection.get().ManuallyProtected(); + } + } + + @Test + public void testAnnotatedPermissionLocal() + throws Exception { + bindLocalService(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mServiceConnection.get().ProtectedByInternet(); + } + } + + @Test + public void testNoPermissionLocal() throws Exception { + bindLocalService(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mServiceConnection.get().NotProtected(); + } + } + + @Test + public void testManuallyProtectedLocal() throws Exception { + bindLocalService(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mServiceConnection.get().ManuallyProtected(); + } + } +} diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp index a4ac1d7c6134..787821546018 100644 --- a/tests/EnforcePermission/service-app/Android.bp +++ b/tests/EnforcePermission/service-app/Android.bp @@ -21,6 +21,14 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +android_library { + name: "EnforcePermissionTestLib", + srcs: [ + "src/**/*.java", + ":frameworks-enforce-permission-test-aidl", + ], +} + android_test_helper_app { name: "EnforcePermissionTestHelper", srcs: [ diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java index 0a3af1af8139..8b809cf41c3e 100644 --- a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java +++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java @@ -17,6 +17,7 @@ package android.tests.enforcepermission.service; import android.annotation.EnforcePermission; +import android.annotation.RequiresNoPermission; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -172,5 +173,15 @@ public class TestService extends Service { public void ProtectedByInternetOrVibrate() { ProtectedByInternetOrVibrate_enforcePermission(); } + + @Override + @RequiresNoPermission + public void NotProtected() { + } + + @Override + public void ManuallyProtected() { + enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "access denied"); + } } } |