14f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet/* 24f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Copyright (C) 2010 The Android Open Source Project 34f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 44f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License"); 54f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * you may not use this file except in compliance with the License. 64f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * You may obtain a copy of the License at 74f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 84f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * http://www.apache.org/licenses/LICENSE-2.0 94f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 104f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Unless required by applicable law or agreed to in writing, software 114f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS, 124f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * See the License for the specific language governing permissions and 144f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * limitations under the License. 154f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */ 164f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet 17c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohetpackage com.android.layoutlib.bridge.impl; 184f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet 19f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohetimport com.android.layoutlib.bridge.util.Debug; 20cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport com.android.layoutlib.bridge.util.SparseWeakArray; 21cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet 224f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohetimport android.util.SparseArray; 234f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet 24cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport java.lang.ref.WeakReference; 25cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport java.util.ArrayList; 26cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport java.util.List; 27cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet 284f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet/** 294f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Manages native delegates. 304f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 314f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * This is used in conjunction with layoublib_create: certain Android java classes are mere 324f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * wrappers around a heavily native based implementation, and we need a way to run these classes 334f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * in our Eclipse rendering framework without bringing all the native code from the Android 344f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * platform. 354f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 364f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their 374f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * native methods by "delegate calls". 384f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 394f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * For example, a native method android.graphics.Matrix.init(...) will actually become 404f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * a call to android.graphics.Matrix_Delegate.init(...). 414f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 424f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * The Android java classes that use native code uses an int (Java side) to reference native 434f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * objects. This int is generally directly the pointer to the C structure counterpart. 444f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Typically a creation method will return such an int, and then this int will be passed later 454f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * to a Java method to identify the C object to manipulate. 464f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 474f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Since we cannot use the Java object reference as the int directly, DelegateManager manages the 484f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * int -> Delegate class link. 494f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 504f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Native methods usually always have the int as parameters. The first thing the delegate method 514f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * will do is call {@link #getDelegate(int)} to get the Java object matching the int. 524f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 534f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Typical native init methods are returning a new int back to the Java class, so 54cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * {@link #addNewDelegate(Object)} does the same. 55cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * 56cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * The JNI references are counted, so we do the same through a {@link WeakReference}. Because 57cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * the Java object needs to count as a reference (even though it only holds an int), we use the 58cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * following mechanism: 59cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * 60cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes 61cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming 62cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * the delegate. 63cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * 64cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a 65cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically 66cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * when nothing references it. This means that any class that holds a delegate (except for the 67cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * Java main class) must not use the int but the Delegate class instead. The integers must 68cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * only be used in the API between the main Java class and the Delegate. 694f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * 704f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * @param <T> the delegate class to manage 714f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */ 724f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohetpublic final class DelegateManager<T> { 73f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet private final Class<T> mClass; 74cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>(); 75cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet /** list used to store delegates when their main object holds a reference to them. 76cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed 77cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * @see #addNewDelegate(Object) 78cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * @see #removeJavaReferenceFor(int) 79cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet */ 80cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet private final List<T> mJavaReferences = new ArrayList<T>(); 814f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet private int mDelegateCounter = 0; 824f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet 83f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet public DelegateManager(Class<T> theClass) { 84f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet mClass = theClass; 85f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet } 86f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 874f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet /** 884f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Returns the delegate from the given native int. 89d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet * <p> 90d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet * If the int is zero, then this will always return null. 91d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet * <p> 92d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet * If the int is non zero and the delegate is not found, this will throw an assert. 93d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet * 944f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * @param native_object the native int. 954f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * @return the delegate or null if not found. 964f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */ 974f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet public T getDelegate(int native_object) { 98d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet if (native_object > 0) { 99d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet T delegate = mDelegates.get(native_object); 100f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 101f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet if (Debug.DEBUG) { 102f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet if (delegate == null) { 103f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet System.out.println("Unknown " + mClass.getSimpleName() + " with int " + 104f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet native_object); 105f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet } 106f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet } 107f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 108d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet assert delegate != null; 109d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet return delegate; 110d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet } 111d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet return null; 1124f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet } 1134f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet 1144f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet /** 1154f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Adds a delegate to the manager and returns the native int used to identify it. 1164f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * @param newDelegate the delegate to add 1174f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * @return a unique native int to identify the delegate 1184f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */ 119cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet public int addNewDelegate(T newDelegate) { 1205a09488a158b669577cd8eb557ce4feb62929e75Xavier Ducrohet int native_object = ++mDelegateCounter; 1215a09488a158b669577cd8eb557ce4feb62929e75Xavier Ducrohet mDelegates.put(native_object, newDelegate); 122cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet assert !mJavaReferences.contains(newDelegate); 123cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet mJavaReferences.add(newDelegate); 124f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 125f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet if (Debug.DEBUG) { 126f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet System.out.println("New " + mClass.getSimpleName() + " with int " + native_object); 127f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet } 128f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 1295a09488a158b669577cd8eb557ce4feb62929e75Xavier Ducrohet return native_object; 1304f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet } 1314f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet 1324f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet /** 133cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * Removes the main reference on the given delegate. 134cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * @param native_object the native integer representing the delegate. 1354f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */ 136cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet public void removeJavaReferenceFor(int native_object) { 137cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet T delegate = getDelegate(native_object); 138f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 139f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet if (Debug.DEBUG) { 140f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet System.out.println("Removing main Java ref on " + mClass.getSimpleName() + 141f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet " with int " + native_object); 142f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet } 143f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet 144cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet mJavaReferences.remove(delegate); 1454f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet } 1464f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet} 147