1 /* 2 * Copyright 2020 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 17 import dalvik.system.PathClassLoader; 18 import java.lang.reflect.Field; 19 import java.lang.reflect.Constructor; 20 import java.lang.reflect.Method; 21 22 class Main { 23 static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar"; 24 static final String SECONDARY_DEX_FILE = 25 System.getenv("DEX_LOCATION") + "/596-app-images-ex.jar"; 26 static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); 27 28 static class Inner { 29 final public static int abc = 10; 30 } 31 32 static class Nested { 33 34 } 35 main(String[] args)36 public static void main(String[] args) throws Exception { 37 System.loadLibrary(args[0]); 38 39 testAppImageLoaded(); 40 testInitializedClasses(); 41 testInternedStrings(); 42 testReloadInternedString(); 43 testClassesOutsideAppImage(); 44 testLoadingSecondaryAppImage(); 45 } 46 checkAppImageLoaded(String name)47 public static native boolean checkAppImageLoaded(String name); checkAppImageContains(Class<?> klass)48 public static native boolean checkAppImageContains(Class<?> klass); checkInitialized(Class<?> klass)49 public static native boolean checkInitialized(Class<?> klass); 50 testAppImageLoaded()51 public static void testAppImageLoaded() throws Exception { 52 assertTrue("App image is loaded", checkAppImageLoaded("596-app-images")); 53 assertTrue("App image contains Inner", checkAppImageContains(Inner.class)); 54 } 55 testInitializedClasses()56 public static void testInitializedClasses() throws Exception { 57 assertInitialized(Inner.class); 58 assertInitialized(Nested.class); 59 assertInitialized(StaticFields.class); 60 assertInitialized(StaticFieldsInitSub.class); 61 assertInitialized(StaticFieldsInit.class); 62 assertInitialized(StaticInternString.class); 63 } 64 assertInitialized(Class<?> klass)65 private static void assertInitialized(Class<?> klass) { 66 assertTrue(klass.toString() + " is preinitialized", checkInitialized(klass)); 67 } 68 testInternedStrings()69 public static void testInternedStrings() throws Exception { 70 StringBuffer sb = new StringBuffer(); 71 sb.append("java."); 72 sb.append("abc."); 73 sb.append("Action"); 74 75 String tmp = sb.toString(); 76 String intern = tmp.intern(); 77 78 assertNotSame("Dynamically constructed string is not interned", tmp, intern); 79 assertEquals("Static string on initialized class is matches runtime interned string", intern, 80 StaticInternString.intent); 81 assertEquals("Static string on initialized class is pre-interned", BootInternedString.boot, 82 BootInternedString.boot.intern()); 83 84 // TODO: Does this next check really provide us anything? 85 Field f = StaticInternString.class.getDeclaredField("intent"); 86 assertEquals("String literals are interned properly", intern, f.get(null)); 87 88 assertEquals("String literals are interned properly across classes", 89 StaticInternString.getIntent(), StaticInternString2.getIntent()); 90 } 91 testReloadInternedString()92 public static void testReloadInternedString() throws Exception { 93 // reload the class StaticInternString, check whether static strings interned properly 94 PathClassLoader loader = new PathClassLoader(DEX_FILE, LIBRARY_SEARCH_PATH, null); 95 Class<?> staticInternString = loader.loadClass("StaticInternString"); 96 assertTrue("Class in app image isn't loaded a second time after loading dex file again", 97 checkAppImageContains(staticInternString)); 98 99 Method getIntent = staticInternString.getDeclaredMethod("getIntent"); 100 assertEquals("Interned strings are still interned after multiple dex loads", 101 StaticInternString.getIntent(), getIntent.invoke(staticInternString)); 102 } 103 testClassesOutsideAppImage()104 public static void testClassesOutsideAppImage() { 105 assertFalse("App image doesn't contain non-optimized class", 106 checkAppImageContains(NonOptimizedClass.class)); 107 assertFalse("App image didn't pre-initialize non-optimized class", 108 checkInitialized(NonOptimizedClass.class)); 109 } 110 testLoadingSecondaryAppImage()111 public static void testLoadingSecondaryAppImage() throws Exception { 112 final ClassLoader parent = Main.class.getClassLoader(); 113 114 // Initial check that the image isn't already loaded so we don't get bogus results below 115 assertFalse("Secondary app image isn't already loaded", 116 checkAppImageLoaded("596-app-images-ex")); 117 118 PathClassLoader pcl = new PathClassLoader(SECONDARY_DEX_FILE, parent); 119 120 assertTrue("Ensure app image is loaded if it should be", 121 checkAppImageLoaded("596-app-images-ex")); 122 123 Class<?> secondaryCls = pcl.loadClass("Secondary"); 124 assertTrue("Ensure Secondary class is in the app image if the CLC is correct", 125 checkAppImageContains(secondaryCls)); 126 assertTrue("Ensure Secondary class is preinitialized if the CLC is correct", 127 checkInitialized(secondaryCls)); 128 129 secondaryCls.getDeclaredMethod("go").invoke(null); 130 } 131 assertTrue(String message, boolean flag)132 private static void assertTrue(String message, boolean flag) { 133 if (flag) { 134 return; 135 } 136 throw new AssertionError(message); 137 } 138 assertEquals(String message, Object a, Object b)139 private static void assertEquals(String message, Object a, Object b) { 140 StringBuilder sb = new StringBuilder(message != null ? message : ""); 141 if (sb.length() > 0) { 142 sb.append(" "); 143 } 144 sb.append("expected:<").append(a).append("> but was:<").append(b).append(">"); 145 assertTrue(sb.toString(), (a == null && b == null) || (a != null && a.equals(b))); 146 } 147 assertFalse(String message, boolean flag)148 private static void assertFalse(String message, boolean flag) { 149 assertTrue(message, !flag); 150 } 151 assertNotSame(String message, Object a, Object b)152 private static void assertNotSame(String message, Object a, Object b) { 153 StringBuilder sb = new StringBuilder(message != null ? message : ""); 154 if (sb.length() > 0) { 155 sb.append(" "); 156 } 157 sb.append("unexpected sameness, found:<").append(a).append("> and:<").append(b).append(">"); 158 assertTrue(sb.toString(), a != b); 159 } 160 } 161 162 class StaticFields { 163 public static int abc; 164 } 165 166 class StaticFieldsInitSub extends StaticFieldsInit { 167 final public static int def = 10; 168 } 169 170 class StaticFieldsInit { 171 final public static int abc = 10; 172 } 173 174 class StaticInternString { 175 final public static String intent = "java.abc.Action"; getIntent()176 static public String getIntent() { 177 return intent; 178 } 179 } 180 181 class BootInternedString { 182 final public static String boot = "double"; 183 } 184 185 class StaticInternString2 { 186 final public static String intent = "java.abc.Action"; 187 getIntent()188 static String getIntent() { 189 return intent; 190 } 191 } 192 193 class NonOptimizedClass {} 194