Loading tests/DexLoggerIntegrationTests/Android.mk 0 → 100644 +49 −0 Original line number Diff line number Diff line # # Copyright 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # LOCAL_PATH:= $(call my-dir) # Build a tiny library that the test app can dynamically load include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DexLoggerTestLibrary LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl) include $(BUILD_JAVA_LIBRARY) dexloggertest_jar := $(LOCAL_BUILT_MODULE) # Build the test app itself include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm) LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ truth-prebuilt \ # This gets us the javalib.jar built by DexLoggerTestLibrary above. LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar) include $(BUILD_PACKAGE) tests/DexLoggerIntegrationTests/AndroidManifest.xml 0 → 100644 +35 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2017 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.dexloggertest"> <!-- Tests feature introduced in P (27) --> <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" /> <uses-permission android:name="android.permission.READ_LOGS" /> <application> <uses-library android:name="android.test.runner" /> </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.frameworks.dexloggertest" android:label="Integration test for DexLogger" /> </manifest> tests/DexLoggerIntegrationTests/AndroidTest.xml 0 → 100644 +29 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2017 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <configuration description="Runs DexLogger Integration Tests"> <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> <option name="test-file-name" value="DexLoggerIntegrationTests.apk"/> <option name="cleanup-apks" value="true"/> </target_preparer> <option name="test-suite-tag" value="apct"/> <option name="test-tag" value="DexLoggerIntegrationTests"/> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.frameworks.dexloggertest"/> <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> </test> </configuration> tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dcl; /** Dummy class which is built into a jar purely so we can pass it to DexClassLoader. */ public final class Simple { public Simple() {} } tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.util.EventLog; import dalvik.system.DexClassLoader; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Formatter; import java.util.List; /** * Integration tests for {@link com.android.server.pm.dex.DexLogger}. * * The setup for the test dynamically loads code in a jar extracted * from our assets (a secondary dex file). * * We then use adb to trigger secondary dex file reconcilation (and * wait for it to complete). As a side-effect of this DexLogger should * be notified of the file and should log the hash of the file's name * and content. We verify that this message appears in the event log. * * Run with "atest DexLoggerIntegrationTests". */ @RunWith(JUnit4.class) public final class DexLoggerIntegrationTests { private static final String TAG = DexLoggerIntegrationTests.class.getSimpleName(); private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest"; private static final int SNET_TAG = 0x534e4554; private static final String DCL_SUBTAG = "dcl"; // Obtained via "echo -n copied.jar | sha256sum" private static final String EXPECTED_NAME_HASH = "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C"; private static String expectedContentHash; @BeforeClass public static void setUpAll() throws Exception { Context context = InstrumentationRegistry.getTargetContext(); MessageDigest hasher = MessageDigest.getInstance("SHA-256"); // Copy the jar from our Java resources to a private data directory File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar"); try (InputStream input = DexLoggerIntegrationTests.class.getResourceAsStream("/javalib.jar"); OutputStream output = new FileOutputStream(privateCopy)) { byte[] buffer = new byte[1024]; while (true) { int numRead = input.read(buffer); if (numRead < 0) { break; } output.write(buffer, 0, numRead); hasher.update(buffer, 0, numRead); } } // Remember the SHA-256 of the file content to check that it is the same as // the value we see logged. Formatter formatter = new Formatter(); for (byte b : hasher.digest()) { formatter.format("%02X", b); } expectedContentHash = formatter.toString(); // Feed the jar to a class loader and make sure it contains what we expect. ClassLoader loader = new DexClassLoader( privateCopy.toString(), null, null, context.getClass().getClassLoader()); loader.loadClass("com.android.dcl.Simple"); } @Test public void testDexLoggerReconcileGeneratesEvents() throws Exception { int[] tagList = new int[] { SNET_TAG }; List<EventLog.Event> events = new ArrayList<>(); // There may already be events in the event log - figure out the most recent one EventLog.readEvents(tagList, events); long previousEventNanos = events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos(); events.clear(); Process process = Runtime.getRuntime().exec( "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME); int exitCode = process.waitFor(); assertThat(exitCode).isEqualTo(0); int myUid = android.os.Process.myUid(); String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash; EventLog.readEvents(tagList, events); boolean found = false; for (EventLog.Event event : events) { if (event.getTimeNanos() <= previousEventNanos) { continue; } Object[] data = (Object[]) event.getData(); // We only care about DCL events that we generated. String subTag = (String) data[0]; if (!DCL_SUBTAG.equals(subTag)) { continue; } int uid = (int) data[1]; if (uid != myUid) { continue; } String message = (String) data[2]; assertThat(message).isEqualTo(expectedMessage); found = true; } assertThat(found).isTrue(); } } Loading
tests/DexLoggerIntegrationTests/Android.mk 0 → 100644 +49 −0 Original line number Diff line number Diff line # # Copyright 2017 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # LOCAL_PATH:= $(call my-dir) # Build a tiny library that the test app can dynamically load include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DexLoggerTestLibrary LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl) include $(BUILD_JAVA_LIBRARY) dexloggertest_jar := $(LOCAL_BUILT_MODULE) # Build the test app itself include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm) LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ truth-prebuilt \ # This gets us the javalib.jar built by DexLoggerTestLibrary above. LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar) include $(BUILD_PACKAGE)
tests/DexLoggerIntegrationTests/AndroidManifest.xml 0 → 100644 +35 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2017 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.dexloggertest"> <!-- Tests feature introduced in P (27) --> <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" /> <uses-permission android:name="android.permission.READ_LOGS" /> <application> <uses-library android:name="android.test.runner" /> </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.frameworks.dexloggertest" android:label="Integration test for DexLogger" /> </manifest>
tests/DexLoggerIntegrationTests/AndroidTest.xml 0 → 100644 +29 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2017 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <configuration description="Runs DexLogger Integration Tests"> <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> <option name="test-file-name" value="DexLoggerIntegrationTests.apk"/> <option name="cleanup-apks" value="true"/> </target_preparer> <option name="test-suite-tag" value="apct"/> <option name="test-tag" value="DexLoggerIntegrationTests"/> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.frameworks.dexloggertest"/> <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> </test> </configuration>
tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dcl; /** Dummy class which is built into a jar purely so we can pass it to DexClassLoader. */ public final class Simple { public Simple() {} }
tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.util.EventLog; import dalvik.system.DexClassLoader; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Formatter; import java.util.List; /** * Integration tests for {@link com.android.server.pm.dex.DexLogger}. * * The setup for the test dynamically loads code in a jar extracted * from our assets (a secondary dex file). * * We then use adb to trigger secondary dex file reconcilation (and * wait for it to complete). As a side-effect of this DexLogger should * be notified of the file and should log the hash of the file's name * and content. We verify that this message appears in the event log. * * Run with "atest DexLoggerIntegrationTests". */ @RunWith(JUnit4.class) public final class DexLoggerIntegrationTests { private static final String TAG = DexLoggerIntegrationTests.class.getSimpleName(); private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest"; private static final int SNET_TAG = 0x534e4554; private static final String DCL_SUBTAG = "dcl"; // Obtained via "echo -n copied.jar | sha256sum" private static final String EXPECTED_NAME_HASH = "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C"; private static String expectedContentHash; @BeforeClass public static void setUpAll() throws Exception { Context context = InstrumentationRegistry.getTargetContext(); MessageDigest hasher = MessageDigest.getInstance("SHA-256"); // Copy the jar from our Java resources to a private data directory File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar"); try (InputStream input = DexLoggerIntegrationTests.class.getResourceAsStream("/javalib.jar"); OutputStream output = new FileOutputStream(privateCopy)) { byte[] buffer = new byte[1024]; while (true) { int numRead = input.read(buffer); if (numRead < 0) { break; } output.write(buffer, 0, numRead); hasher.update(buffer, 0, numRead); } } // Remember the SHA-256 of the file content to check that it is the same as // the value we see logged. Formatter formatter = new Formatter(); for (byte b : hasher.digest()) { formatter.format("%02X", b); } expectedContentHash = formatter.toString(); // Feed the jar to a class loader and make sure it contains what we expect. ClassLoader loader = new DexClassLoader( privateCopy.toString(), null, null, context.getClass().getClassLoader()); loader.loadClass("com.android.dcl.Simple"); } @Test public void testDexLoggerReconcileGeneratesEvents() throws Exception { int[] tagList = new int[] { SNET_TAG }; List<EventLog.Event> events = new ArrayList<>(); // There may already be events in the event log - figure out the most recent one EventLog.readEvents(tagList, events); long previousEventNanos = events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos(); events.clear(); Process process = Runtime.getRuntime().exec( "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME); int exitCode = process.waitFor(); assertThat(exitCode).isEqualTo(0); int myUid = android.os.Process.myUid(); String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash; EventLog.readEvents(tagList, events); boolean found = false; for (EventLog.Event event : events) { if (event.getTimeNanos() <= previousEventNanos) { continue; } Object[] data = (Object[]) event.getData(); // We only care about DCL events that we generated. String subTag = (String) data[0]; if (!DCL_SUBTAG.equals(subTag)) { continue; } int uid = (int) data[1]; if (uid != myUid) { continue; } String message = (String) data[2]; assertThat(message).isEqualTo(expectedMessage); found = true; } assertThat(found).isTrue(); } }