1761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes/*
2761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * Copyright 2017 The Android Open Source Project
3761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *
4761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * Licensed under the Apache License, Version 2.0 (the "License");
5761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * you may not use this file except in compliance with the License.
6761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * You may obtain a copy of the License at
7761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *
8761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *      http://www.apache.org/licenses/LICENSE-2.0
9761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *
10761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * Unless required by applicable law or agreed to in writing, software
11761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * distributed under the License is distributed on an "AS IS" BASIS,
12761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * See the License for the specific language governing permissions and
14761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * limitations under the License.
15761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes */
16761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
1755d11f6197fcfc80567fe96699f334c9813e0f0bAlan Stokespackage com.android.server.pm.dex;
18761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
19761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport static com.google.common.truth.Truth.assertThat;
20761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
21761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport android.content.Context;
22761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport android.support.test.InstrumentationRegistry;
2355d11f6197fcfc80567fe96699f334c9813e0f0bAlan Stokesimport android.support.test.filters.LargeTest;
24761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport android.util.EventLog;
25761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport dalvik.system.DexClassLoader;
26761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
27761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.After;
28761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.AfterClass;
29761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.Before;
30761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.BeforeClass;
31761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.Test;
32761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.runner.RunWith;
33761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport org.junit.runners.JUnit4;
34761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
35761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.io.File;
36761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.io.FileOutputStream;
37761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.io.InputStream;
38761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.io.OutputStream;
39761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.security.MessageDigest;
40761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.util.ArrayList;
41761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.util.Formatter;
42761d618a131656f500bf904bc9072f69f27af3b7Alan Stokesimport java.util.List;
43761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
44761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes/**
45761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * Integration tests for {@link com.android.server.pm.dex.DexLogger}.
46761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *
47761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * The setup for the test dynamically loads code in a jar extracted
48761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * from our assets (a secondary dex file).
49761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *
50761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * We then use adb to trigger secondary dex file reconcilation (and
51761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * wait for it to complete). As a side-effect of this DexLogger should
52761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * be notified of the file and should log the hash of the file's name
53761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * and content.  We verify that this message appears in the event log.
54761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes *
55761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes * Run with "atest DexLoggerIntegrationTests".
56761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes */
5755d11f6197fcfc80567fe96699f334c9813e0f0bAlan Stokes@LargeTest
58761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes@RunWith(JUnit4.class)
59761d618a131656f500bf904bc9072f69f27af3b7Alan Stokespublic final class DexLoggerIntegrationTests {
60761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
61761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest";
62761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
631e24bf6043395ec316a368f92d9e2a144c7f57ceAlan Stokes    // Event log tag used for SNET related events
64761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    private static final int SNET_TAG = 0x534e4554;
651e24bf6043395ec316a368f92d9e2a144c7f57ceAlan Stokes    // Subtag used to distinguish dynamic code loading events
66761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    private static final String DCL_SUBTAG = "dcl";
67761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
68761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    // Obtained via "echo -n copied.jar | sha256sum"
69761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    private static final String EXPECTED_NAME_HASH =
70761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
71761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
72761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    private static String expectedContentHash;
73761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
74761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    @BeforeClass
75761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    public static void setUpAll() throws Exception {
76761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        Context context = InstrumentationRegistry.getTargetContext();
77761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        MessageDigest hasher = MessageDigest.getInstance("SHA-256");
78761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
79761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        // Copy the jar from our Java resources to a private data directory
80761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar");
811e24bf6043395ec316a368f92d9e2a144c7f57ceAlan Stokes        Class<?> thisClass = DexLoggerIntegrationTests.class;
821e24bf6043395ec316a368f92d9e2a144c7f57ceAlan Stokes        try (InputStream input = thisClass.getResourceAsStream("/javalib.jar");
83761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                OutputStream output = new FileOutputStream(privateCopy)) {
84761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            byte[] buffer = new byte[1024];
85761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            while (true) {
86761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                int numRead = input.read(buffer);
87761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                if (numRead < 0) {
88761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                    break;
89761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                }
90761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                output.write(buffer, 0, numRead);
91761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                hasher.update(buffer, 0, numRead);
92761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            }
93761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        }
94761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
95761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        // Remember the SHA-256 of the file content to check that it is the same as
96761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        // the value we see logged.
97761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        Formatter formatter = new Formatter();
98761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        for (byte b : hasher.digest()) {
99761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            formatter.format("%02X", b);
100761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        }
101761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        expectedContentHash = formatter.toString();
102761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
103761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        // Feed the jar to a class loader and make sure it contains what we expect.
104761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        ClassLoader loader =
105761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                new DexClassLoader(
106761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                    privateCopy.toString(), null, null, context.getClass().getClassLoader());
107761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        loader.loadClass("com.android.dcl.Simple");
108761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    }
109761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
110761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    @Test
111761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    public void testDexLoggerReconcileGeneratesEvents() throws Exception {
112761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        int[] tagList = new int[] { SNET_TAG };
113761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        List<EventLog.Event> events = new ArrayList<>();
114761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
115761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        // There may already be events in the event log - figure out the most recent one
116761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        EventLog.readEvents(tagList, events);
1171e24bf6043395ec316a368f92d9e2a144c7f57ceAlan Stokes        long previousEventNanos =
1181e24bf6043395ec316a368f92d9e2a144c7f57ceAlan Stokes                events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
119761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        events.clear();
120761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
121761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        Process process = Runtime.getRuntime().exec(
122761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME);
123761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        int exitCode = process.waitFor();
124761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        assertThat(exitCode).isEqualTo(0);
125761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
126761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        int myUid = android.os.Process.myUid();
127761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash;
128761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
129761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        EventLog.readEvents(tagList, events);
130761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        boolean found = false;
131761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        for (EventLog.Event event : events) {
132761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            if (event.getTimeNanos() <= previousEventNanos) {
133761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                continue;
134761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            }
135761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            Object[] data = (Object[]) event.getData();
136761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
137761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            // We only care about DCL events that we generated.
138761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            String subTag = (String) data[0];
139761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            if (!DCL_SUBTAG.equals(subTag)) {
140761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                continue;
141761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            }
142761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            int uid = (int) data[1];
143761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            if (uid != myUid) {
144761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes                continue;
145761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            }
146761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
147761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            String message = (String) data[2];
148761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            assertThat(message).isEqualTo(expectedMessage);
149761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes            found = true;
150761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        }
151761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes
152761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes        assertThat(found).isTrue();
153761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes    }
154761d618a131656f500bf904bc9072f69f27af3b7Alan Stokes}
155