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