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