1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2017 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 art.*; 18*795d594fSAndroid Build Coastguard Worker import java.lang.ref.*; 19*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.*; 20*795d594fSAndroid Build Coastguard Worker import java.util.*; 21*795d594fSAndroid Build Coastguard Worker import java.util.function.Consumer; 22*795d594fSAndroid Build Coastguard Worker import sun.misc.Unsafe; 23*795d594fSAndroid Build Coastguard Worker 24*795d594fSAndroid Build Coastguard Worker public class Main { 25*795d594fSAndroid Build Coastguard Worker public static class Transform { 26*795d594fSAndroid Build Coastguard Worker static { 27*795d594fSAndroid Build Coastguard Worker } 28*795d594fSAndroid Build Coastguard Worker 29*795d594fSAndroid Build Coastguard Worker public static Object SECRET_ARRAY = new byte[] {1, 2, 3, 4}; 30*795d594fSAndroid Build Coastguard Worker public static long SECRET_NUMBER = 42; 31*795d594fSAndroid Build Coastguard Worker foo()32*795d594fSAndroid Build Coastguard Worker public static void foo() {} 33*795d594fSAndroid Build Coastguard Worker } 34*795d594fSAndroid Build Coastguard Worker 35*795d594fSAndroid Build Coastguard Worker /* Base64 for 36*795d594fSAndroid Build Coastguard Worker * public static class Trasform { 37*795d594fSAndroid Build Coastguard Worker * static {} 38*795d594fSAndroid Build Coastguard Worker * public static Object AAA_PADDING; 39*795d594fSAndroid Build Coastguard Worker * public static Object SECRET_ARRAY; 40*795d594fSAndroid Build Coastguard Worker * public static long SECRET_NUMBER; 41*795d594fSAndroid Build Coastguard Worker * public static void foo() {} 42*795d594fSAndroid Build Coastguard Worker * public static void bar() {} 43*795d594fSAndroid Build Coastguard Worker * } 44*795d594fSAndroid Build Coastguard Worker */ 45*795d594fSAndroid Build Coastguard Worker public static final byte[] REDEFINED_DEX_FILE = 46*795d594fSAndroid Build Coastguard Worker Base64.getDecoder() 47*795d594fSAndroid Build Coastguard Worker .decode( 48*795d594fSAndroid Build Coastguard Worker "ZGV4CjAzNQDdmsOAlizFD4Ogb6+/mfSdVzhmL8e/mRcYBAAAcAAAAHhWNBIAAAAAAAAAAGADAAAU" 49*795d594fSAndroid Build Coastguard Worker + "AAAAcAAAAAcAAADAAAAAAQAAANwAAAADAAAA6AAAAAUAAAAAAQAAAQAAACgBAADQAgAASAEAAKwB" 50*795d594fSAndroid Build Coastguard Worker + "AAC2AQAAvgEAAMsBAADOAQAA4AEAAOgBAAAMAgAALAIAAEACAABLAgAAWQIAAGgCAABzAgAAdgIA" 51*795d594fSAndroid Build Coastguard Worker + "AIMCAACIAgAAjQIAAJMCAACaAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAADQAAAA0AAAAGAAAA" 52*795d594fSAndroid Build Coastguard Worker + "AAAAAAEABQACAAAAAQAFAAoAAAABAAAACwAAAAEAAAAAAAAAAQAAAAEAAAABAAAADwAAAAEAAAAQ" 53*795d594fSAndroid Build Coastguard Worker + "AAAABQAAAAEAAAABAAAAAQAAAAUAAAAAAAAACQAAAFADAAAhAwAAAAAAAAAAAAAAAAAAmgEAAAEA" 54*795d594fSAndroid Build Coastguard Worker + "AAAOAAAAAQABAAEAAACeAQAABAAAAHAQBAAAAA4AAAAAAAAAAACiAQAAAQAAAA4AAAAAAAAAAAAA" 55*795d594fSAndroid Build Coastguard Worker + "AKYBAAABAAAADgAHAA4ABgAOAAsADgAKAA4AAAAIPGNsaW5pdD4ABjxpbml0PgALQUFBX1BBRERJ" 56*795d594fSAndroid Build Coastguard Worker + "TkcAAUoAEExNYWluJFRyYW5zZm9ybTsABkxNYWluOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv" 57*795d594fSAndroid Build Coastguard Worker + "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09i" 58*795d594fSAndroid Build Coastguard Worker + "amVjdDsACU1haW4uamF2YQAMU0VDUkVUX0FSUkFZAA1TRUNSRVRfTlVNQkVSAAlUcmFuc2Zvcm0A" 59*795d594fSAndroid Build Coastguard Worker + "AVYAC2FjY2Vzc0ZsYWdzAANiYXIAA2ZvbwAEbmFtZQAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9u" 60*795d594fSAndroid Build Coastguard Worker + "LW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiYTgzNTJmMjU0ODg1MzYyY2NkOGQ5" 61*795d594fSAndroid Build Coastguard Worker + "MDlkMzUyOWM2MDA5NGRkODk2ZSIsInZlcnNpb24iOiIxLjYuMjAtZGV2In0AAgMBEhgCAgQCDgQJ" 62*795d594fSAndroid Build Coastguard Worker + "ERcMAwAEAAAJAQkBCQCIgATIAgGBgATcAgEJ9AIBCYgDAAAAAAACAAAAEgMAABgDAABEAwAAAAAA" 63*795d594fSAndroid Build Coastguard Worker + "AAAAAAAAAAAADwAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAHAAAAwAAAAAMAAAABAAAA" 64*795d594fSAndroid Build Coastguard Worker + "3AAAAAQAAAADAAAA6AAAAAUAAAAFAAAAAAEAAAYAAAABAAAAKAEAAAEgAAAEAAAASAEAAAMgAAAE" 65*795d594fSAndroid Build Coastguard Worker + "AAAAmgEAAAIgAAAUAAAArAEAAAQgAAACAAAAEgMAAAAgAAABAAAAIQMAAAMQAAACAAAAQAMAAAYg" 66*795d594fSAndroid Build Coastguard Worker + "AAABAAAAUAMAAAAQAAABAAAAYAMAAA=="); 67*795d594fSAndroid Build Coastguard Worker 68*795d594fSAndroid Build Coastguard Worker private interface TConsumer<T> { accept(T t)69*795d594fSAndroid Build Coastguard Worker public void accept(T t) throws Exception; 70*795d594fSAndroid Build Coastguard Worker } 71*795d594fSAndroid Build Coastguard Worker 72*795d594fSAndroid Build Coastguard Worker private interface ResetIterator<T> extends Iterator<T> { reset()73*795d594fSAndroid Build Coastguard Worker public void reset(); 74*795d594fSAndroid Build Coastguard Worker } 75*795d594fSAndroid Build Coastguard Worker 76*795d594fSAndroid Build Coastguard Worker private static final class BaseResetIter implements ResetIterator<Object[]> { 77*795d594fSAndroid Build Coastguard Worker private boolean have_next = true; 78*795d594fSAndroid Build Coastguard Worker next()79*795d594fSAndroid Build Coastguard Worker public Object[] next() { 80*795d594fSAndroid Build Coastguard Worker if (have_next) { 81*795d594fSAndroid Build Coastguard Worker have_next = false; 82*795d594fSAndroid Build Coastguard Worker return new Object[0]; 83*795d594fSAndroid Build Coastguard Worker } else { 84*795d594fSAndroid Build Coastguard Worker throw new NoSuchElementException("only one element"); 85*795d594fSAndroid Build Coastguard Worker } 86*795d594fSAndroid Build Coastguard Worker } 87*795d594fSAndroid Build Coastguard Worker hasNext()88*795d594fSAndroid Build Coastguard Worker public boolean hasNext() { 89*795d594fSAndroid Build Coastguard Worker return have_next; 90*795d594fSAndroid Build Coastguard Worker } 91*795d594fSAndroid Build Coastguard Worker reset()92*795d594fSAndroid Build Coastguard Worker public void reset() { 93*795d594fSAndroid Build Coastguard Worker have_next = true; 94*795d594fSAndroid Build Coastguard Worker } 95*795d594fSAndroid Build Coastguard Worker } 96*795d594fSAndroid Build Coastguard Worker main(String[] args)97*795d594fSAndroid Build Coastguard Worker public static void main(String[] args) throws Exception { 98*795d594fSAndroid Build Coastguard Worker System.loadLibrary(args[0]); 99*795d594fSAndroid Build Coastguard Worker Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); 100*795d594fSAndroid Build Coastguard Worker 101*795d594fSAndroid Build Coastguard Worker // Get the Unsafe object. 102*795d594fSAndroid Build Coastguard Worker Field f = Unsafe.class.getDeclaredField("THE_ONE"); 103*795d594fSAndroid Build Coastguard Worker f.setAccessible(true); 104*795d594fSAndroid Build Coastguard Worker Unsafe u = (Unsafe) f.get(null); 105*795d594fSAndroid Build Coastguard Worker 106*795d594fSAndroid Build Coastguard Worker // Get the offsets into the original Transform class of the fields 107*795d594fSAndroid Build Coastguard Worker long off_secret_array = genericFieldOffset(Transform.class.getDeclaredField("SECRET_ARRAY")); 108*795d594fSAndroid Build Coastguard Worker long off_secret_number = genericFieldOffset(Transform.class.getDeclaredField("SECRET_NUMBER")); 109*795d594fSAndroid Build Coastguard Worker 110*795d594fSAndroid Build Coastguard Worker System.out.println("Reading normally."); 111*795d594fSAndroid Build Coastguard Worker System.out.println("\tOriginal secret number is: " + Transform.SECRET_NUMBER); 112*795d594fSAndroid Build Coastguard Worker System.out.println("\tOriginal secret array is: " + Arrays.toString((byte[])Transform.SECRET_ARRAY)); 113*795d594fSAndroid Build Coastguard Worker System.out.println("Using unsafe to access values directly from memory."); 114*795d594fSAndroid Build Coastguard Worker System.out.println( 115*795d594fSAndroid Build Coastguard Worker "\tOriginal secret number is: " + u.getLong(Transform.class, off_secret_number)); 116*795d594fSAndroid Build Coastguard Worker System.out.println( 117*795d594fSAndroid Build Coastguard Worker "\tOriginal secret array is: " 118*795d594fSAndroid Build Coastguard Worker + Arrays.toString((byte[]) u.getObject(Transform.class, off_secret_array))); 119*795d594fSAndroid Build Coastguard Worker 120*795d594fSAndroid Build Coastguard Worker // Redefine in a way that changes the offsets. 121*795d594fSAndroid Build Coastguard Worker Redefinition.doCommonStructuralClassRedefinition(Transform.class, REDEFINED_DEX_FILE); 122*795d594fSAndroid Build Coastguard Worker 123*795d594fSAndroid Build Coastguard Worker // Make sure the value is the same. 124*795d594fSAndroid Build Coastguard Worker System.out.println("Reading normally post redefinition."); 125*795d594fSAndroid Build Coastguard Worker System.out.println("\tPost-redefinition secret number is: " + Transform.SECRET_NUMBER); 126*795d594fSAndroid Build Coastguard Worker System.out.println("\tPost-redefinition secret array is: " + Arrays.toString((byte[])Transform.SECRET_ARRAY)); 127*795d594fSAndroid Build Coastguard Worker 128*795d594fSAndroid Build Coastguard Worker // Get the (old) obsolete class from the ClassExt 129*795d594fSAndroid Build Coastguard Worker Field ext_field = Class.class.getDeclaredField("extData"); 130*795d594fSAndroid Build Coastguard Worker ext_field.setAccessible(true); 131*795d594fSAndroid Build Coastguard Worker Object ext_data = ext_field.get(Transform.class); 132*795d594fSAndroid Build Coastguard Worker Field oc_field = ext_data.getClass().getDeclaredField("obsoleteClass"); 133*795d594fSAndroid Build Coastguard Worker oc_field.setAccessible(true); 134*795d594fSAndroid Build Coastguard Worker Class<?> obsolete_class = (Class<?>) oc_field.get(ext_data); 135*795d594fSAndroid Build Coastguard Worker 136*795d594fSAndroid Build Coastguard Worker // Try reading the fields directly out of memory using unsafe. 137*795d594fSAndroid Build Coastguard Worker System.out.println("Obsolete class is: " + obsolete_class); 138*795d594fSAndroid Build Coastguard Worker System.out.println("Using unsafe to access obsolete values directly from memory."); 139*795d594fSAndroid Build Coastguard Worker System.out.println( 140*795d594fSAndroid Build Coastguard Worker "\tObsolete secret number is: " + u.getLong(obsolete_class, off_secret_number)); 141*795d594fSAndroid Build Coastguard Worker System.out.println( 142*795d594fSAndroid Build Coastguard Worker "\tObsolete secret array is: " 143*795d594fSAndroid Build Coastguard Worker + Arrays.toString((byte[]) u.getObject(obsolete_class, off_secret_array))); 144*795d594fSAndroid Build Coastguard Worker 145*795d594fSAndroid Build Coastguard Worker // Try calling all the public, non-static methods on the obsolete class. Make sure we cannot get 146*795d594fSAndroid Build Coastguard Worker // j.l.r.{Method,Field} objects or instances. 147*795d594fSAndroid Build Coastguard Worker TConsumer<Class> cc = 148*795d594fSAndroid Build Coastguard Worker (Class c) -> { 149*795d594fSAndroid Build Coastguard Worker for (Method m : Class.class.getDeclaredMethods()) { 150*795d594fSAndroid Build Coastguard Worker if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())) { 151*795d594fSAndroid Build Coastguard Worker Iterable<Object[]> iter = CollectParameterValues(m, obsolete_class); 152*795d594fSAndroid Build Coastguard Worker System.out.println("Calling " + m + " with params: " + iter); 153*795d594fSAndroid Build Coastguard Worker for (Object[] arr : iter) { 154*795d594fSAndroid Build Coastguard Worker try { 155*795d594fSAndroid Build Coastguard Worker System.out.println( 156*795d594fSAndroid Build Coastguard Worker m 157*795d594fSAndroid Build Coastguard Worker + " on " 158*795d594fSAndroid Build Coastguard Worker + safePrint(c) 159*795d594fSAndroid Build Coastguard Worker + " with " 160*795d594fSAndroid Build Coastguard Worker + deepPrint(arr) 161*795d594fSAndroid Build Coastguard Worker + " = " 162*795d594fSAndroid Build Coastguard Worker + safePrint(m.invoke(c, arr))); 163*795d594fSAndroid Build Coastguard Worker } catch (Throwable e) { 164*795d594fSAndroid Build Coastguard Worker System.out.println( 165*795d594fSAndroid Build Coastguard Worker m + " with " + deepPrint(arr) + " throws " + safePrint(e) + ": " + safePrint(e.getCause())); 166*795d594fSAndroid Build Coastguard Worker } 167*795d594fSAndroid Build Coastguard Worker } 168*795d594fSAndroid Build Coastguard Worker } 169*795d594fSAndroid Build Coastguard Worker } 170*795d594fSAndroid Build Coastguard Worker }; 171*795d594fSAndroid Build Coastguard Worker System.out.println("\n\nUsing obsolete class object!\n\n"); 172*795d594fSAndroid Build Coastguard Worker cc.accept(obsolete_class); 173*795d594fSAndroid Build Coastguard Worker System.out.println("\n\nUsing non-obsolete class object!\n\n"); 174*795d594fSAndroid Build Coastguard Worker cc.accept(Transform.class); 175*795d594fSAndroid Build Coastguard Worker } 176*795d594fSAndroid Build Coastguard Worker CollectParameterValues(Method m, Class<?> obsolete_class)177*795d594fSAndroid Build Coastguard Worker public static Iterable<Object[]> CollectParameterValues(Method m, Class<?> obsolete_class) throws Exception { 178*795d594fSAndroid Build Coastguard Worker Class<?>[] types = m.getParameterTypes(); 179*795d594fSAndroid Build Coastguard Worker final Object[][] params = new Object[types.length][]; 180*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < types.length; i++) { 181*795d594fSAndroid Build Coastguard Worker if (types[i].equals(Class.class)) { 182*795d594fSAndroid Build Coastguard Worker params[i] = 183*795d594fSAndroid Build Coastguard Worker new Object[] { 184*795d594fSAndroid Build Coastguard Worker null, Object.class, obsolete_class, Transform.class, Long.TYPE, Class.class 185*795d594fSAndroid Build Coastguard Worker }; 186*795d594fSAndroid Build Coastguard Worker } else if (types[i].equals(Boolean.TYPE)) { 187*795d594fSAndroid Build Coastguard Worker params[i] = new Object[] {Boolean.TRUE, Boolean.FALSE}; 188*795d594fSAndroid Build Coastguard Worker } else if (types[i].equals(String.class)) { 189*795d594fSAndroid Build Coastguard Worker params[i] = new Object[] {"NOT_USED_STRING", "foo", "SECRET_ARRAY"}; 190*795d594fSAndroid Build Coastguard Worker } else if (types[i].equals(Object.class)) { 191*795d594fSAndroid Build Coastguard Worker params[i] = new Object[] {null, "foo", "NOT_USED_STRING", Transform.class}; 192*795d594fSAndroid Build Coastguard Worker } else if (types[i].isArray()) { 193*795d594fSAndroid Build Coastguard Worker params[i] = new Object[] {new Object[0], new Class[0], null}; 194*795d594fSAndroid Build Coastguard Worker } else { 195*795d594fSAndroid Build Coastguard Worker throw new Exception("Unknown type " + types[i] + " at " + i + " in " + m); 196*795d594fSAndroid Build Coastguard Worker } 197*795d594fSAndroid Build Coastguard Worker } 198*795d594fSAndroid Build Coastguard Worker // Build the reset-iter. 199*795d594fSAndroid Build Coastguard Worker ResetIterator<Object[]> iter = new BaseResetIter(); 200*795d594fSAndroid Build Coastguard Worker for (int i = params.length - 1; i >= 0; i--) { 201*795d594fSAndroid Build Coastguard Worker iter = new ComboIter(Arrays.asList(params[i]), iter); 202*795d594fSAndroid Build Coastguard Worker } 203*795d594fSAndroid Build Coastguard Worker final Iterator<Object[]> fiter = iter; 204*795d594fSAndroid Build Coastguard Worker // Wrap in an iterator with a useful toString method. 205*795d594fSAndroid Build Coastguard Worker return new Iterable<Object[]>() { 206*795d594fSAndroid Build Coastguard Worker public Iterator<Object[]> iterator() { return fiter; } 207*795d594fSAndroid Build Coastguard Worker public String toString() { return deepPrint(params); } 208*795d594fSAndroid Build Coastguard Worker }; 209*795d594fSAndroid Build Coastguard Worker } 210*795d594fSAndroid Build Coastguard Worker 211*795d594fSAndroid Build Coastguard Worker public static String deepPrint(Object[] o) { 212*795d594fSAndroid Build Coastguard Worker return Arrays.toString( 213*795d594fSAndroid Build Coastguard Worker Arrays.stream(o) 214*795d594fSAndroid Build Coastguard Worker .map( 215*795d594fSAndroid Build Coastguard Worker (x) -> { 216*795d594fSAndroid Build Coastguard Worker if (x == null) { 217*795d594fSAndroid Build Coastguard Worker return "null"; 218*795d594fSAndroid Build Coastguard Worker } else if (x.getClass().isArray()) { 219*795d594fSAndroid Build Coastguard Worker if (((Object[]) x).length == 0) { 220*795d594fSAndroid Build Coastguard Worker return "new " + x.getClass().getComponentType().getName() + "[0]"; 221*795d594fSAndroid Build Coastguard Worker } else { 222*795d594fSAndroid Build Coastguard Worker return deepPrint((Object[]) x); 223*795d594fSAndroid Build Coastguard Worker } 224*795d594fSAndroid Build Coastguard Worker } else { 225*795d594fSAndroid Build Coastguard Worker return safePrint(x); 226*795d594fSAndroid Build Coastguard Worker } 227*795d594fSAndroid Build Coastguard Worker }) 228*795d594fSAndroid Build Coastguard Worker .toArray()); 229*795d594fSAndroid Build Coastguard Worker } 230*795d594fSAndroid Build Coastguard Worker 231*795d594fSAndroid Build Coastguard Worker public static String safePrint(Object o) { 232*795d594fSAndroid Build Coastguard Worker if (o instanceof ClassLoader) { 233*795d594fSAndroid Build Coastguard Worker return o.getClass().getName(); 234*795d594fSAndroid Build Coastguard Worker } else if (o == null) { 235*795d594fSAndroid Build Coastguard Worker return "null"; 236*795d594fSAndroid Build Coastguard Worker } else if (o instanceof Exception) { 237*795d594fSAndroid Build Coastguard Worker String res = o.toString(); 238*795d594fSAndroid Build Coastguard Worker if (res.endsWith("-transformed)")) { 239*795d594fSAndroid Build Coastguard Worker res = res.substring(0, res.lastIndexOf(" ")) + " <transformed-jar>)"; 240*795d594fSAndroid Build Coastguard Worker } else if (res.endsWith(".jar)")) { 241*795d594fSAndroid Build Coastguard Worker res = res.substring(0, res.lastIndexOf(" ")) + " <original-jar>)"; 242*795d594fSAndroid Build Coastguard Worker } 243*795d594fSAndroid Build Coastguard Worker return res; 244*795d594fSAndroid Build Coastguard Worker } else if (o instanceof Transform) { 245*795d594fSAndroid Build Coastguard Worker return "Transform Instance"; 246*795d594fSAndroid Build Coastguard Worker } else if (o instanceof Class && isObsoleteObject((Class) o)) { 247*795d594fSAndroid Build Coastguard Worker return "(obsolete)" + o.toString(); 248*795d594fSAndroid Build Coastguard Worker } else if (o.getClass().isArray()) { 249*795d594fSAndroid Build Coastguard Worker return Arrays.toString((Object[])o); 250*795d594fSAndroid Build Coastguard Worker } else { 251*795d594fSAndroid Build Coastguard Worker return o.toString(); 252*795d594fSAndroid Build Coastguard Worker } 253*795d594fSAndroid Build Coastguard Worker } 254*795d594fSAndroid Build Coastguard Worker 255*795d594fSAndroid Build Coastguard Worker private static class ComboIter implements ResetIterator<Object[]> { 256*795d594fSAndroid Build Coastguard Worker private ResetIterator<Object[]> next; 257*795d594fSAndroid Build Coastguard Worker private Object cur; 258*795d594fSAndroid Build Coastguard Worker private boolean first; 259*795d594fSAndroid Build Coastguard Worker private Iterator<Object> my_vals; 260*795d594fSAndroid Build Coastguard Worker private Iterable<Object> my_vals_reset; 261*795d594fSAndroid Build Coastguard Worker 262*795d594fSAndroid Build Coastguard Worker public Object[] next() { 263*795d594fSAndroid Build Coastguard Worker if (!next.hasNext()) { 264*795d594fSAndroid Build Coastguard Worker cur = my_vals.next(); 265*795d594fSAndroid Build Coastguard Worker first = false; 266*795d594fSAndroid Build Coastguard Worker if (next != null) { 267*795d594fSAndroid Build Coastguard Worker next.reset(); 268*795d594fSAndroid Build Coastguard Worker } 269*795d594fSAndroid Build Coastguard Worker } 270*795d594fSAndroid Build Coastguard Worker if (first) { 271*795d594fSAndroid Build Coastguard Worker first = false; 272*795d594fSAndroid Build Coastguard Worker cur = my_vals.next(); 273*795d594fSAndroid Build Coastguard Worker } 274*795d594fSAndroid Build Coastguard Worker Object[] nv = next.next(); 275*795d594fSAndroid Build Coastguard Worker Object[] res = new Object[nv.length + 1]; 276*795d594fSAndroid Build Coastguard Worker res[0] = cur; 277*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < nv.length; i++) { 278*795d594fSAndroid Build Coastguard Worker res[i + 1] = nv[i]; 279*795d594fSAndroid Build Coastguard Worker } 280*795d594fSAndroid Build Coastguard Worker return res; 281*795d594fSAndroid Build Coastguard Worker } 282*795d594fSAndroid Build Coastguard Worker 283*795d594fSAndroid Build Coastguard Worker public boolean hasNext() { 284*795d594fSAndroid Build Coastguard Worker return next.hasNext() || my_vals.hasNext(); 285*795d594fSAndroid Build Coastguard Worker } 286*795d594fSAndroid Build Coastguard Worker 287*795d594fSAndroid Build Coastguard Worker public void reset() { 288*795d594fSAndroid Build Coastguard Worker my_vals = my_vals_reset.iterator(); 289*795d594fSAndroid Build Coastguard Worker next.reset(); 290*795d594fSAndroid Build Coastguard Worker cur = null; 291*795d594fSAndroid Build Coastguard Worker first = true; 292*795d594fSAndroid Build Coastguard Worker } 293*795d594fSAndroid Build Coastguard Worker 294*795d594fSAndroid Build Coastguard Worker public ComboIter(Iterable<Object> this_reset, ResetIterator<Object[]> next_reset) { 295*795d594fSAndroid Build Coastguard Worker my_vals_reset = this_reset; 296*795d594fSAndroid Build Coastguard Worker next = next_reset; 297*795d594fSAndroid Build Coastguard Worker reset(); 298*795d594fSAndroid Build Coastguard Worker } 299*795d594fSAndroid Build Coastguard Worker } 300*795d594fSAndroid Build Coastguard Worker 301*795d594fSAndroid Build Coastguard Worker public static native long genericFieldOffset(Field f); 302*795d594fSAndroid Build Coastguard Worker 303*795d594fSAndroid Build Coastguard Worker public static native boolean isObsoleteObject(Class c); 304*795d594fSAndroid Build Coastguard Worker } 305