1ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/* 2ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Copyright (C) 2010 The Android Open Source Project 3ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 4ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); 5ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * you may not use this file except in compliance with the License. 6ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * You may obtain a copy of the License at 7ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 8ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * http://www.apache.org/licenses/LICENSE-2.0 9ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 10ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Unless required by applicable law or agreed to in writing, software 11ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, 12ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * See the License for the specific language governing permissions and 14ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * limitations under the License. 15ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */ 16ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 17ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipackage com.android.layoutlib.bridge.impl; 18ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 19ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport com.android.layoutlib.bridge.util.Debug; 20ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport com.android.layoutlib.bridge.util.SparseWeakArray; 21ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 22442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.Nullable; 23ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport android.util.SparseArray; 24ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 25852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perezimport java.io.PrintStream; 26ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.lang.ref.WeakReference; 2782c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillardimport java.util.HashSet; 2882c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillardimport java.util.Set; 29852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perezimport java.util.concurrent.atomic.AtomicLong; 30ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 31ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/** 32ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Manages native delegates. 33ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 34ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * This is used in conjunction with layoublib_create: certain Android java classes are mere 35ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * wrappers around a heavily native based implementation, and we need a way to run these classes 3682c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard * in our Android Studio rendering framework without bringing all the native code from the Android 37ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * platform. 38ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 39ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their 40ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * native methods by "delegate calls". 41ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 42ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * For example, a native method android.graphics.Matrix.init(...) will actually become 43ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * a call to android.graphics.Matrix_Delegate.init(...). 44ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 45ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * The Android java classes that use native code uses an int (Java side) to reference native 46ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * objects. This int is generally directly the pointer to the C structure counterpart. 47ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Typically a creation method will return such an int, and then this int will be passed later 48ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * to a Java method to identify the C object to manipulate. 49ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 50ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Since we cannot use the Java object reference as the int directly, DelegateManager manages the 51ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * int -> Delegate class link. 52ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 53ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Native methods usually always have the int as parameters. The first thing the delegate method 549fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta * will do is call {@link #getDelegate(long)} to get the Java object matching the int. 55ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 56ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Typical native init methods are returning a new int back to the Java class, so 57ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * {@link #addNewDelegate(Object)} does the same. 58ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 59ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * The JNI references are counted, so we do the same through a {@link WeakReference}. Because 60ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * the Java object needs to count as a reference (even though it only holds an int), we use the 61ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * following mechanism: 62ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 639fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes 6482c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard * the delegate to/from a set. This set holds the reference and prevents the GC from reclaiming 65ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * the delegate. 66ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 67ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a 68ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically 69ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * when nothing references it. This means that any class that holds a delegate (except for the 70ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Java main class) must not use the int but the Delegate class instead. The integers must 71ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * only be used in the API between the main Java class and the Delegate. 72ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 73ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @param <T> the delegate class to manage 74ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */ 75ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipublic final class DelegateManager<T> { 769fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta @SuppressWarnings("FieldCanBeLocal") 77ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski private final Class<T> mClass; 78852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>(); 7982c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard /** Set used to store delegates when their main object holds a reference to them. 80ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed 81ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @see #addNewDelegate(Object) 829fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta * @see #removeJavaReferenceFor(long) 83ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */ 8482c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard private static final Set<Object> sJavaReferences = new HashSet<>(); 85852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez private static final AtomicLong sDelegateCounter = new AtomicLong(1); 86ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 87ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski public DelegateManager(Class<T> theClass) { 88ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski mClass = theClass; 89ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 90ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 91ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski /** 92ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Returns the delegate from the given native int. 93ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * <p> 94ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * If the int is zero, then this will always return null. 95ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * <p> 96ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * If the int is non zero and the delegate is not found, this will throw an assert. 97ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * 98ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @param native_object the native int. 99ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @return the delegate or null if not found. 100ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */ 1019fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta @Nullable 102852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez public T getDelegate(long native_object) { 103ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski if (native_object > 0) { 104852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez Object delegate; 105852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez synchronized (DelegateManager.class) { 106852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez delegate = sDelegates.get(native_object); 107852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez } 108ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 109ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski if (Debug.DEBUG) { 110ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski if (delegate == null) { 111c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez System.err.println("Unknown " + mClass.getSimpleName() + " with int " + 112ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski native_object); 113ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 114ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 115ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 116ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski assert delegate != null; 117852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez //noinspection unchecked 118852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez return (T)delegate; 119ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 120ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski return null; 121ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 122ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 123ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski /** 124ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Adds a delegate to the manager and returns the native int used to identify it. 125ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @param newDelegate the delegate to add 126ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @return a unique native int to identify the delegate 127ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */ 128852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez public long addNewDelegate(T newDelegate) { 129852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez long native_object = sDelegateCounter.getAndIncrement(); 130852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez synchronized (DelegateManager.class) { 131852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez sDelegates.put(native_object, newDelegate); 1328b5212450d394c8fb40e80b322369d1d7db3e5dbDiego Perez // Only for development: assert !sJavaReferences.contains(newDelegate); 133852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez sJavaReferences.add(newDelegate); 134852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez } 135ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 136ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski if (Debug.DEBUG) { 137c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez System.out.println( 138c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez "New " + mClass.getSimpleName() + " " + 139c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez "with int " + 140c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez native_object); 141ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 142ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 143ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski return native_object; 144ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 145ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 146ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski /** 147ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Removes the main reference on the given delegate. 148ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @param native_object the native integer representing the delegate. 149ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */ 150852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez public void removeJavaReferenceFor(long native_object) { 151852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez synchronized (DelegateManager.class) { 152852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez T delegate = getDelegate(native_object); 153ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 154852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez if (Debug.DEBUG) { 155852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez System.out.println("Removing main Java ref on " + mClass.getSimpleName() + 156852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez " with int " + native_object); 157852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez } 158852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez 159852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez sJavaReferences.remove(delegate); 160ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 161852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez } 162ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski 163852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez public synchronized static void dump(PrintStream out) { 164852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez for (Object reference : sJavaReferences) { 165852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez int idx = sDelegates.indexOfValue(reference); 166852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName()); 167852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez } 168ca7b05455262ebd3dd5e8b1969b1b62a93233f09Diego Perez out.printf("\nTotal number of objects: %d\n", sJavaReferences.size()); 169ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski } 170ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski} 171