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