Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 761d618a authored by Alan Stokes's avatar Alan Stokes
Browse files

Add integration test for DexLogger.

Bug: 63927552
Bug: 68703204
Test: atest DexLoggerIntegrationTests
Change-Id: Ib61cf433ef4729c4e4a0539682b5ddbefdb65575
parent 37d870bc
Loading
Loading
Loading
Loading
+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)
+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>
+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>
+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() {}
}
+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();
    }
}