1/* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.pm.dex; 18 19import static com.google.common.truth.Truth.assertThat; 20 21import android.content.Context; 22import android.support.test.InstrumentationRegistry; 23import android.support.test.filters.LargeTest; 24import android.util.EventLog; 25import dalvik.system.DexClassLoader; 26 27import org.junit.After; 28import org.junit.AfterClass; 29import org.junit.Before; 30import org.junit.BeforeClass; 31import org.junit.Test; 32import org.junit.runner.RunWith; 33import org.junit.runners.JUnit4; 34 35import java.io.File; 36import java.io.FileOutputStream; 37import java.io.InputStream; 38import java.io.OutputStream; 39import java.security.MessageDigest; 40import java.util.ArrayList; 41import java.util.Formatter; 42import java.util.List; 43 44/** 45 * Integration tests for {@link com.android.server.pm.dex.DexLogger}. 46 * 47 * The setup for the test dynamically loads code in a jar extracted 48 * from our assets (a secondary dex file). 49 * 50 * We then use adb to trigger secondary dex file reconcilation (and 51 * wait for it to complete). As a side-effect of this DexLogger should 52 * be notified of the file and should log the hash of the file's name 53 * and content. We verify that this message appears in the event log. 54 * 55 * Run with "atest DexLoggerIntegrationTests". 56 */ 57@LargeTest 58@RunWith(JUnit4.class) 59public final class DexLoggerIntegrationTests { 60 61 private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest"; 62 63 // Event log tag used for SNET related events 64 private static final int SNET_TAG = 0x534e4554; 65 // Subtag used to distinguish dynamic code loading events 66 private static final String DCL_SUBTAG = "dcl"; 67 68 // Obtained via "echo -n copied.jar | sha256sum" 69 private static final String EXPECTED_NAME_HASH = 70 "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C"; 71 72 private static String expectedContentHash; 73 74 @BeforeClass 75 public static void setUpAll() throws Exception { 76 Context context = InstrumentationRegistry.getTargetContext(); 77 MessageDigest hasher = MessageDigest.getInstance("SHA-256"); 78 79 // Copy the jar from our Java resources to a private data directory 80 File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar"); 81 Class<?> thisClass = DexLoggerIntegrationTests.class; 82 try (InputStream input = thisClass.getResourceAsStream("/javalib.jar"); 83 OutputStream output = new FileOutputStream(privateCopy)) { 84 byte[] buffer = new byte[1024]; 85 while (true) { 86 int numRead = input.read(buffer); 87 if (numRead < 0) { 88 break; 89 } 90 output.write(buffer, 0, numRead); 91 hasher.update(buffer, 0, numRead); 92 } 93 } 94 95 // Remember the SHA-256 of the file content to check that it is the same as 96 // the value we see logged. 97 Formatter formatter = new Formatter(); 98 for (byte b : hasher.digest()) { 99 formatter.format("%02X", b); 100 } 101 expectedContentHash = formatter.toString(); 102 103 // Feed the jar to a class loader and make sure it contains what we expect. 104 ClassLoader loader = 105 new DexClassLoader( 106 privateCopy.toString(), null, null, context.getClass().getClassLoader()); 107 loader.loadClass("com.android.dcl.Simple"); 108 } 109 110 @Test 111 public void testDexLoggerReconcileGeneratesEvents() throws Exception { 112 int[] tagList = new int[] { SNET_TAG }; 113 List<EventLog.Event> events = new ArrayList<>(); 114 115 // There may already be events in the event log - figure out the most recent one 116 EventLog.readEvents(tagList, events); 117 long previousEventNanos = 118 events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos(); 119 events.clear(); 120 121 Process process = Runtime.getRuntime().exec( 122 "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME); 123 int exitCode = process.waitFor(); 124 assertThat(exitCode).isEqualTo(0); 125 126 int myUid = android.os.Process.myUid(); 127 String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash; 128 129 EventLog.readEvents(tagList, events); 130 boolean found = false; 131 for (EventLog.Event event : events) { 132 if (event.getTimeNanos() <= previousEventNanos) { 133 continue; 134 } 135 Object[] data = (Object[]) event.getData(); 136 137 // We only care about DCL events that we generated. 138 String subTag = (String) data[0]; 139 if (!DCL_SUBTAG.equals(subTag)) { 140 continue; 141 } 142 int uid = (int) data[1]; 143 if (uid != myUid) { 144 continue; 145 } 146 147 String message = (String) data[2]; 148 assertThat(message).isEqualTo(expectedMessage); 149 found = true; 150 } 151 152 assertThat(found).isTrue(); 153 } 154} 155