• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * 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 Android Studio 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 set. This set holds 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     /** Set 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 Set<Object> sJavaReferences = new HashSet<>();
85     private static final AtomicLong sDelegateCounter = new AtomicLong(1);
86 
DelegateManager(Class<T> theClass)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
getDelegate(long native_object)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      */
addNewDelegate(T newDelegate)128     public long addNewDelegate(T newDelegate) {
129         long native_object = sDelegateCounter.getAndIncrement();
130         synchronized (DelegateManager.class) {
131             sDelegates.put(native_object, newDelegate);
132             // Only for development: 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      */
removeJavaReferenceFor(long native_object)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 
dump(PrintStream out)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         out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
169     }
170 }
171