1 /* 2 * Copyright (C) 2010 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 package com.android.layoutlib.bridge.impl; 18 19 import com.android.layoutlib.bridge.util.Debug; 20 import com.android.layoutlib.bridge.util.SparseWeakArray; 21 22 import android.annotation.Nullable; 23 import android.util.SparseArray; 24 25 import java.lang.ref.WeakReference; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 /** 30 * Manages native delegates. 31 * 32 * This is used in conjunction with layoublib_create: certain Android java classes are mere 33 * wrappers around a heavily native based implementation, and we need a way to run these classes 34 * in our Eclipse rendering framework without bringing all the native code from the Android 35 * platform. 36 * 37 * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their 38 * native methods by "delegate calls". 39 * 40 * For example, a native method android.graphics.Matrix.init(...) will actually become 41 * a call to android.graphics.Matrix_Delegate.init(...). 42 * 43 * The Android java classes that use native code uses an int (Java side) to reference native 44 * objects. This int is generally directly the pointer to the C structure counterpart. 45 * Typically a creation method will return such an int, and then this int will be passed later 46 * to a Java method to identify the C object to manipulate. 47 * 48 * Since we cannot use the Java object reference as the int directly, DelegateManager manages the 49 * int -> Delegate class link. 50 * 51 * Native methods usually always have the int as parameters. The first thing the delegate method 52 * will do is call {@link #getDelegate(long)} to get the Java object matching the int. 53 * 54 * Typical native init methods are returning a new int back to the Java class, so 55 * {@link #addNewDelegate(Object)} does the same. 56 * 57 * The JNI references are counted, so we do the same through a {@link WeakReference}. Because 58 * the Java object needs to count as a reference (even though it only holds an int), we use the 59 * following mechanism: 60 * 61 * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes 62 * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming 63 * the delegate. 64 * 65 * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a 66 * {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically 67 * when nothing references it. This means that any class that holds a delegate (except for the 68 * Java main class) must not use the int but the Delegate class instead. The integers must 69 * only be used in the API between the main Java class and the Delegate. 70 * 71 * @param <T> the delegate class to manage 72 */ 73 public final class DelegateManager<T> { 74 @SuppressWarnings("FieldCanBeLocal") 75 private final Class<T> mClass; 76 private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>(); 77 /** list used to store delegates when their main object holds a reference to them. 78 * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed 79 * @see #addNewDelegate(Object) 80 * @see #removeJavaReferenceFor(long) 81 */ 82 private final List<T> mJavaReferences = new ArrayList<T>(); 83 private int mDelegateCounter = 0; 84 85 public DelegateManager(Class<T> theClass) { 86 mClass = theClass; 87 } 88 89 /** 90 * Returns the delegate from the given native int. 91 * <p> 92 * If the int is zero, then this will always return null. 93 * <p> 94 * If the int is non zero and the delegate is not found, this will throw an assert. 95 * 96 * @param native_object the native int. 97 * @return the delegate or null if not found. 98 */ 99 @Nullable 100 public synchronized T getDelegate(long native_object) { 101 if (native_object > 0) { 102 T delegate = mDelegates.get(native_object); 103 104 if (Debug.DEBUG) { 105 if (delegate == null) { 106 System.err.println("Unknown " + mClass.getSimpleName() + " with int " + 107 native_object); 108 } 109 } 110 111 assert delegate != null; 112 return delegate; 113 } 114 return null; 115 } 116 117 /** 118 * Adds a delegate to the manager and returns the native int used to identify it. 119 * @param newDelegate the delegate to add 120 * @return a unique native int to identify the delegate 121 */ 122 public synchronized long addNewDelegate(T newDelegate) { 123 long native_object = ++mDelegateCounter; 124 125 mDelegates.put(native_object, newDelegate); 126 assert !mJavaReferences.contains(newDelegate); 127 mJavaReferences.add(newDelegate); 128 129 if (Debug.DEBUG) { 130 System.out.println( 131 "New " + mClass.getSimpleName() + " " + 132 "with int " + 133 native_object); 134 } 135 136 return native_object; 137 } 138 139 /** 140 * Removes the main reference on the given delegate. 141 * @param native_object the native integer representing the delegate. 142 */ 143 public synchronized void removeJavaReferenceFor(long native_object) { 144 T delegate = getDelegate(native_object); 145 146 if (Debug.DEBUG) { 147 System.out.println("Removing main Java ref on " + mClass.getSimpleName() + 148 " with int " + native_object); 149 } 150 151 mJavaReferences.remove(delegate); 152 } 153 } 154