diff options
author | 2021-12-23 12:13:28 -0800 | |
---|---|---|
committer | 2021-12-23 12:18:31 -0800 | |
commit | 0d2fa6ff93fd77e86c741e09924ffe614a96158a (patch) | |
tree | a1b7c3330b39adad8a45f9a87810df7f1337c003 | |
parent | 9710d3a2bb48999b9c5bf9d899d232117480656c (diff) |
Remove iorap framework codes
- It's decided to remove iorap from Android.
- This CL removes iorap framework codes.
- Removing iorap daemon and git repo will follow.
Bug: 211461392
Test: build okay
Change-Id: I848f6590803dc498585f973ee9cf0c3af919793d
32 files changed, 0 insertions, 4349 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d0fee11c5f26..15399e4325a9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6582,10 +6582,6 @@ android:resource="@xml/autofill_compat_accessibility_service" /> </service> - <service android:name="com.google.android.startop.iorap.IorapForwardingService$IorapdJobServiceProxy" - android:permission="android.permission.BIND_JOB_SERVICE" > - </service> - <service android:name="com.android.server.blob.BlobStoreIdleJobService" android:permission="android.permission.BIND_JOB_SERVICE"> </service> diff --git a/services/Android.bp b/services/Android.bp index c830c226cb66..bf7252651ecc 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -93,7 +93,6 @@ filegroup { ":services.selectiontoolbar-sources", ":services.smartspace-sources", ":services.speech-sources", - ":services.startop.iorap-sources", ":services.systemcaptions-sources", ":services.translation-sources", ":services.texttospeech-sources", @@ -148,7 +147,6 @@ java_library { "services.selectiontoolbar", "services.smartspace", "services.speech", - "services.startop", "services.systemcaptions", "services.translation", "services.texttospeech", @@ -200,7 +198,6 @@ stubs_defaults { " --hide-annotation android.annotation.Hide" + " --hide InternalClasses" + // com.android.* classes are okay in this interface // TODO: remove the --hide options below - " --hide-package com.google.android.startop.iorap" + " --hide DeprecationMismatch" + " --hide HiddenTypedefConstant", visibility: ["//frameworks/base:__subpackages__"], diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 663e17bc9784..4d2614423869 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -210,8 +210,6 @@ import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; -import com.google.android.startop.iorap.IorapForwardingService; - import java.io.File; import java.io.FileDescriptor; import java.io.IOException; @@ -1614,10 +1612,6 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(PinnerService.class); t.traceEnd(); - t.traceBegin("IorapForwardingService"); - mSystemServiceManager.startService(IorapForwardingService.class); - t.traceEnd(); - if (Build.IS_DEBUGGABLE && ProfcollectForwardingService.enabled()) { t.traceBegin("ProfcollectForwardingService"); mSystemServiceManager.startService(ProfcollectForwardingService.class); diff --git a/services/startop/Android.bp b/services/startop/Android.bp deleted file mode 100644 index c56c463d0168..000000000000 --- a/services/startop/Android.bp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-MIT - // SPDX-license-identifier-Unicode-DFS - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library_static { - name: "services.startop", - defaults: ["platform_service_defaults"], - - static_libs: [ - // frameworks/base/startop/iorap - "services.startop.iorap", - ], -} diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp deleted file mode 100644 index 4fdf34cc1814..000000000000 --- a/startop/iorap/Android.bp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -filegroup { - name: "services.startop.iorap-javasources", - srcs: ["src/**/*.java"], - path: "src", - visibility: ["//visibility:private"], -} - -filegroup { - name: "services.startop.iorap-sources", - srcs: [ - ":services.startop.iorap-javasources", - ":iorap-aidl", - ], - visibility: ["//frameworks/base/services:__subpackages__"], -} - -java_library_static { - name: "services.startop.iorap", - srcs: [":services.startop.iorap-sources"], - libs: ["services.core"], -} diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/TEST_MAPPING deleted file mode 100644 index 8c9d4dfb0894..000000000000 --- a/startop/iorap/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "libiorap-java-tests" - } - ], - "imports": [ - { - "path": "system/iorap" - } - ] -} diff --git a/startop/iorap/functional_tests/Android.bp b/startop/iorap/functional_tests/Android.bp deleted file mode 100644 index 43c61551cdec..000000000000 --- a/startop/iorap/functional_tests/Android.bp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2020 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "iorap-functional-tests", - srcs: ["src/**/*.java"], - data: [":iorap-functional-test-apps"], - static_libs: [ - // Non-test dependencies - // library under test - "services.startop.iorap", - // Test Dependencies - // test android dependencies - "platform-test-annotations", - "androidx.test.rules", - "androidx.test.ext.junit", - "androidx.test.uiautomator_uiautomator", - // test framework dependencies - "truth-prebuilt", - ], - dxflags: ["--multi-dex"], - test_suites: ["device-tests"], - compile_multilib: "both", - libs: [ - "android.test.base", - "android.test.runner", - ], - certificate: "platform", - platform_apis: true, -} diff --git a/startop/iorap/functional_tests/AndroidManifest.xml b/startop/iorap/functional_tests/AndroidManifest.xml deleted file mode 100644 index 6bddc4a39577..000000000000 --- a/startop/iorap/functional_tests/AndroidManifest.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 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. ---> -<!--suppress AndroidUnknownAttribute --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.google.android.startop.iorap.tests" - android:sharedUserId="com.google.android.startop.iorap.tests.functional" - android:versionCode="1" - android:versionName="1.0" > - - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <!--suppress AndroidDomInspection --> - <instrumentation - android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.google.android.startop.iorap.tests" /> - - <!-- - 'debuggable=true' is required to properly load mockito jvmti dependencies, - otherwise it gives the following error at runtime: - - Openjdkjvmti plugin was loaded on a non-debuggable Runtime. - Plugin was loaded too late to change runtime state to DEBUGGABLE. --> - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> -</manifest> diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml deleted file mode 100644 index 31d4f6c47b11..000000000000 --- a/startop/iorap/functional_tests/AndroidTest.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 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 iorap-functional-tests."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-instrumentation" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="iorap-functional-tests.apk" /> - </target_preparer> - - <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> - - <target_preparer - class="com.android.tradefed.targetprep.DeviceSetup"> - - <!-- iorapd does not pick up the above changes until we restart it --> - <option name="run-command" value="stop iorapd" /> - - <!-- Clean up the existing iorap database. --> - <option name="run-command" value="rm -r /data/misc/iorapd/*" /> - <option name="run-command" value="sleep 1" /> - - <!-- Set system properties to enable perfetto tracing, readahead and detailed logging. --> - <option name="run-command" value="setprop iorapd.perfetto.enable true" /> - <option name="run-command" value="setprop iorapd.readahead.enable true" /> - <option name="run-command" value="setprop iorapd.log.verbose true" /> - - <option name="run-command" value="start iorapd" /> - - <!-- give it some time to restart the service; otherwise the first unit test might fail --> - <option name="run-command" value="sleep 1" /> - </target_preparer> - - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> - <option name="cleanup" value="true" /> - <option name="abort-on-push-failure" value="true" /> - <option name="push-file" - key="iorap_test_app_v1.apk" - value="/data/misc/iorapd/iorap_test_app_v1.apk" /> - <option name="push-file" - key="iorap_test_app_v2.apk" - value="/data/misc/iorapd/iorap_test_app_v2.apk" /> - <option name="push-file" - key="iorap_test_app_v3.apk" - value="/data/misc/iorapd/iorap_test_app_v3.apk" /> - </target_preparer> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.google.android.startop.iorap.tests" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <!-- test-timeout unit is ms, value = 30 min --> - <option name="test-timeout" value="1800000" /> - </test> - -</configuration> - diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java deleted file mode 100644 index 5352be6f283f..000000000000 --- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2020 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.google.android.startop.iorapd; - -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.Until; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Duration; -import java.util.ArrayList; -import java.util.concurrent.TimeUnit; -import java.util.Date; -import java.util.function.BooleanSupplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.List; -import java.text.SimpleDateFormat; - -/** - * Test for the work flow of iorap. - * - * <p> This test tests the function of iorap from: - * perfetto collection -> compilation -> prefetching -> version update -> perfetto collection. - */ -@RunWith(AndroidJUnit4.class) -public class IorapWorkFlowTest { - private static final String TAG = "IorapWorkFlowTest"; - - private static final String TEST_APP_VERSION_ONE_PATH = "/data/misc/iorapd/iorap_test_app_v1.apk"; - private static final String TEST_APP_VERSION_TWO_PATH = "/data/misc/iorapd/iorap_test_app_v2.apk"; - private static final String TEST_APP_VERSION_THREE_PATH = "/data/misc/iorapd/iorap_test_app_v3.apk"; - - private static final String DB_PATH = "/data/misc/iorapd/sqlite.db"; - private static final Duration TIMEOUT = Duration.ofSeconds(300L); - - private UiDevice mDevice; - - @Before - public void setUp() throws Exception { - // Initialize UiDevice instance - mDevice = UiDevice.getInstance(getInstrumentation()); - - // Start from the home screen - mDevice.pressHome(); - - // Wait for launcher - final String launcherPackage = mDevice.getLauncherPackageName(); - assertThat(launcherPackage, notNullValue()); - mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds()); - } - - @After - public void tearDown() throws Exception { - String packageName = "com.example.ioraptestapp"; - uninstallApk(packageName); - } - - @Test (timeout = 300000) - public void testNormalWorkFlow() throws Exception { - assertThat(mDevice, notNullValue()); - - // Install test app version one - installApk(TEST_APP_VERSION_ONE_PATH); - String packageName = "com.example.ioraptestapp"; - String activityName = "com.example.ioraptestapp.MainActivity"; - - // Perfetto trace collection phase. - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/1L)); - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/1L)); - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/1L)); - - // Trigger maintenance service for compilation. - TimeUnit.SECONDS.sleep(5L); - assertTrue(compile(packageName, activityName, /*version=*/1L)); - - // Run app with prefetching - assertTrue(startAppWithCompiledTrace( - packageName, activityName, /*version=*/1L)); - } - - @Test (timeout = 300000) - public void testUpdateApp() throws Exception { - assertThat(mDevice, notNullValue()); - - // Install test app version two, - String packageName = "com.example.ioraptestapp"; - String activityName = "com.example.ioraptestapp.MainActivity"; - installApk(TEST_APP_VERSION_TWO_PATH); - - // Perfetto trace collection phase. - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/2L)); - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/2L)); - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/2L)); - - // Trigger maintenance service for compilation. - TimeUnit.SECONDS.sleep(5L); - assertTrue(compile(packageName, activityName, /*version=*/2L)); - - // Run app with prefetching - assertTrue(startAppWithCompiledTrace( - packageName, activityName, /*version=*/2L)); - - // Update test app to version 3 - installApk(TEST_APP_VERSION_THREE_PATH); - - // Rerun app, should do pefetto tracing. - assertTrue(startAppForPerfettoTrace( - packageName, activityName, /*version=*/3L)); - } - - private static void installApk(String apkPath) throws Exception { - // Disable the selinux to allow pm install apk in the dir. - executeShellCommand("setenforce 0"); - executeShellCommand("pm install -r -d " + apkPath); - executeShellCommand("setenforce 1"); - - } - - private static void uninstallApk(String apkPath) throws Exception { - executeShellCommand("pm uninstall " + apkPath); - } - - /** - * Starts the testing app to collect the perfetto trace. - * - * @param expectPerfettoTraceCount is the expected count of perfetto traces. - */ - private boolean startAppForPerfettoTrace( - String packageName, String activityName, long version) - throws Exception { - LogcatTimestamp timestamp = runAppOnce(packageName, activityName); - return waitForPerfettoTraceSavedFromLogcat( - packageName, activityName, version, timestamp); - } - - private boolean startAppWithCompiledTrace( - String packageName, String activityName, long version) - throws Exception { - LogcatTimestamp timestamp = runAppOnce(packageName, activityName); - return waitForPrefetchingFromLogcat( - packageName, activityName, version, timestamp); - } - - private LogcatTimestamp runAppOnce(String packageName, String activityName) throws Exception { - // Close the specified app if it's open - closeApp(packageName); - LogcatTimestamp timestamp = new LogcatTimestamp(); - // Launch the specified app - startApp(packageName, activityName); - // Wait for the app to appear - mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), TIMEOUT.getSeconds()); - return timestamp; - } - - // Invokes the maintenance to compile the perfetto traces to compiled trace. - private boolean compile( - String packageName, String activityName, long version) throws Exception { - // The job id (283673059) is defined in class IorapForwardingService. - executeShellCommandViaTmpFile("cmd jobscheduler run -f android 283673059"); - return waitForFileExistence(getCompiledTracePath(packageName, activityName, version)); - } - - private String getCompiledTracePath( - String packageName, String activityName, long version) { - return String.format( - "/data/misc/iorapd/%s/%d/%s/compiled_traces/compiled_trace.pb", - packageName, version, activityName); - } - - /** - * Starts the testing app. - */ - private void startApp(String packageName, String activityName) throws Exception { - executeShellCommandViaTmpFile( - String.format("am start %s/%s", packageName, activityName)); - } - - /** - * Closes the testing app. - * <p> Keep trying to kill the process of the app until no process of the app package - * appears.</p> - */ - private void closeApp(String packageName) throws Exception { - while (true) { - String pid = executeShellCommand("pidof " + packageName); - if (pid.isEmpty()) { - Log.i(TAG, "Closed app " + packageName); - return; - } - executeShellCommand("kill -9 " + pid); - TimeUnit.SECONDS.sleep(1L); - } - } - - /** Waits for a file to appear. */ - private boolean waitForFileExistence(String fileName) throws Exception { - return retryWithTimeout(TIMEOUT, () -> { - try { - String fileExists = executeShellCommandViaTmpFile( - String.format("test -f %s; echo $?", fileName)); - Log.i(TAG, fileName + " existence is " + fileExists); - return fileExists.trim().equals("0"); - } catch (Exception e) { - Log.i(TAG, e.getMessage()); - return false; - } - }); - } - - /** Waits for the perfetto trace saved message from logcat. */ - private boolean waitForPerfettoTraceSavedFromLogcat( - String packageName, String activityName, long version, LogcatTimestamp timestamp) - throws Exception { - Pattern p = Pattern.compile(".*" - + getPerfettoTraceSavedIndicator(packageName, activityName, version) - + "(.*[.]perfetto_trace[.]pb)\n.*", Pattern.DOTALL); - - return retryWithTimeout(TIMEOUT, () -> { - try { - String log = timestamp.getLogcatAfter(); - Matcher m = p.matcher(log); - Log.d(TAG, "Tries to find perfetto trace..."); - if (!m.matches()) { - Log.i(TAG, "Cannot find perfetto trace saved in log."); - return false; - } - String filePath = m.group(1); - Log.i(TAG, "Perfetto trace is saved to " + filePath); - return true; - } catch(Exception e) { - Log.e(TAG, e.getMessage()); - return false; - } - }); - } - - private String getPerfettoTraceSavedIndicator( - String packageName, String activityName, long version) { - return String.format( - "Perfetto TraceBuffer saved to file: /data/misc/iorapd/%s/%d/%s/raw_traces/", - packageName, version, activityName); - } - - /** - * Waits for the prefetching log in the logcat. - * - * <p> When prefetching works, the perfetto traces should not be collected. </p> - */ - private boolean waitForPrefetchingFromLogcat( - String packageName, String activityName, long version, LogcatTimestamp timestamp) - throws Exception { - Pattern p = Pattern.compile( - ".*" + getReadaheadIndicator(packageName, activityName, version) + - ".*Total File Paths=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n" - + ".*Total Entries=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n" - + ".*Total Bytes=(\\d+) \\(good: (\\d+[.]?\\d*)%\\).*", - Pattern.DOTALL); - - return retryWithTimeout(TIMEOUT, () -> { - try { - String log = timestamp.getLogcatAfter(); - Matcher m = p.matcher(log); - if (!m.matches()) { - Log.i(TAG, "Cannot find readahead log."); - return false; - } - - int totalFilePath = Integer.parseInt(m.group(1)); - float totalFilePathGoodRate = Float.parseFloat(m.group(2)) / 100; - int totalEntries = Integer.parseInt(m.group(3)); - float totalEntriesGoodRate = Float.parseFloat(m.group(4)) / 100; - int totalBytes = Integer.parseInt(m.group(5)); - float totalBytesGoodRate = Float.parseFloat(m.group(6)) / 100; - - Log.i(TAG, String.format( - "totalFilePath: %d (good %.2f) totalEntries: %d (good %.2f) totalBytes: %d (good %.2f)", - totalFilePath, totalFilePathGoodRate, totalEntries, totalEntriesGoodRate, totalBytes, - totalBytesGoodRate)); - - return totalFilePath > 0 && - totalEntries > 0 && - totalBytes > 0 && - totalFilePathGoodRate > 0.5 && - totalEntriesGoodRate > 0.5 && - totalBytesGoodRate > 0.5; - } catch(Exception e) { - return false; - } - }); - } - - private static String getReadaheadIndicator( - String packageName, String activityName, long version) { - return String.format( - "Description = /data/misc/iorapd/%s/%d/%s/compiled_traces/compiled_trace.pb", - packageName, version, activityName); - } - - /** Retry until timeout. */ - private boolean retryWithTimeout(Duration timeout, BooleanSupplier supplier) throws Exception { - long totalSleepTimeSeconds = 0L; - long sleepIntervalSeconds = 2L; - while (true) { - if (supplier.getAsBoolean()) { - return true; - } - TimeUnit.SECONDS.sleep(sleepIntervalSeconds); - totalSleepTimeSeconds += sleepIntervalSeconds; - if (totalSleepTimeSeconds > timeout.getSeconds()) { - return false; - } - } - } - - /** - * Executes command in adb shell via a tmp file. - * - * <p> This should be run as root.</p> - */ - private static String executeShellCommandViaTmpFile(String cmd) throws Exception { - Log.i(TAG, "Execute via tmp file: " + cmd); - Path tmp = null; - try { - tmp = Files.createTempFile(/*prefix=*/null, /*suffix=*/".sh"); - Files.write(tmp, cmd.getBytes(StandardCharsets.UTF_8)); - tmp.toFile().setExecutable(true); - return UiDevice.getInstance( - InstrumentationRegistry.getInstrumentation()). - executeShellCommand(tmp.toString()); - } finally { - if (tmp != null) { - Files.delete(tmp); - } - } - } - - /** - * Executes command in adb shell. - * - * <p> This should be run as root.</p> - */ - private static String executeShellCommand(String cmd) throws Exception { - Log.i(TAG, "Execute: " + cmd); - return UiDevice.getInstance( - InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); - } - - static class LogcatTimestamp { - private String epochTime; - - public LogcatTimestamp() throws Exception{ - long currentTimeMillis = System.currentTimeMillis(); - epochTime = String.format( - "%d.%03d", currentTimeMillis/1000, currentTimeMillis%1000); - Log.i(TAG, "Current logcat timestamp is " + epochTime); - } - - // For example, 1585264100.000 - public String getEpochTime() { - return epochTime; - } - - // Gets the logcat after this epoch time. - public String getLogcatAfter() throws Exception { - return executeShellCommandViaTmpFile( - "logcat -v epoch -t '" + epochTime + "'"); - } - } -} - diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java deleted file mode 100644 index 1d38f4c1e23d..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Provide a hint to iorapd that an activity has transitioned state.<br /><br /> - * - * Knowledge of when an activity starts/stops can be used by iorapd to increase system - * performance (e.g. by launching perfetto tracing to record an io profile, or by - * playing back an ioprofile via readahead) over the long run.<br /><br /> - * - * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br /> - * - * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type. - * All other states could be terminal, see below: <br /><br /> - * - * <pre> - * - * ┌──────────────────────────────────────┐ - * │ ▼ - * ┌─────────┐ ╔════════════════╗ ╔═══════════╗ - * ──▶ │ STARTED │ ──▶ ║ COMPLETED ║ ──▶ ║ CANCELLED ║ - * └─────────┘ ╚════════════════╝ ╚═══════════╝ - * │ - * │ - * ▼ - * ╔════════════════╗ - * ║ POST_COMPLETED ║ - * ╚════════════════╝ - * - * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot --> - * - * @hide - */ -public class ActivityHintEvent implements Parcelable { - - public static final int TYPE_STARTED = 0; - public static final int TYPE_CANCELLED = 1; - public static final int TYPE_COMPLETED = 2; - public static final int TYPE_POST_COMPLETED = 3; - private static final int TYPE_MAX = TYPE_POST_COMPLETED; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_STARTED, - TYPE_CANCELLED, - TYPE_COMPLETED, - TYPE_POST_COMPLETED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - public final ActivityInfo activityInfo; - - public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) { - this.type = type; - this.activityInfo = activityInfo; - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - Objects.requireNonNull(activityInfo, "activityInfo"); - } - - @Override - public String toString() { - return String.format("{type: %d, activityInfo: %s}", type, activityInfo); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof ActivityHintEvent) { - return equals((ActivityHintEvent) other); - } - return false; - } - - private boolean equals(ActivityHintEvent other) { - return type == other.type && - Objects.equals(activityInfo, other.activityInfo); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - activityInfo.writeToParcel(out, flags); - } - - private ActivityHintEvent(Parcel in) { - this.type = in.readInt(); - this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in); - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<ActivityHintEvent> CREATOR - = new Parcelable.Creator<ActivityHintEvent>() { - public ActivityHintEvent createFromParcel(Parcel in) { - return new ActivityHintEvent(in); - } - - public ActivityHintEvent[] newArray(int size) { - return new ActivityHintEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java deleted file mode 100644 index f47a42cffdd8..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import java.util.Objects; - -import android.os.Parcelable; -import android.os.Parcel; - -/** - * Provide minimal information for launched activities to iorap.<br /><br /> - * - * This uniquely identifies a system-wide activity by providing the {@link #packageName} and - * {@link #activityName}. - * - * @see ActivityHintEvent - * @see AppIntentEvent - * - * @hide - */ -public class ActivityInfo implements Parcelable { - - /** The name of the package, for example {@code com.android.calculator}. */ - public final String packageName; - /** The name of the activity, for example {@code .activities.activity.MainActivity} */ - public final String activityName; - - public ActivityInfo(String packageName, String activityName) { - this.packageName = packageName; - this.activityName = activityName; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - Objects.requireNonNull(packageName, "packageName"); - Objects.requireNonNull(activityName, "activityName"); - } - - @Override - public String toString() { - return String.format("{packageName: %s, activityName: %s}", packageName, activityName); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof ActivityInfo) { - return equals((ActivityInfo) other); - } - return false; - } - - private boolean equals(ActivityInfo other) { - return Objects.equals(packageName, other.packageName) && - Objects.equals(activityName, other.activityName); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(packageName); - out.writeString(activityName); - } - - private ActivityInfo(Parcel in) { - packageName = in.readString(); - activityName = in.readString(); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<ActivityInfo> CREATOR - = new Parcelable.Creator<ActivityInfo>() { - public ActivityInfo createFromParcel(Parcel in) { - return new ActivityInfo(in); - } - - public ActivityInfo[] newArray(int size) { - return new ActivityInfo[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java deleted file mode 100644 index 1cd37b5546b9..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br /> - * - * Intent defaults provide a mechanism for an app to register itself as an automatic handler. - * For example the camera app might be registered as the default handler for - * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently, - * if an arbitrary other app requests for a still image camera photo to be taken, the system - * will launch the respective default camera app to be launched to handle that request.<br /><br /> - * - * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of - * applications that resolve from the default intent. If the application would now be resolved - * differently, iorapd would unpin the old application and pin the new application.<br /><br /> - * - * @hide - */ -public class AppIntentEvent implements Parcelable { - - /** @see android.content.Intent#CATEGORY_DEFAULT */ - public static final int TYPE_DEFAULT_INTENT_CHANGED = 0; - private static final int TYPE_MAX = 0; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_DEFAULT_INTENT_CHANGED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - - public final ActivityInfo oldActivityInfo; - public final ActivityInfo newActivityInfo; - - // TODO: Probably need the corresponding action here as well. - - public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo, - ActivityInfo newActivityInfo) { - return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo, - newActivityInfo); - } - - private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo, - ActivityInfo newActivityInfo) { - this.type = type; - this.oldActivityInfo = oldActivityInfo; - this.newActivityInfo = newActivityInfo; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - Objects.requireNonNull(oldActivityInfo, "oldActivityInfo"); - Objects.requireNonNull(oldActivityInfo, "newActivityInfo"); - } - - @Override - public String toString() { - return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo, - newActivityInfo); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof AppIntentEvent) { - return equals((AppIntentEvent) other); - } - return false; - } - - private boolean equals(AppIntentEvent other) { - return type == other.type && - Objects.equals(oldActivityInfo, other.oldActivityInfo) && - Objects.equals(newActivityInfo, other.newActivityInfo); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - oldActivityInfo.writeToParcel(out, flags); - newActivityInfo.writeToParcel(out, flags); - } - - private AppIntentEvent(Parcel in) { - this.type = in.readInt(); - this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in); - this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<AppIntentEvent> CREATOR - = new Parcelable.Creator<AppIntentEvent>() { - public AppIntentEvent createFromParcel(Parcel in) { - return new AppIntentEvent(in); - } - - public AppIntentEvent[] newArray(int size) { - return new AppIntentEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java deleted file mode 100644 index 8263e0af4422..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.annotation.LongDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.proto.ProtoOutputStream; - -import com.android.server.wm.ActivityMetricsLaunchObserver; -import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; -import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.Objects; - -/** - * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br /> - * - * Knowledge of when an activity starts/stops can be used by iorapd to increase system - * performance (e.g. by launching perfetto tracing to record an io profile, or by - * playing back an ioprofile via readahead) over the long run.<br /><br /> - * - * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br /> - * @see com.android.server.wm.ActivityMetricsLaunchObserver - * ActivityMetricsLaunchObserver for the possible event states. - * @hide - */ -public abstract class AppLaunchEvent implements Parcelable { - @LongDef - @Retention(RetentionPolicy.SOURCE) - public @interface SequenceId {} - - public final @SequenceId - long sequenceId; - - protected AppLaunchEvent(@SequenceId long sequenceId) { - this.sequenceId = sequenceId; - } - - @Override - public boolean equals(Object other) { - if (other instanceof AppLaunchEvent) { - return equals((AppLaunchEvent) other); - } - return false; - } - - protected boolean equals(AppLaunchEvent other) { - return sequenceId == other.sequenceId; - } - - - @Override - public String toString() { - return getClass().getSimpleName() + - "{" + "sequenceId=" + Long.toString(sequenceId) + - toStringBody() + "}"; - } - - protected String toStringBody() { return ""; }; - - // List of possible variants: - - public static final class IntentStarted extends AppLaunchEvent { - @NonNull - public final Intent intent; - public final long timestampNs; - - public IntentStarted(@SequenceId long sequenceId, - Intent intent, - long timestampNs) { - super(sequenceId); - this.intent = intent; - this.timestampNs = timestampNs; - - Objects.requireNonNull(intent, "intent"); - } - - @Override - public boolean equals(Object other) { - if (other instanceof IntentStarted) { - return intent.equals(((IntentStarted)other).intent) && - timestampNs == ((IntentStarted)other).timestampNs && - super.equals(other); - } - return false; - } - - @Override - protected String toStringBody() { - return ", intent=" + intent.toString() + - " , timestampNs=" + Long.toString(timestampNs); - } - - - @Override - protected void writeToParcelImpl(Parcel p, int flags) { - super.writeToParcelImpl(p, flags); - IntentProtoParcelable.write(p, intent, flags); - p.writeLong(timestampNs); - } - - IntentStarted(Parcel p) { - super(p); - intent = IntentProtoParcelable.create(p); - timestampNs = p.readLong(); - } - } - - public static final class IntentFailed extends AppLaunchEvent { - public IntentFailed(@SequenceId long sequenceId) { - super(sequenceId); - } - - @Override - public boolean equals(Object other) { - if (other instanceof IntentFailed) { - return super.equals(other); - } - return false; - } - - IntentFailed(Parcel p) { - super(p); - } - } - - public static abstract class BaseWithActivityRecordData extends AppLaunchEvent { - public final @NonNull - @ActivityRecordProto byte[] activityRecordSnapshot; - - protected BaseWithActivityRecordData(@SequenceId long sequenceId, - @NonNull @ActivityRecordProto byte[] snapshot) { - super(sequenceId); - activityRecordSnapshot = snapshot; - - Objects.requireNonNull(snapshot, "snapshot"); - } - - @Override - public boolean equals(Object other) { - if (other instanceof BaseWithActivityRecordData) { - return (Arrays.equals(activityRecordSnapshot, - ((BaseWithActivityRecordData)other).activityRecordSnapshot)) && - super.equals(other); - } - return false; - } - - @Override - protected String toStringBody() { - return ", " + new String(activityRecordSnapshot); - } - - @Override - protected void writeToParcelImpl(Parcel p, int flags) { - super.writeToParcelImpl(p, flags); - ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags); - } - - BaseWithActivityRecordData(Parcel p) { - super(p); - activityRecordSnapshot = ActivityRecordProtoParcelable.create(p); - } - } - - public static final class ActivityLaunched extends BaseWithActivityRecordData { - public final @ActivityMetricsLaunchObserver.Temperature - int temperature; - - public ActivityLaunched(@SequenceId long sequenceId, - @NonNull @ActivityRecordProto byte[] snapshot, - @ActivityMetricsLaunchObserver.Temperature int temperature) { - super(sequenceId, snapshot); - this.temperature = temperature; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ActivityLaunched) { - return temperature == ((ActivityLaunched)other).temperature && - super.equals(other); - } - return false; - } - - @Override - protected String toStringBody() { - return super.toStringBody() + ", temperature=" + Integer.toString(temperature); - } - - @Override - protected void writeToParcelImpl(Parcel p, int flags) { - super.writeToParcelImpl(p, flags); - p.writeInt(temperature); - } - - ActivityLaunched(Parcel p) { - super(p); - temperature = p.readInt(); - } - } - - public static final class ActivityLaunchFinished extends BaseWithActivityRecordData { - public final long timestampNs; - - public ActivityLaunchFinished(@SequenceId long sequenceId, - @NonNull @ActivityRecordProto byte[] snapshot, - long timestampNs) { - super(sequenceId, snapshot); - this.timestampNs = timestampNs; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ActivityLaunchFinished) { - return timestampNs == ((ActivityLaunchFinished)other).timestampNs && - super.equals(other); - } - return false; - } - - @Override - protected String toStringBody() { - return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs); - } - - @Override - protected void writeToParcelImpl(Parcel p, int flags) { - super.writeToParcelImpl(p, flags); - p.writeLong(timestampNs); - } - - ActivityLaunchFinished(Parcel p) { - super(p); - timestampNs = p.readLong(); - } - } - - public static class ActivityLaunchCancelled extends AppLaunchEvent { - public final @Nullable @ActivityRecordProto byte[] activityRecordSnapshot; - - public ActivityLaunchCancelled(@SequenceId long sequenceId, - @Nullable @ActivityRecordProto byte[] snapshot) { - super(sequenceId); - activityRecordSnapshot = snapshot; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ActivityLaunchCancelled) { - return Arrays.equals(activityRecordSnapshot, - ((ActivityLaunchCancelled)other).activityRecordSnapshot) && - super.equals(other); - } - return false; - } - - @Override - protected String toStringBody() { - return super.toStringBody() + ", " + new String(activityRecordSnapshot); - } - - @Override - protected void writeToParcelImpl(Parcel p, int flags) { - super.writeToParcelImpl(p, flags); - if (activityRecordSnapshot != null) { - p.writeBoolean(true); - ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags); - } else { - p.writeBoolean(false); - } - } - - ActivityLaunchCancelled(Parcel p) { - super(p); - if (p.readBoolean()) { - activityRecordSnapshot = ActivityRecordProtoParcelable.create(p); - } else { - activityRecordSnapshot = null; - } - } - } - - public static final class ReportFullyDrawn extends BaseWithActivityRecordData { - public final long timestampNs; - - public ReportFullyDrawn(@SequenceId long sequenceId, - @NonNull @ActivityRecordProto byte[] snapshot, - long timestampNs) { - super(sequenceId, snapshot); - this.timestampNs = timestampNs; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ReportFullyDrawn) { - return timestampNs == ((ReportFullyDrawn)other).timestampNs && - super.equals(other); - } - return false; - } - - @Override - protected String toStringBody() { - return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs); - } - - @Override - protected void writeToParcelImpl(Parcel p, int flags) { - super.writeToParcelImpl(p, flags); - p.writeLong(timestampNs); - } - - ReportFullyDrawn(Parcel p) { - super(p); - timestampNs = p.readLong(); - } - } - - @Override - public @ContentsFlags int describeContents() { return 0; } - - @Override - public void writeToParcel(Parcel p, @WriteFlags int flags) { - p.writeInt(getTypeIndex()); - - writeToParcelImpl(p, flags); - } - - - public static Creator<AppLaunchEvent> CREATOR = - new Creator<AppLaunchEvent>() { - @Override - public AppLaunchEvent createFromParcel(Parcel source) { - int typeIndex = source.readInt(); - - Class<?> kls = getClassFromTypeIndex(typeIndex); - if (kls == null) { - throw new IllegalArgumentException("Invalid type index: " + typeIndex); - } - - try { - return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source); - } catch (InstantiationException e) { - throw new AssertionError(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } catch (InvocationTargetException e) { - throw new AssertionError(e); - } catch (NoSuchMethodException e) { - throw new AssertionError(e); - } - } - - @Override - public AppLaunchEvent[] newArray(int size) { - return new AppLaunchEvent[0]; - } - }; - - protected void writeToParcelImpl(Parcel p, int flags) { - p.writeLong(sequenceId); - } - - protected AppLaunchEvent(Parcel p) { - sequenceId = p.readLong(); - } - - private int getTypeIndex() { - for (int i = 0; i < sTypes.length; ++i) { - if (sTypes[i].equals(this.getClass())) { - return i; - } - } - throw new AssertionError("sTypes did not include this type: " + this.getClass()); - } - - private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) { - if (typeIndex >= 0 && typeIndex < sTypes.length) { - return sTypes[typeIndex]; - } - return null; - } - - // Index position matters: It is used to encode the specific type in parceling. - // Keep up-to-date with C++ side. - private static Class<?>[] sTypes = new Class[] { - IntentStarted.class, - IntentFailed.class, - ActivityLaunched.class, - ActivityLaunchFinished.class, - ActivityLaunchCancelled.class, - ReportFullyDrawn.class, - }; - - public static class ActivityRecordProtoParcelable { - public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot, - int flags) { - p.writeByteArray(activityRecordSnapshot); - } - - public static @ActivityRecordProto byte[] create(Parcel p) { - byte[] data = p.createByteArray(); - - return data; - } - } - - public static class IntentProtoParcelable { - private static final int INTENT_PROTO_CHUNK_SIZE = 1024; - - public static void write(Parcel p, @NonNull Intent intent, int flags) { - // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream, - // so create a new one every time. - final ProtoOutputStream protoOutputStream = - new ProtoOutputStream(INTENT_PROTO_CHUNK_SIZE); - // Write this data out as the top-most IntentProto (i.e. it is not a sub-object). - intent.dumpDebug(protoOutputStream); - final byte[] bytes = protoOutputStream.getBytes(); - - p.writeByteArray(bytes); - } - - // TODO: Should be mockable for testing? - // We cannot deserialize in the platform because we don't have a 'readFromProto' - // code. - public static @NonNull Intent create(Parcel p) { - // This will "read" the correct amount of data, but then we discard it. - byte[] data = p.createByteArray(); - - // Never called by real code in a platform, this binder API is implemented only in C++. - return new Intent("<cannot deserialize IntentProto>"); - } - } -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java deleted file mode 100644 index 34aedd7685d8..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.startop.iorap; - -/** - * Convenience short-hand to throw {@link IllegalAccessException} when the arguments - * are out-of-range. - */ -public class CheckHelpers { - /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */ - public static void checkTypeInRange(int type, int maxValue) { - if (type < 0) { - throw new IllegalArgumentException( - String.format("type must be non-negative (value=%d)", type)); - } - if (type > maxValue) { - throw new IllegalArgumentException( - String.format("type out of range (value=%d, max=%d)", type, maxValue)); - } - } - - /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */ - public static void checkStateInRange(int state, int maxValue) { - if (state < 0) { - throw new IllegalArgumentException( - String.format("state must be non-negative (value=%d)", state)); - } - if (state > maxValue) { - throw new IllegalArgumentException( - String.format("state out of range (value=%d, max=%d)", state, maxValue)); - } - } -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java b/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java deleted file mode 100644 index 72c5eaa84c96..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/DexOptEvent.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2020 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.google.android.startop.iorap; - -import android.annotation.NonNull; -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Notifications for iorapd specifying when a package is updated by dexopt service.<br /><br /> - * - * @hide - */ -public class DexOptEvent implements Parcelable { - public static final int TYPE_PACKAGE_UPDATE = 0; - private static final int TYPE_MAX = 0; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_PACKAGE_UPDATE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - public final String packageName; - - @NonNull - public static DexOptEvent createPackageUpdate(String packageName) { - return new DexOptEvent(TYPE_PACKAGE_UPDATE, packageName); - } - - private DexOptEvent(@Type int type, String packageName) { - this.type = type; - this.packageName = packageName; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - Objects.requireNonNull(packageName, "packageName"); - } - - @Override - public String toString() { - return String.format("{DexOptEvent: packageName: %s}", packageName); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof DexOptEvent) { - return equals((DexOptEvent) other); - } - return false; - } - - private boolean equals(DexOptEvent other) { - return packageName.equals(other.packageName); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - out.writeString(packageName); - } - - private DexOptEvent(Parcel in) { - this.type = in.readInt(); - this.packageName = in.readString(); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<DexOptEvent> CREATOR - = new Parcelable.Creator<DexOptEvent>() { - public DexOptEvent createFromParcel(Parcel in) { - return new DexOptEvent(in); - } - - public DexOptEvent[] newArray(int size) { - return new DexOptEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java deleted file mode 100644 index dcaff26b79c6..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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.google.android.startop.iorap; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Intent; -import android.util.Log; - -import com.android.server.wm.ActivityMetricsLaunchObserver; - -import java.io.StringWriter; -import java.io.PrintWriter; - -/** - * A validator to check the correctness of event sequence during app startup. - * - * <p> A valid state transition of event sequence is shown as the following: - * - * <pre> - * - * +--------------------+ - * | | - * | INIT | - * | | - * +--------------------+ - * | - * | - * ↓ - * +--------------------+ - * | | - * +-------------------| INTENT_STARTED | ←--------------------------------+ - * | | | | - * | +--------------------+ | - * | | | - * | | | - * ↓ ↓ | - * +--------------------+ +--------------------+ | - * | | | | | - * | INTENT_FAILED | | ACTIVITY_LAUNCHED |------------------+ | - * | | | | | | - * +--------------------+ +--------------------+ | | - * | | | | - * | ↓ ↓ | - * | +--------------------+ +--------------------+ | - * | | | | | | - * +------------------ | ACTIVITY_FINISHED | | ACTIVITY_CANCELLED | | - * | | | | | | - * | +--------------------+ +--------------------+ | - * | | | | - * | | | | - * | ↓ | | - * | +--------------------+ | | - * | | | | | - * | | REPORT_FULLY_DRAWN | | | - * | | | | | - * | +--------------------+ | | - * | | | | - * | | | | - * | ↓ | | - * | +--------------------+ | | - * | | | | | - * +-----------------→ | END |←-----------------+ | - * | | | - * +--------------------+ | - * | | - * | | - * | | - * +--------------------------------------------- - * - * <p> END is not a real state in implementation. All states that points to END directly - * could transition to INTENT_STARTED. - * - * <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state - * could be accumulated, because during the UNKNOWN state more IntentStarted may - * be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted - * should termniate. - * - * <p> During UNKNOWN state, each IntentStarted increases the accumulation, and any of - * IntentFailed, ActivityLaunchCancelled and ActivityFinished decreases the accumulation. - * ReportFullyDrawn doesn't impact the accumulation. - */ -public class EventSequenceValidator implements ActivityMetricsLaunchObserver { - static final String TAG = "EventSequenceValidator"; - /** $> adb shell 'setprop log.tag.EventSequenceValidator VERBOSE' */ - public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private State state = State.INIT; - private long accIntentStartedEvents = 0; - - @Override - public void onIntentStarted(@NonNull Intent intent, long timestampNs) { - if (state == State.UNKNOWN) { - logWarningWithStackTrace("IntentStarted during UNKNOWN. " + intent); - incAccIntentStartedEvents(); - return; - } - - if (state != State.INIT && - state != State.INTENT_FAILED && - state != State.ACTIVITY_CANCELLED && - state != State.ACTIVITY_FINISHED && - state != State.REPORT_FULLY_DRAWN) { - logWarningWithStackTrace( - String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED)); - incAccIntentStartedEvents(); - incAccIntentStartedEvents(); - return; - } - - Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED)); - state = State.INTENT_STARTED; - } - - @Override - public void onIntentFailed() { - if (state == State.UNKNOWN) { - logWarningWithStackTrace("onIntentFailed during UNKNOWN."); - decAccIntentStartedEvents(); - return; - } - if (state != State.INTENT_STARTED) { - logWarningWithStackTrace( - String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED)); - incAccIntentStartedEvents(); - return; - } - - Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED)); - state = State.INTENT_FAILED; - } - - @Override - public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, - @Temperature int temperature) { - if (state == State.UNKNOWN) { - logWarningWithStackTrace("onActivityLaunched during UNKNOWN."); - return; - } - if (state != State.INTENT_STARTED) { - logWarningWithStackTrace( - String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED)); - incAccIntentStartedEvents(); - return; - } - - Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_LAUNCHED)); - state = State.ACTIVITY_LAUNCHED; - } - - @Override - public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { - if (state == State.UNKNOWN) { - logWarningWithStackTrace("onActivityLaunchCancelled during UNKNOWN."); - decAccIntentStartedEvents(); - return; - } - if (state != State.ACTIVITY_LAUNCHED) { - logWarningWithStackTrace( - String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED)); - incAccIntentStartedEvents(); - return; - } - - Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_CANCELLED)); - state = State.ACTIVITY_CANCELLED; - } - - @Override - public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity, - long timestampNs) { - if (state == State.UNKNOWN) { - logWarningWithStackTrace("onActivityLaunchFinished during UNKNOWN."); - decAccIntentStartedEvents(); - return; - } - - if (state != State.ACTIVITY_LAUNCHED) { - logWarningWithStackTrace( - String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED)); - incAccIntentStartedEvents(); - return; - } - - Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_FINISHED)); - state = State.ACTIVITY_FINISHED; - } - - @Override - public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, - long timestampNs) { - if (state == State.UNKNOWN) { - logWarningWithStackTrace("onReportFullyDrawn during UNKNOWN."); - return; - } - if (state == State.INIT) { - return; - } - - if (state != State.ACTIVITY_FINISHED) { - logWarningWithStackTrace( - String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN)); - return; - } - - Log.i(TAG, String.format("Transition from %s to %s", state, State.REPORT_FULLY_DRAWN)); - state = State.REPORT_FULLY_DRAWN; - } - - enum State { - INIT, - INTENT_STARTED, - INTENT_FAILED, - ACTIVITY_LAUNCHED, - ACTIVITY_CANCELLED, - ACTIVITY_FINISHED, - REPORT_FULLY_DRAWN, - UNKNOWN, - } - - private void incAccIntentStartedEvents() { - if (accIntentStartedEvents < 0) { - throw new AssertionError("The number of unknowns cannot be negative"); - } - if (accIntentStartedEvents == 0) { - state = State.UNKNOWN; - } - ++accIntentStartedEvents; - Log.i(TAG, - String.format("inc AccIntentStartedEvents to %d", accIntentStartedEvents)); - } - - private void decAccIntentStartedEvents() { - if (accIntentStartedEvents <= 0) { - throw new AssertionError("The number of unknowns cannot be negative"); - } - if(accIntentStartedEvents == 1) { - state = State.INIT; - } - --accIntentStartedEvents; - Log.i(TAG, - String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents)); - } - - private void logWarningWithStackTrace(String log) { - if (DEBUG) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw); - Log.wtf(TAG, String.format("%s\n%s", log, sw)); - } - } -} - diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java deleted file mode 100644 index 77046f2276e9..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java +++ /dev/null @@ -1,806 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; -// TODO: rename to com.android.server.startop.iorap - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.IBinder.DeathRecipient; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemProperties; -import android.provider.DeviceConfig; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.IoThread; -import com.android.server.LocalServices; -import com.android.server.SystemService; -import com.android.server.pm.BackgroundDexOptService; -import com.android.server.pm.PackageManagerService; -import com.android.server.wm.ActivityMetricsLaunchObserver; -import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; -import com.android.server.wm.ActivityTaskManagerInternal; - -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.BooleanSupplier; - -/** - * System-server-local proxy into the {@code IIorap} native service. - */ -public class IorapForwardingService extends SystemService { - - public static final String TAG = "IorapForwardingService"; - /** $> adb shell 'setprop log.tag.IorapForwardingService VERBOSE' */ - public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - /** $> adb shell 'setprop ro.iorapd.enable true' */ - private static boolean IS_ENABLED = SystemProperties.getBoolean("ro.iorapd.enable", true); - /** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */ - private static boolean WTF_CRASH = SystemProperties.getBoolean( - "iorapd.forwarding_service.wtf_crash", false); - private static final Duration TIMEOUT = Duration.ofSeconds(600L); - - // "Unique" job ID from the service name. Also equal to 283673059. - public static final int JOB_ID_IORAPD = encodeEnglishAlphabetStringIntoInt("iorapd"); - // Run every 24 hours. - public static final long JOB_INTERVAL_MS = TimeUnit.HOURS.toMillis(24); - - private IIorap mIorapRemote; - private final Object mLock = new Object(); - /** Handle onBinderDeath by periodically trying to reconnect. */ - private final Handler mHandler = - new BinderConnectionHandler(IoThread.getHandler().getLooper()); - - private volatile IorapdJobService mJobService; // Write-once (null -> non-null forever). - private volatile static IorapForwardingService sSelfService; // Write once (null -> non-null). - - - /** - * Atomics set to true if the JobScheduler requests an abort. - */ - private final AtomicBoolean mAbortIdleCompilation = new AtomicBoolean(false); - - /** - * Initializes the system service. - * <p> - * Subclasses must define a single argument constructor that accepts the context - * and passes it to super. - * </p> - * - * @param context The system server context. - */ - public IorapForwardingService(Context context) { - super(context); - - if (DEBUG) { - Log.v(TAG, "IorapForwardingService (Context=" + context.toString() + ")"); - } - - if (sSelfService != null) { - throw new AssertionError("only one service instance allowed"); - } - sSelfService = this; - } - - //<editor-fold desc="Providers"> - /* - * Providers for external dependencies: - * - These are marked as protected to allow tests to inject different values via mocks. - */ - - @VisibleForTesting - protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() { - ActivityTaskManagerInternal amtInternal = - LocalServices.getService(ActivityTaskManagerInternal.class); - ActivityMetricsLaunchObserverRegistry launchObserverRegistry = - amtInternal.getLaunchObserverRegistry(); - return launchObserverRegistry; - } - - @VisibleForTesting - protected IIorap provideIorapRemote() { - IIorap iorap; - try { - iorap = IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd")); - } catch (ServiceManager.ServiceNotFoundException e) { - Log.w(TAG, e.getMessage()); - return null; - } - - try { - iorap.asBinder().linkToDeath(provideDeathRecipient(), /*flags*/0); - } catch (RemoteException e) { - handleRemoteError(e); - return null; - } - - return iorap; - } - - @VisibleForTesting - protected DeathRecipient provideDeathRecipient() { - return new DeathRecipient() { - @Override - public void binderDied() { - Log.w(TAG, "iorapd has died"); - retryConnectToRemoteAndConfigure(/*attempts*/0); - - if (mJobService != null) { - mJobService.onIorapdDisconnected(); - } - } - }; - } - - @VisibleForTesting - protected boolean isIorapEnabled() { - // These two mendel flags should match those in iorapd native process - // system/iorapd/src/common/property.h - boolean isTracingEnabled = - getMendelFlag("iorap_perfetto_enable", "iorapd.perfetto.enable", false); - boolean isReadAheadEnabled = - getMendelFlag("iorap_readahead_enable", "iorapd.readahead.enable", false); - // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process - // never comes up, so all binder connections will fail indefinitely. - return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled); - } - - private boolean getMendelFlag(String mendelFlag, String sysProperty, boolean defaultValue) { - // TODO(yawanng) use DeviceConfig to get mendel property. - // DeviceConfig doesn't work and the reason is not clear. - // Provider service is already up before IORapForwardService. - String mendelProperty = "persist.device_config." - + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT - + "." - + mendelFlag; - return SystemProperties.getBoolean(mendelProperty, - SystemProperties.getBoolean(sysProperty, defaultValue)); - } - - //</editor-fold> - - @Override - public void onStart() { - if (DEBUG) { - Log.v(TAG, "onStart"); - } - - retryConnectToRemoteAndConfigure(/*attempts*/0); - } - - @Override - public void onBootPhase(int phase) { - if (phase == PHASE_BOOT_COMPLETED) { - if (DEBUG) { - Log.v(TAG, "onBootPhase(PHASE_BOOT_COMPLETED)"); - } - - if (isIorapEnabled()) { - // Set up a recurring background job. This has to be done in a later phase since it - // has a dependency the job scheduler. - // - // Doing this too early can result in a ServiceNotFoundException for 'jobservice' - // or a null reference for #getSystemService(JobScheduler.class) - mJobService = new IorapdJobService(getContext()); - } - } - } - - private class BinderConnectionHandler extends Handler { - public BinderConnectionHandler(android.os.Looper looper) { - super(looper); - } - - public static final int MESSAGE_BINDER_CONNECT = 0; - - private int mAttempts = 0; - - @Override - public void handleMessage(android.os.Message message) { - switch (message.what) { - case MESSAGE_BINDER_CONNECT: - if (!retryConnectToRemoteAndConfigure(mAttempts)) { - mAttempts++; - } else { - mAttempts = 0; - } - break; - default: - throw new AssertionError("Unknown message: " + message.toString()); - } - } - } - - /** - * Handle iorapd shutdowns and crashes, by attempting to reconnect - * until the service is reached again. - * - * <p>The first connection attempt is synchronous, - * subsequent attempts are done by posting delayed tasks to the IoThread.</p> - * - * @return true if connection succeeded now, or false if it failed now [and needs to requeue]. - */ - private boolean retryConnectToRemoteAndConfigure(int attempts) { - final int sleepTime = 1000; // ms - - if (DEBUG) { - Log.v(TAG, "retryConnectToRemoteAndConfigure - attempt #" + attempts); - } - - if (connectToRemoteAndConfigure()) { - return true; - } - - // Either 'iorapd' is stuck in a crash loop (ouch!!) or we manually - // called 'adb shell stop iorapd' , which means this would loop until it comes back - // up. - // - // TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid - // printing this warning. - if (DEBUG) { - Log.v(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime); - } - - // Use a handler instead of Thread#sleep to avoid backing up the binder thread - // when this is called from the death recipient callback. - mHandler.sendMessageDelayed( - mHandler.obtainMessage(BinderConnectionHandler.MESSAGE_BINDER_CONNECT), - sleepTime); - - return false; - - // Log.e(TAG, "Can't connect to iorapd - giving up after " + attempts + " attempts"); - } - - private boolean connectToRemoteAndConfigure() { - synchronized (mLock) { - // Synchronize against any concurrent calls to this via the DeathRecipient. - return connectToRemoteAndConfigureLocked(); - } - } - - private boolean connectToRemoteAndConfigureLocked() { - if (!isIorapEnabled()) { - if (DEBUG) { - Log.v(TAG, "connectToRemoteAndConfigure - iorapd is disabled, skip rest of work"); - } - // When we see that iorapd is disabled (when system server comes up), - // it stays disabled permanently until the next system server reset. - - // TODO: consider listening to property changes as a callback, then we can - // be more dynamic about handling enable/disable. - return true; - } - - // Connect to the native binder service. - mIorapRemote = provideIorapRemote(); - if (mIorapRemote == null) { - if (DEBUG) { - Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?"); - } - return false; - } - invokeRemote(mIorapRemote, - (IIorap remote) -> remote.setTaskListener(new RemoteTaskListener()) ); - registerInProcessListenersLocked(); - - Log.i(TAG, "Connected to iorapd native service."); - - return true; - } - - private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver(); - private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator(); - private final DexOptPackagesUpdated mDexOptPackagesUpdated = new DexOptPackagesUpdated(); - private boolean mRegisteredListeners = false; - - private void registerInProcessListenersLocked() { - if (mRegisteredListeners) { - // Listeners are registered only once (idempotent operation). - // - // Today listeners are tolerant of the remote side going away - // by handling remote errors. - // - // We could try to 'unregister' the listener when we get a binder disconnect, - // but we'd still have to handle the case of encountering synchronous errors so - // it really wouldn't be a win (other than having less log spew). - return; - } - - // Listen to App Launch Sequence events from ActivityTaskManager, - // and forward them to the native binder service. - ActivityMetricsLaunchObserverRegistry launchObserverRegistry = - provideLaunchObserverRegistry(); - launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver); - launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator); - - BackgroundDexOptService.getService().addPackagesUpdatedListener( - mDexOptPackagesUpdated); - - - mRegisteredListeners = true; - } - - private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener { - @Override - public void onPackagesUpdated(ArraySet<String> updatedPackages) { - String[] updated = updatedPackages.toArray(new String[0]); - for (String packageName : updated) { - Log.d(TAG, "onPackagesUpdated: " + packageName); - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onDexOptEvent(RequestId.nextValueForSequence(), - DexOptEvent.createPackageUpdate(packageName)) - ); - } - } - } - - private class AppLaunchObserver implements ActivityMetricsLaunchObserver { - // We add a synthetic sequence ID here to make it easier to differentiate new - // launch sequences on the native side. - private @AppLaunchEvent.SequenceId long mSequenceId = -1; - - // All callbacks occur on the same background thread. Don't synchronize explicitly. - - @Override - public void onIntentStarted(@NonNull Intent intent, long timestampNs) { - // #onIntentStarted [is the only transition that] initiates a new launch sequence. - ++mSequenceId; - - if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)", - mSequenceId, intent, timestampNs)); - } - - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs)) - ); - } - - @Override - public void onIntentFailed() { - if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId)); - } - - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.IntentFailed(mSequenceId)) - ); - } - - @Override - public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, - @Temperature int temperature) { - if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)", - mSequenceId, activity, temperature)); - } - - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature)) - ); - } - - @Override - public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { - if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)", - mSequenceId, activity)); - } - - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, - activity))); - } - - @Override - public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity, - long timestampNs) { - if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)", - mSequenceId, activity, timestampNs)); - } - - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, - activity, - timestampNs)) - ); - } - - @Override - public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, - long timestampNs) { - if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)", - mSequenceId, activity, timestampNs)); - } - - invokeRemote(mIorapRemote, - (IIorap remote) -> - remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs)) - ); - } - } - - /** - * Debugging: - * - * $> adb shell dumpsys jobscheduler - * - * Search for 'IorapdJobServiceProxy'. - * - * JOB #1000/283673059: 6e54ed android/com.google.android.startop.iorap.IorapForwardingService$IorapdJobServiceProxy - * ^ ^ ^ - * (uid, job id) ComponentName(package/class) - * - * Forcing the job to be run, ignoring constraints: - * - * $> adb shell cmd jobscheduler run -f android 283673059 - * ^ ^ - * package job_id - * - * ------------------------------------------------------------ - * - * This class is instantiated newly by the JobService every time - * it wants to run a new job. - * - * We need to forward invocations to the current running instance of - * IorapForwardingService#IorapdJobService. - * - * Visibility: Must be accessible from android.app.AppComponentFactory - */ - public static class IorapdJobServiceProxy extends JobService { - - public IorapdJobServiceProxy() { - getActualIorapdJobService().bindProxy(this); - } - - - @NonNull - private IorapdJobService getActualIorapdJobService() { - // Can't ever be null, because the guarantee is that the - // IorapForwardingService is always running. - // We are in the same process as Job Service. - return sSelfService.mJobService; - } - - // Called by system to start the job. - @Override - public boolean onStartJob(JobParameters params) { - return getActualIorapdJobService().onStartJob(params); - } - - // Called by system to prematurely stop the job. - @Override - public boolean onStopJob(JobParameters params) { - return getActualIorapdJobService().onStopJob(params); - } - } - - private class IorapdJobService extends JobService { - private final ComponentName IORAPD_COMPONENT_NAME; - - private final Object mLock = new Object(); - // Jobs currently running remotely on iorapd. - // They were started by the JobScheduler and need to be finished. - private final HashMap<RequestId, JobParameters> mRunningJobs = new HashMap<>(); - - private final JobInfo IORAPD_JOB_INFO; - - private volatile IorapdJobServiceProxy mProxy; - - public void bindProxy(IorapdJobServiceProxy proxy) { - mProxy = proxy; - } - - // Create a new job service which immediately schedules a 24-hour idle maintenance mode - // background job to execute. - public IorapdJobService(Context context) { - if (DEBUG) { - Log.v(TAG, "IorapdJobService (Context=" + context.toString() + ")"); - } - - // Schedule the proxy class to be instantiated by the JobScheduler - // when it is time to invoke background jobs for IorapForwardingService. - - - // This also needs a BIND_JOB_SERVICE permission in - // frameworks/base/core/res/AndroidManifest.xml - IORAPD_COMPONENT_NAME = new ComponentName(context, IorapdJobServiceProxy.class); - - JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_IORAPD, IORAPD_COMPONENT_NAME); - builder.setPeriodic(JOB_INTERVAL_MS); - - builder.setRequiresCharging(true); - builder.setRequiresDeviceIdle(true); - - builder.setRequiresStorageNotLow(true); - - IORAPD_JOB_INFO = builder.build(); - - JobScheduler js = context.getSystemService(JobScheduler.class); - js.schedule(IORAPD_JOB_INFO); - Log.d(TAG, - "BgJob Scheduled (jobId=" + JOB_ID_IORAPD - + ", interval: " + JOB_INTERVAL_MS + "ms)"); - } - - // Called by system to start the job. - @Override - public boolean onStartJob(JobParameters params) { - // Tell iorapd to start a background job. - Log.d(TAG, "Starting background job: " + params.toString()); - - mAbortIdleCompilation.set(false); - // PackageManagerService starts before IORap service. - PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); - List<String> pkgs = pm.getAllPackages(); - runIdleCompilationAsync(params, pkgs); - return true; - } - - private void runIdleCompilationAsync(final JobParameters params, final List<String> pkgs) { - new Thread("IORap_IdleCompilation") { - @Override - public void run() { - for (int i = 0; i < pkgs.size(); i++) { - String pkg = pkgs.get(i); - if (mAbortIdleCompilation.get()) { - Log.i(TAG, "The idle compilation is aborted"); - return; - } - - // We wait until that job's sequence ID returns to us with 'Completed', - RequestId request; - synchronized (mLock) { - // TODO: would be cleaner if we got the request from the - // 'invokeRemote' function. Better yet, consider - // a Pair<RequestId, Future<TaskResult>> or similar. - request = RequestId.nextValueForSequence(); - mRunningJobs.put(request, params); - } - - Log.i(TAG, String.format("IORap compile package: %s, [%d/%d]", - pkg, i + 1, pkgs.size())); - boolean shouldUpdateVersions = (i == 0); - if (!invokeRemote(mIorapRemote, (IIorap remote) -> - remote.onJobScheduledEvent(request, - JobScheduledEvent.createIdleMaintenance( - JobScheduledEvent.TYPE_START_JOB, - params, - pkg, - shouldUpdateVersions)))) { - synchronized (mLock) { - mRunningJobs.remove(request); // Avoid memory leaks. - } - } - - // Wait until the job is complete and removed from the running jobs. - retryWithTimeout(TIMEOUT, () -> { - synchronized (mLock) { - return !mRunningJobs.containsKey(request); - } - }); - } - - // Finish the job after all packages are compiled. - if (mProxy != null) { - mProxy.jobFinished(params, /*reschedule*/false); - } - } - }.start(); - } - - /** Retry until timeout. */ - private boolean retryWithTimeout(final Duration timeout, BooleanSupplier supplier) { - long totalSleepTimeMs = 0L; - long sleepIntervalMs = 10L; - while (true) { - if (supplier.getAsBoolean()) { - return true; - } - try { - TimeUnit.MILLISECONDS.sleep(sleepIntervalMs); - } catch (InterruptedException e) { - Log.e(TAG, e.getMessage()); - return false; - } - - totalSleepTimeMs += sleepIntervalMs; - if (totalSleepTimeMs > timeout.toMillis()) { - return false; - } - } - } - - // Called by system to prematurely stop the job. - @Override - public boolean onStopJob(JobParameters params) { - // As this is unexpected behavior, print a warning. - Log.w(TAG, "onStopJob(params=" + params.toString() + ")"); - mAbortIdleCompilation.set(true); - - // Yes, retry the job at a later time no matter what. - return true; - } - - // Listen to *all* task completes for all requests. - // The majority of these might be unrelated to background jobs. - public void onIorapdTaskCompleted(RequestId requestId) { - JobParameters jobParameters; - synchronized (mLock) { - jobParameters = mRunningJobs.remove(requestId); - } - - // Typical case: This was a task callback unrelated to our jobs. - if (jobParameters == null) { - return; - } - - if (DEBUG) { - Log.v(TAG, - String.format("IorapdJobService#onIorapdTaskCompleted(%s), found params=%s", - requestId, jobParameters)); - } - - Log.d(TAG, "Finished background job: " + jobParameters.toString()); - } - - public void onIorapdDisconnected() { - synchronized (mLock) { - mRunningJobs.clear(); - } - - if (DEBUG) { - Log.v(TAG, String.format("IorapdJobService#onIorapdDisconnected")); - } - - // TODO: should we try to resubmit all incomplete jobs after it's reconnected? - } - } - - private class RemoteTaskListener extends ITaskListener.Stub { - @Override - public void onProgress(RequestId requestId, TaskResult result) throws RemoteException { - if (DEBUG) { - Log.v(TAG, - String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result)); - } - - // TODO: implement rest. - } - - @Override - public void onComplete(RequestId requestId, TaskResult result) throws RemoteException { - if (DEBUG) { - Log.v(TAG, - String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result)); - } - - if (mJobService != null) { - mJobService.onIorapdTaskCompleted(requestId); - } - - // TODO: implement rest. - } - } - - /** Allow passing lambdas to #invokeRemote */ - private interface RemoteRunnable { - // TODO: run(RequestId) ? - void run(IIorap iorap) throws RemoteException; - } - - // Always pass in the iorap directly here to avoid data race. - private static boolean invokeRemote(IIorap iorap, RemoteRunnable r) { - if (iorap == null) { - Log.w(TAG, "IIorap went to null in this thread, drop invokeRemote."); - return false; - } - try { - r.run(iorap); - return true; - } catch (RemoteException e) { - // This could be a logic error (remote side returning error), which we need to fix. - // - // This could also be a DeadObjectException in which case its probably just iorapd - // being manually restarted. - // - // Don't make any assumption, since DeadObjectException could also mean iorapd crashed - // unexpectedly. - // - // DeadObjectExceptions are recovered from using DeathRecipient and #linkToDeath. - handleRemoteError(e); - return false; - } - } - - private static void handleRemoteError(Throwable t) { - if (WTF_CRASH) { - // In development modes, we just want to crash. - throw new AssertionError("unexpected remote error", t); - } else { - // Log to wtf which gets sent to dropbox, and in system_server this does not crash. - Log.wtf(TAG, t); - } - } - - // Encode A-Z bitstring into bits. Every character is bits. - // Characters outside of the range [a,z] are considered out of range. - // - // The least significant bits hold the last character. - // First 2 bits are left as 0. - private static int encodeEnglishAlphabetStringIntoInt(String name) { - int value = 0; - - final int CHARS_PER_INT = 6; - final int BITS_PER_CHAR = 5; - // Note: 2 top bits are unused, this also means our values are non-negative. - final char CHAR_LOWER = 'a'; - final char CHAR_UPPER = 'z'; - - if (name.length() > CHARS_PER_INT) { - throw new IllegalArgumentException( - "String too long. Cannot encode more than 6 chars: " + name); - } - - for (int i = 0; i < name.length(); ++i) { - char c = name.charAt(i); - - if (c < CHAR_LOWER || c > CHAR_UPPER) { - throw new IllegalArgumentException("String has out-of-range [a-z] chars: " + name); - } - - // Avoid sign extension during promotion. - int cur_value = (c & 0xFFFF) - (CHAR_LOWER & 0xFFFF); - if (cur_value >= (1 << BITS_PER_CHAR)) { - throw new AssertionError("wtf? i=" + i + ", name=" + name); - } - - value = value << BITS_PER_CHAR; - value = value | cur_value; - } - - return value; - } -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java deleted file mode 100644 index b91dd71fd28c..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.app.job.JobParameters; -import android.annotation.NonNull; -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Forward JobService events to iorapd. <br /><br /> - * - * iorapd sometimes need to use background jobs. Forwarding these events to iorapd - * notifies iorapd when it is an opportune time to execute these background jobs. - * - * @hide - */ -public class JobScheduledEvent implements Parcelable { - - /** JobService#onJobStarted */ - public static final int TYPE_START_JOB = 0; - /** JobService#onJobStopped */ - public static final int TYPE_STOP_JOB = 1; - private static final int TYPE_MAX = 1; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_START_JOB, - TYPE_STOP_JOB, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - - /** @see JobParameters#getJobId() */ - public final int jobId; - - public final String packageName; - - public final boolean shouldUpdateVersions; - - /** Device is 'idle' and it's charging (plugged in). */ - public static final int SORT_IDLE_MAINTENANCE = 0; - private static final int SORT_MAX = 0; - - /** @hide */ - @IntDef(flag = true, prefix = { "SORT_" }, value = { - SORT_IDLE_MAINTENANCE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Sort {} - - /** - * Roughly corresponds to the {@code extras} fields in a JobParameters. - */ - @Sort public final int sort; - - /** - * Creates a {@link #SORT_IDLE_MAINTENANCE} event from the type and job parameters. - * - * Only the job ID is retained from {@code jobParams}, all other param info is dropped. - */ - @NonNull - public static JobScheduledEvent createIdleMaintenance( - @Type int type, JobParameters jobParams, String packageName, boolean shouldUpdateVersions) { - return new JobScheduledEvent( - type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE, packageName, shouldUpdateVersions); - } - - private JobScheduledEvent(@Type int type, - int jobId, - @Sort int sort, - String packageName, - boolean shouldUpdateVersions) { - this.type = type; - this.jobId = jobId; - this.sort = sort; - this.packageName = packageName; - this.shouldUpdateVersions = shouldUpdateVersions; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - // No check for 'jobId': any int is valid. - CheckHelpers.checkTypeInRange(sort, SORT_MAX); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof JobScheduledEvent) { - return equals((JobScheduledEvent) other); - } - return false; - } - - private boolean equals(JobScheduledEvent other) { - return type == other.type && - jobId == other.jobId && - sort == other.sort && - packageName.equals(other.packageName) && - shouldUpdateVersions == other.shouldUpdateVersions; - } - - @Override - public String toString() { - return String.format( - "{type: %d, jobId: %d, sort: %d, packageName: %s, shouldUpdateVersions %b}", - type, jobId, sort, packageName, shouldUpdateVersions); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - out.writeInt(jobId); - out.writeInt(sort); - out.writeString(packageName); - out.writeBoolean(shouldUpdateVersions); - - // We do not parcel the entire JobParameters here because there is no C++ equivalent - // of that class [which the iorapd side of the binder interface requires]. - } - - private JobScheduledEvent(Parcel in) { - this.type = in.readInt(); - this.jobId = in.readInt(); - this.sort = in.readInt(); - this.packageName = in.readString(); - this.shouldUpdateVersions = in.readBoolean(); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<JobScheduledEvent> CREATOR - = new Parcelable.Creator<JobScheduledEvent>() { - public JobScheduledEvent createFromParcel(Parcel in) { - return new JobScheduledEvent(in); - } - - public JobScheduledEvent[] newArray(int size) { - return new JobScheduledEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java deleted file mode 100644 index aa4eea716363..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.annotation.NonNull; -import android.os.Parcelable; -import android.os.Parcel; -import android.net.Uri; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Forward package manager events to iorapd. <br /><br /> - * - * Knowing when packages are modified by the system are a useful tidbit to help with performance: - * for example when a package is replaced, it could be a hint used to invalidate any collected - * io profiles used for prefetching or pinning. - * - * @hide - */ -public class PackageEvent implements Parcelable { - - /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */ - public static final int TYPE_REPLACED = 0; - private static final int TYPE_MAX = 0; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_REPLACED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - - /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */ - public final Uri packageUri; - /** The name of the package, for example {@code com.android.calculator}. */ - public final String packageName; - - @NonNull - public static PackageEvent createReplaced(Uri packageUri, String packageName) { - return new PackageEvent(TYPE_REPLACED, packageUri, packageName); - } - - private PackageEvent(@Type int type, Uri packageUri, String packageName) { - this.type = type; - this.packageUri = packageUri; - this.packageName = packageName; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - Objects.requireNonNull(packageUri, "packageUri"); - Objects.requireNonNull(packageName, "packageName"); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof PackageEvent) { - return equals((PackageEvent) other); - } - return false; - } - - private boolean equals(PackageEvent other) { - return type == other.type && - Objects.equals(packageUri, other.packageUri) && - Objects.equals(packageName, other.packageName); - } - - @Override - public String toString() { - return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - packageUri.writeToParcel(out, flags); - out.writeString(packageName); - } - - private PackageEvent(Parcel in) { - this.type = in.readInt(); - this.packageUri = Uri.CREATOR.createFromParcel(in); - this.packageName = in.readString(); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<PackageEvent> CREATOR - = new Parcelable.Creator<PackageEvent>() { - public PackageEvent createFromParcel(Parcel in) { - return new PackageEvent(in); - } - - public PackageEvent[] newArray(int size) { - return new PackageEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java deleted file mode 100644 index 503e1c633581..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.NonNull; - -/** - * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation, - * used for asynchronous callbacks by the server. <br /><br /> - * - * As all system server binder calls must be {@code oneway}, this means all invocations - * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID - * exists to associate all calls with their respective callbacks in - * {@link com.google.android.startop.iorap.ITaskListener}. - * - * @see com.google.android.startop.iorap.IIorap - * - * @hide - */ -public class RequestId implements Parcelable { - - public final long requestId; - - private static Object mLock = new Object(); - private static long mNextRequestId = 0; - - /** - * Create a monotonically increasing request ID.<br /><br /> - * - * It is invalid to re-use the same request ID for multiple method calls on - * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created - * each time. - */ - @NonNull public static RequestId nextValueForSequence() { - long currentRequestId; - synchronized (mLock) { - currentRequestId = mNextRequestId; - ++mNextRequestId; - } - return new RequestId(currentRequestId); - } - - private RequestId(long requestId) { - this.requestId = requestId; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - if (requestId < 0) { - throw new IllegalArgumentException("request id must be non-negative"); - } - } - - @Override - public String toString() { - return String.format("{requestId: %d}", requestId); - } - - @Override - public int hashCode() { - return Long.hashCode(requestId); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof RequestId) { - return equals((RequestId) other); - } - return false; - } - - private boolean equals(RequestId other) { - return requestId == other.requestId; - } - - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeLong(requestId); - } - - private RequestId(Parcel in) { - requestId = in.readLong(); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<RequestId> CREATOR - = new Parcelable.Creator<RequestId>() { - public RequestId createFromParcel(Parcel in) { - return new RequestId(in); - } - - public RequestId[] newArray(int size) { - return new RequestId[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java deleted file mode 100644 index 75d47f9e3d17..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Forward system service events to iorapd. - * - * @see com.android.server.SystemService - * - * @hide - */ -public class SystemServiceEvent implements Parcelable { - - /** @see com.android.server.SystemService#onBootPhase */ - public static final int TYPE_BOOT_PHASE = 0; - /** @see com.android.server.SystemService#onStart */ - public static final int TYPE_START = 1; - private static final int TYPE_MAX = TYPE_START; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_BOOT_PHASE, - TYPE_START, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - - // TODO: do we want to pass the exact build phase enum? - - public SystemServiceEvent(@Type int type) { - this.type = type; - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - } - - @Override - public String toString() { - return String.format("{type: %d}", type); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof SystemServiceEvent) { - return equals((SystemServiceEvent) other); - } - return false; - } - - private boolean equals(SystemServiceEvent other) { - return type == other.type; - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - } - - private SystemServiceEvent(Parcel in) { - this.type = in.readInt(); - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<SystemServiceEvent> CREATOR - = new Parcelable.Creator<SystemServiceEvent>() { - public SystemServiceEvent createFromParcel(Parcel in) { - return new SystemServiceEvent(in); - } - - public SystemServiceEvent[] newArray(int size) { - return new SystemServiceEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java deleted file mode 100644 index 2e7bafe7bdbf..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Forward user events to iorapd.<br /><br /> - * - * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies - * by iorapd (e.g. to handle user default pinned applications changing). - * - * @see com.android.server.SystemService - * - * @hide - */ -public class SystemServiceUserEvent implements Parcelable { - - /** @see com.android.server.SystemService#onUserStarting */ - public static final int TYPE_START_USER = 0; - /** @see com.android.server.SystemService#onUserUnlocking */ - public static final int TYPE_UNLOCK_USER = 1; - /** @see com.android.server.SystemService#onUserSwitching*/ - public static final int TYPE_SWITCH_USER = 2; - /** @see com.android.server.SystemService#onUserStopping */ - public static final int TYPE_STOP_USER = 3; - /** @see com.android.server.SystemService#onUserStopped */ - public static final int TYPE_CLEANUP_USER = 4; - private static final int TYPE_MAX = TYPE_CLEANUP_USER; - - /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_START_USER, - TYPE_UNLOCK_USER, - TYPE_SWITCH_USER, - TYPE_STOP_USER, - TYPE_CLEANUP_USER, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - @Type public final int type; - public final int userHandle; - - public SystemServiceUserEvent(@Type int type, int userHandle) { - this.type = type; - this.userHandle = userHandle; - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkTypeInRange(type, TYPE_MAX); - if (userHandle < 0) { - throw new IllegalArgumentException("userHandle must be non-negative"); - } - } - - @Override - public String toString() { - return String.format("{type: %d, userHandle: %d}", type, userHandle); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof SystemServiceUserEvent) { - return equals((SystemServiceUserEvent) other); - } - return false; - } - - private boolean equals(SystemServiceUserEvent other) { - return type == other.type && - userHandle == other.userHandle; - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(type); - out.writeInt(userHandle); - } - - private SystemServiceUserEvent(Parcel in) { - this.type = in.readInt(); - this.userHandle = in.readInt(); - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR - = new Parcelable.Creator<SystemServiceUserEvent>() { - public SystemServiceUserEvent createFromParcel(Parcel in) { - return new SystemServiceUserEvent(in); - } - - public SystemServiceUserEvent[] newArray(int size) { - return new SystemServiceUserEvent[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java deleted file mode 100644 index b5fd6d8d1c45..000000000000 --- a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.startop.iorap; - -import android.os.Parcelable; -import android.os.Parcel; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener} - * callbacks.<br /><br /> - * - * Following {@link com.google.android.startop.iorap.IIorap} method invocation, - * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br /> - * - * State transitions are as follows: <br /><br /> - * - * <pre> - * ┌─────────────────────────────┐ - * │ ▼ - * ┌───────┐ ┌─────────┐ ╔═══════════╗ - * ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║ - * └───────┘ └─────────┘ ╚═══════════╝ - * │ │ - * │ │ - * ▼ │ - * ╔═══════╗ │ - * ──▶ ║ ERROR ║ ◀─────┘ - * ╚═══════╝ - * - * </pre> <!-- system/iorap/docs/binder/TaskResult.dot --> - * - * @hide - */ -public class TaskResult implements Parcelable { - - public static final int STATE_BEGAN = 0; - public static final int STATE_ONGOING = 1; - public static final int STATE_COMPLETED = 2; - public static final int STATE_ERROR = 3; - private static final int STATE_MAX = STATE_ERROR; - - /** @hide */ - @IntDef(flag = true, prefix = { "STATE_" }, value = { - STATE_BEGAN, - STATE_ONGOING, - STATE_COMPLETED, - STATE_ERROR, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface State {} - - @State public final int state; - - @Override - public String toString() { - return String.format("{state: %d}", state); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof TaskResult) { - return equals((TaskResult) other); - } - return false; - } - - private boolean equals(TaskResult other) { - return state == other.state; - } - - public TaskResult(@State int state) { - this.state = state; - - checkConstructorArguments(); - } - - private void checkConstructorArguments() { - CheckHelpers.checkStateInRange(state, STATE_MAX); - } - - //<editor-fold desc="Binder boilerplate"> - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(state); - } - - private TaskResult(Parcel in) { - state = in.readInt(); - - checkConstructorArguments(); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator<TaskResult> CREATOR - = new Parcelable.Creator<TaskResult>() { - public TaskResult createFromParcel(Parcel in) { - return new TaskResult(in); - } - - public TaskResult[] newArray(int size) { - return new TaskResult[size]; - } - }; - //</editor-fold> -} diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp deleted file mode 100644 index 6e8725d091fb..000000000000 --- a/startop/iorap/stress/Android.bp +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (C) 2020 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -cc_binary { - name: "iorap.stress.memory", - srcs: ["main_memory.cc"], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter" - ], - - shared_libs: [ - "libbase" - ], - - host_supported: true, -} diff --git a/startop/iorap/stress/main_memory.cc b/startop/iorap/stress/main_memory.cc deleted file mode 100644 index 1f268619e4d9..000000000000 --- a/startop/iorap/stress/main_memory.cc +++ /dev/null @@ -1,126 +0,0 @@ -// -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include <chrono> -#include <fstream> -#include <iostream> -#include <random> -#include <string> - -#include <string.h> -#include <stdlib.h> -#include <sys/mman.h> - -#include <android-base/parseint.h> - -static constexpr size_t kBytesPerMb = 1048576; -const size_t kMemoryAllocationSize = 2 * 1024 * kBytesPerMb; - -#define USE_MLOCKALL 0 - -std::string GetProcessStatus(const char* key) { - // Build search pattern of key and separator. - std::string pattern(key); - pattern.push_back(':'); - - // Search for status lines starting with pattern. - std::ifstream fs("/proc/self/status"); - std::string line; - while (std::getline(fs, line)) { - if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) { - // Skip whitespace in matching line (if any). - size_t pos = line.find_first_not_of(" \t", pattern.size()); - if (pos == std::string::npos) { - break; - } - return std::string(line, pos); - } - } - return "<unknown>"; -} - -int main(int argc, char** argv) { - size_t allocationSize = 0; - if (argc >= 2) { - if (!android::base::ParseUint(argv[1], /*out*/&allocationSize)) { - std::cerr << "Failed to parse the allocation size (must be 0,MAX_SIZE_T)" << std::endl; - return 1; - } - } else { - allocationSize = kMemoryAllocationSize; - } - - void* mem = malloc(allocationSize); - if (mem == nullptr) { - std::cerr << "Malloc failed" << std::endl; - return 1; - } - - volatile int* imem = static_cast<int *>(mem); // don't optimize out memory usage - - size_t imemCount = allocationSize / sizeof(int); - - std::cout << "Allocated " << allocationSize << " bytes" << std::endl; - - auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - std::mt19937 mt_rand(seed); - - size_t randPrintCount = 10; - - // Write random numbers: - // * Ensures each page is resident - // * Avoids zeroed out pages (zRAM) - // * Avoids same-page merging - for (size_t i = 0; i < imemCount; ++i) { - imem[i] = mt_rand(); - - if (i < randPrintCount) { - std::cout << "Generated random value: " << imem[i] << std::endl; - } - } - -#if USE_MLOCKALL - /* - * Lock all pages from the address space of this process. - */ - if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) { - std::cerr << "Mlockall failed" << std::endl; - return 1; - } -#else - // Use mlock because of the predictable VmLck size. - // Using mlockall tends to bring in anywhere from 2-2.5GB depending on the device. - if (mlock(mem, allocationSize) != 0) { - std::cerr << "Mlock failed" << std::endl; - return 1; - } -#endif - - // Validate memory is actually resident and locked with: - // $> cat /proc/$(pidof iorap.stress.memory)/status | grep VmLck - std::cout << "Locked memory (VmLck) = " << GetProcessStatus("VmLck") << std::endl; - - std::cout << "Press any key to terminate" << std::endl; - int any_input; - std::cin >> any_input; - - std::cout << "Terminating..." << std::endl; - - munlockall(); - free(mem); - - return 0; -} diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp deleted file mode 100644 index ad3d001ad7d4..000000000000 --- a/startop/iorap/tests/Android.bp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// TODO: once b/80095087 is fixed, rewrite this back to android_test -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library { - name: "libiorap-java-test-lib", - srcs: ["src/**/*.kt"], - static_libs: [ - // Non-test dependencies - // library under test - "services.startop.iorap", - // need the system_server code to be on the classpath, - "services.core", - // Test Dependencies - // test android dependencies - "platform-test-annotations", - "androidx.test.rules", - // test framework dependencies - "mockito-target-inline-minus-junit4", - // "mockito-target-minus-junit4", - // Mockito also requires JNI (see Android.mk) - // and android:debuggable=true (see AndroidManifest.xml) - "truth-prebuilt", - ], - // sdk_version: "current", - // certificate: "platform", - libs: [ - "android.test.base", - "android.test.runner", - ], - // test_suites: ["device-tests"], -} - -android_test { - name: "libiorap-java-tests", - dxflags: ["--multi-dex"], - test_suites: ["device-tests"], - static_libs: ["libiorap-java-test-lib"], - compile_multilib: "both", - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - "libmultiplejvmtiagentsinterferenceagent", - ], - libs: [ - "android.test.base", - "android.test.runner", - ], - // Use private APIs - certificate: "platform", - platform_apis: true, -} diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml deleted file mode 100644 index b967e7207a3f..000000000000 --- a/startop/iorap/tests/AndroidManifest.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<!--suppress AndroidUnknownAttribute --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.google.android.startop.iorap.tests" - android:sharedUserId="com.google.android.startop.iorap.tests" - android:versionCode="1" - android:versionName="1.0" > - - <!--suppress AndroidDomInspection --> - <instrumentation - android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.google.android.startop.iorap.tests" /> - - <!-- - 'debuggable=true' is required to properly load mockito jvmti dependencies, - otherwise it gives the following error at runtime: - - Openjdkjvmti plugin was loaded on a non-debuggable Runtime. - Plugin was loaded too late to change runtime state to DEBUGGABLE. --> - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> -</manifest> diff --git a/startop/iorap/tests/AndroidTest.xml b/startop/iorap/tests/AndroidTest.xml deleted file mode 100644 index 6102c44e61bf..000000000000 --- a/startop/iorap/tests/AndroidTest.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<configuration description="Runs libiorap-java-tests."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-instrumentation" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="libiorap-java-tests.apk" /> - </target_preparer> - - <!-- - Our IIorapIntegrationTest.kt requires setlinux to be disabled: - it connects to the iorapd binder service but this requires selinux permissions: - - avc: denied { find } for service=iorapd pid=2738 uid=10050 - scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:iorapd_service:s0 - tclass=service_manager permissive=0 - --> - <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"> - </target_preparer> - - <!-- do not use DeviceSetup#set-property because it reboots the device b/136200738. - furthermore the changes in /data/local.prop don't actually seem to get picked up. - --> - <target_preparer - class="com.android.tradefed.targetprep.DeviceSetup"> - <!-- we need this magic flag, otherwise it always reboots and breaks the selinux --> - <option name="force-skip-system-props" value="true" /> - - <!-- Crash instead of using Log.wtf within the system_server iorap code. --> - <option name="run-command" value="setprop iorapd.forwarding_service.wtf_crash true" /> - <!-- IIorapd has fake behavior: it doesn't do anything but reply with 'DONE' status --> - <option name="run-command" value="setprop iorapd.binder.fake true" /> - - <!-- iorapd does not pick up the above changes until we restart it --> - <option name="run-command" value="stop iorapd" /> - <option name="run-command" value="start iorapd" /> - <!-- give it some time to restart the service; otherwise the first unit test might fail --> - <option name="run-command" value="sleep 1" /> - </target_preparer> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.google.android.startop.iorap.tests" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - </test> - - <!-- using DeviceSetup again does not work. we simply leave the device in a semi-bad - state. there is no way to clean this up as far as I know. - --> - -</configuration> - diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt deleted file mode 100644 index 51e407d4cbff..000000000000 --- a/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.google.android.startop.iorap - -import android.content.Intent; -import android.net.Uri -import android.os.Parcel -import android.os.Parcelable -import androidx.test.filters.SmallTest -import com.google.android.startop.iorap.AppLaunchEvent; -import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunched -import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunchCancelled -import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunchFinished -import com.google.android.startop.iorap.AppLaunchEvent.IntentStarted; -import com.google.android.startop.iorap.AppLaunchEvent.IntentFailed; -import com.google.android.startop.iorap.AppLaunchEvent.ReportFullyDrawn -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized - - -/** - * Basic unit tests to test all of the [AppLaunchEvent]s in [com.google.android.startop.iorap]. - */ -@SmallTest -class AppLaunchEventTest { - /** - * Test for IntentStarted. - */ - @Test - fun testIntentStarted() { - var intent = Intent() - val valid = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 1L) - val copy = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 1L) - val noneCopy1 = IntentStarted(/* sequenceId= */1L, intent, /* timestampNs= */ 1L) - val noneCopy2 = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 2L) - val noneCopy3 = IntentStarted(/* sequenceId= */2L, Intent(), /* timestampNs= */ 1L) - - // equals(Object other) - assertThat(valid).isEqualTo(copy) - assertThat(valid).isNotEqualTo(noneCopy1) - assertThat(valid).isNotEqualTo(noneCopy2) - assertThat(valid).isNotEqualTo(noneCopy3) - - // test toString() - val result = valid.toString() - assertThat(result).isEqualTo("IntentStarted{sequenceId=2, intent=Intent { } , timestampNs=1}") - } - - /** - * Test for IntentFailed. - */ - @Test - fun testIntentFailed() { - val valid = IntentFailed(/* sequenceId= */2L) - val copy = IntentFailed(/* sequenceId= */2L) - val noneCopy = IntentFailed(/* sequenceId= */1L) - - // equals(Object other) - assertThat(valid).isEqualTo(copy) - assertThat(valid).isNotEqualTo(noneCopy) - - // test toString() - val result = valid.toString() - assertThat(result).isEqualTo("IntentFailed{sequenceId=2}") - } - - /** - * Test for ActivityLaunched. - */ - @Test - fun testActivityLaunched() { - //var activityRecord = - val valid = ActivityLaunched(/* sequenceId= */2L, "test".toByteArray(), - /* temperature= */ 0) - val copy = ActivityLaunched(/* sequenceId= */2L, "test".toByteArray(), - /* temperature= */ 0) - val noneCopy1 = ActivityLaunched(/* sequenceId= */1L, "test".toByteArray(), - /* temperature= */ 0) - val noneCopy2 = ActivityLaunched(/* sequenceId= */1L, "test".toByteArray(), - /* temperature= */ 1) - val noneCopy3 = ActivityLaunched(/* sequenceId= */1L, "test1".toByteArray(), - /* temperature= */ 0) - - // equals(Object other) - assertThat(valid).isEqualTo(copy) - assertThat(valid).isNotEqualTo(noneCopy1) - assertThat(valid).isNotEqualTo(noneCopy2) - assertThat(valid).isNotEqualTo(noneCopy3) - - // test toString() - val result = valid.toString() - assertThat(result).isEqualTo("ActivityLaunched{sequenceId=2, test, temperature=0}") - } - - - /** - * Test for ActivityLaunchFinished. - */ - @Test - fun testActivityLaunchFinished() { - val valid = ActivityLaunchFinished(/* sequenceId= */2L, "test".toByteArray(), - /* timestampNs= */ 1L) - val copy = ActivityLaunchFinished(/* sequenceId= */2L, "test".toByteArray(), - /* timestampNs= */ 1L) - val noneCopy1 = ActivityLaunchFinished(/* sequenceId= */1L, "test".toByteArray(), - /* timestampNs= */ 1L) - val noneCopy2 = ActivityLaunchFinished(/* sequenceId= */1L, "test".toByteArray(), - /* timestampNs= */ 2L) - val noneCopy3 = ActivityLaunchFinished(/* sequenceId= */2L, "test1".toByteArray(), - /* timestampNs= */ 1L) - - // equals(Object other) - assertThat(valid).isEqualTo(copy) - assertThat(valid).isNotEqualTo(noneCopy1) - assertThat(valid).isNotEqualTo(noneCopy2) - assertThat(valid).isNotEqualTo(noneCopy3) - - // test toString() - val result = valid.toString() - assertThat(result).isEqualTo("ActivityLaunchFinished{sequenceId=2, test, timestampNs=1}") - } - - /** - * Test for ActivityLaunchCancelled. - */ - @Test - fun testActivityLaunchCancelled() { - val valid = ActivityLaunchCancelled(/* sequenceId= */2L, "test".toByteArray()) - val copy = ActivityLaunchCancelled(/* sequenceId= */2L, "test".toByteArray()) - val noneCopy1 = ActivityLaunchCancelled(/* sequenceId= */1L, "test".toByteArray()) - val noneCopy2 = ActivityLaunchCancelled(/* sequenceId= */2L, "test1".toByteArray()) - - // equals(Object other) - assertThat(valid).isEqualTo(copy) - assertThat(valid).isNotEqualTo(noneCopy1) - assertThat(valid).isNotEqualTo(noneCopy2) - - // test toString() - val result = valid.toString() - assertThat(result).isEqualTo("ActivityLaunchCancelled{sequenceId=2, test}") - } - - /** - * Test for ReportFullyDrawn. - */ - @Test - fun testReportFullyDrawn() { - val valid = ReportFullyDrawn(/* sequenceId= */2L, "test".toByteArray(), /* timestampNs= */ 1L) - val copy = ReportFullyDrawn(/* sequenceId= */2L, "test".toByteArray(), /* timestampNs= */ 1L) - val noneCopy1 = ReportFullyDrawn(/* sequenceId= */1L, "test".toByteArray(), - /* timestampNs= */ 1L) - val noneCopy2 = ReportFullyDrawn(/* sequenceId= */1L, "test".toByteArray(), - /* timestampNs= */ 1L) - val noneCopy3 = ReportFullyDrawn(/* sequenceId= */2L, "test1".toByteArray(), - /* timestampNs= */ 1L) - - // equals(Object other) - assertThat(valid).isEqualTo(copy) - assertThat(valid).isNotEqualTo(noneCopy1) - assertThat(valid).isNotEqualTo(noneCopy2) - assertThat(valid).isNotEqualTo(noneCopy3) - - // test toString() - val result = valid.toString() - assertThat(result).isEqualTo("ReportFullyDrawn{sequenceId=2, test, timestampNs=1}") - } -} diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt deleted file mode 100644 index 18c249136d05..000000000000 --- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.google.android.startop.iorap - -import android.net.Uri -import android.os.ServiceManager -import androidx.test.filters.FlakyTest -import androidx.test.filters.MediumTest -import org.junit.Test -import org.mockito.Mockito.argThat -import org.mockito.Mockito.eq -import org.mockito.Mockito.inOrder -import org.mockito.Mockito.spy -import org.mockito.Mockito.timeout - -// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it") -@MediumTest -@FlakyTest(bugId = 149098310) // Failing on cuttlefish with SecurityException. -class IIorapIntegrationTest { - /** - * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found - */ - private val iorapService: IIorap by lazy { - // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply. - IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd")) - - // Use 'adb shell setenforce 0' otherwise this whole test fails, - // because the servicemanager is not allowed to hand out the binder token for iorapd. - - // TODO: implement the selinux policies for iorapd. - } - - // A dummy binder stub implementation is required to use with mockito#spy. - // Mockito overrides the methods at runtime and tracks how methods were invoked. - open class DummyTaskListener : ITaskListener.Stub() { - // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions - // from using the mockito matchers (eq, argThat, etc). - override fun onProgress(requestId: RequestId?, result: TaskResult?) { - } - - override fun onComplete(requestId: RequestId?, result: TaskResult?) { - } - } - - private fun testAnyMethod(func: (RequestId) -> Unit) { - val taskListener = spy(DummyTaskListener())!! - - // FIXME: b/149098310 - return - - try { - iorapService.setTaskListener(taskListener) - // Note: Binder guarantees total order for oneway messages sent to the same binder - // interface, so we don't need any additional blocking here before sending later calls. - - // Every new method call should have a unique request id. - val requestId = RequestId.nextValueForSequence()!! - - // Apply the specific function under test. - func(requestId) - - // Typical mockito behavior is to allow any-order callbacks, but we want to test order. - val inOrder = inOrder(taskListener) - - // The "stub" behavior of iorapd is that every request immediately gets a response of - // BEGAN,ONGOING,COMPLETED - inOrder.verify(taskListener, timeout(100)) - .onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN }) - inOrder.verify(taskListener, timeout(100)) - .onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING }) - inOrder.verify(taskListener, timeout(100)) - .onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED }) - inOrder.verifyNoMoreInteractions() - } finally { - // iorapService.setTaskListener(null) - // FIXME: null is broken, C++ side sees a non-null object. - } - } - - @Test - fun testOnPackageEvent() { - // FIXME (b/137134253): implement PackageEvent parsing on the C++ side. - // This is currently (silently: b/137135024) failing because IIorap is 'oneway' and the - // C++ PackageEvent un-parceling fails since its not implemented fully. - /* - testAnyMethod { requestId : RequestId -> - iorapService.onPackageEvent(requestId, - PackageEvent.createReplaced( - Uri.parse("https://www.google.com"), "com.fake.package")) - } - */ - } - - @Test - fun testOnAppIntentEvent() { - testAnyMethod { requestId: RequestId -> - iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged( - ActivityInfo("dont care", "dont care"), - ActivityInfo("dont care 2", "dont care 2"))) - } - } - - @Test - fun testOnAppLaunchEvent() { - testAnyMethod { requestId : RequestId -> - iorapService.onAppLaunchEvent(requestId, AppLaunchEvent.IntentFailed(/*sequenceId*/123)) - } - } - - @Test - fun testOnSystemServiceEvent() { - testAnyMethod { requestId: RequestId -> - iorapService.onSystemServiceEvent(requestId, - SystemServiceEvent(SystemServiceEvent.TYPE_START)) - } - } - - @Test - fun testOnSystemServiceUserEvent() { - testAnyMethod { requestId: RequestId -> - iorapService.onSystemServiceUserEvent(requestId, - SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 0)) - } - } -} diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt deleted file mode 100644 index 150577a21f5a..000000000000 --- a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.google.android.startop.iorap - -import android.net.Uri -import android.os.Parcel -import android.os.Parcelable -import androidx.test.filters.SmallTest -import org.junit.Test -import org.junit.runner.RunWith -import com.google.common.truth.Truth.assertThat -import org.junit.runners.Parameterized - -/** - * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap] - * have a valid-conforming interface implementation. - */ -@SmallTest -@RunWith(Parameterized::class) -class ParcelablesTest<T : Parcelable>(private val inputData: InputData<T>) { - companion object { - private val initialRequestId = RequestId.nextValueForSequence()!! - - @JvmStatic - @Parameterized.Parameters - fun data() = listOf( - InputData( - newActivityInfo(), - newActivityInfo(), - ActivityInfo("some package", "some other activity")), - InputData( - ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()), - ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()), - ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED, - newActivityInfo())), - InputData( - AppIntentEvent.createDefaultIntentChanged(newActivityInfo(), - newActivityInfoOther()), - AppIntentEvent.createDefaultIntentChanged(newActivityInfo(), - newActivityInfoOther()), - AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(), - newActivityInfo())), - InputData( - PackageEvent.createReplaced(newUri(), "some package"), - PackageEvent.createReplaced(newUri(), "some package"), - PackageEvent.createReplaced(newUri(), "some other package") - ), - InputData(initialRequestId, cloneRequestId(initialRequestId), - RequestId.nextValueForSequence()), - InputData( - SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE), - SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE), - SystemServiceEvent(SystemServiceEvent.TYPE_START)), - InputData( - SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345), - SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345), - SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)), - InputData( - TaskResult(TaskResult.STATE_COMPLETED), - TaskResult(TaskResult.STATE_COMPLETED), - TaskResult(TaskResult.STATE_ONGOING)) - ) - - private fun newActivityInfo(): ActivityInfo { - return ActivityInfo("some package", "some activity") - } - - private fun newActivityInfoOther(): ActivityInfo { - return ActivityInfo("some package 2", "some activity 2") - } - - private fun newUri(): Uri { - return Uri.parse("https://www.google.com") - } - - private fun cloneRequestId(requestId: RequestId): RequestId { - val constructor = requestId::class.java.declaredConstructors[0] - constructor.isAccessible = true - return constructor.newInstance(requestId.requestId) as RequestId - } - } - - /** - * Test for [Object.equals] implementation. - */ - @Test - fun testEquality() { - assertThat(inputData.valid).isEqualTo(inputData.valid) - assertThat(inputData.valid).isEqualTo(inputData.validCopy) - assertThat(inputData.valid).isNotEqualTo(inputData.validOther) - } - - /** - * Test for [Parcelable] implementation. - */ - @Test - fun testParcelRoundTrip() { - // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data. - val assertParcels = { it: T, data: InputData<T> -> - val parcel = Parcel.obtain() - it.writeToParcel(parcel, 0) - parcel.setDataPosition(0) // future reads will see all previous writes. - assertThat(it).isEqualTo(data.createFromParcel(parcel)) - parcel.recycle() - } - - assertParcels(inputData.valid, inputData) - assertParcels(inputData.validCopy, inputData) - assertParcels(inputData.validOther, inputData) - } - - data class InputData<T : Parcelable>(val valid: T, val validCopy: T, val validOther: T) { - val kls = valid.javaClass - init { - assertThat(valid).isNotSameInstanceAs(validCopy) - // Don't use isInstanceOf because of phantom warnings in intellij about Class! - assertThat(validCopy.javaClass).isEqualTo(valid.javaClass) - assertThat(validOther.javaClass).isEqualTo(valid.javaClass) - } - - fun createFromParcel(parcel: Parcel): T { - val field = kls.getDeclaredField("CREATOR") - val creator = field.get(null) as Parcelable.Creator<T> - - return creator.createFromParcel(parcel) - } - } -} |