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