1ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/*
2ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Copyright (C) 2010 The Android Open Source Project
3ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
4ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * you may not use this file except in compliance with the License.
6ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * You may obtain a copy of the License at
7ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
8ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
10ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Unless required by applicable law or agreed to in writing, software
11ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * See the License for the specific language governing permissions and
14ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * limitations under the License.
15ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */
16ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
17ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipackage com.android.layoutlib.bridge.impl;
18ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
19ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport com.android.layoutlib.bridge.util.Debug;
20ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport com.android.layoutlib.bridge.util.SparseWeakArray;
21ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
22442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.Nullable;
23ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport android.util.SparseArray;
24ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
25852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perezimport java.io.PrintStream;
26ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.lang.ref.WeakReference;
2782c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillardimport java.util.HashSet;
2882c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillardimport java.util.Set;
29852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perezimport java.util.concurrent.atomic.AtomicLong;
30ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
31ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/**
32ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Manages native delegates.
33ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
34ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * This is used in conjunction with layoublib_create: certain Android java classes are mere
35ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * wrappers around a heavily native based implementation, and we need a way to run these classes
3682c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard * in our Android Studio rendering framework without bringing all the native code from the Android
37ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * platform.
38ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
39ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
40ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * native methods by "delegate calls".
41ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
42ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * For example, a native method android.graphics.Matrix.init(...) will actually become
43ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * a call to android.graphics.Matrix_Delegate.init(...).
44ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
45ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * The Android java classes that use native code uses an int (Java side) to reference native
46ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * objects. This int is generally directly the pointer to the C structure counterpart.
47ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Typically a creation method will return such an int, and then this int will be passed later
48ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * to a Java method to identify the C object to manipulate.
49ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
50ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
51ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * int -> Delegate class link.
52ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
53ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Native methods usually always have the int as parameters. The first thing the delegate method
549fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta * will do is call {@link #getDelegate(long)} to get the Java object matching the int.
55ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
56ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Typical native init methods are returning a new int back to the Java class, so
57ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * {@link #addNewDelegate(Object)} does the same.
58ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
59ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * The JNI references are counted, so we do the same through a {@link WeakReference}. Because
60ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * the Java object needs to count as a reference (even though it only holds an int), we use the
61ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * following mechanism:
62ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
639fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes
6482c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard *   the delegate to/from a set. This set holds the reference and prevents the GC from reclaiming
65ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *   the delegate.
66ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
67ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a
68ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *   {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically
69ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *   when nothing references it. This means that any class that holds a delegate (except for the
70ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *   Java main class) must not use the int but the Delegate class instead. The integers must
71ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *   only be used in the API between the main Java class and the Delegate.
72ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
73ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * @param <T> the delegate class to manage
74ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */
75ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipublic final class DelegateManager<T> {
769fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta    @SuppressWarnings("FieldCanBeLocal")
77ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    private final Class<T> mClass;
78852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>();
7982c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard    /** Set used to store delegates when their main object holds a reference to them.
80ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
81ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * @see #addNewDelegate(Object)
829fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta     * @see #removeJavaReferenceFor(long)
83ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     */
8482c43ed36422ff673aff03983fdc97bc3adb45f6Jerome Gaillard    private static final Set<Object> sJavaReferences = new HashSet<>();
85852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    private static final AtomicLong sDelegateCounter = new AtomicLong(1);
86ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
87ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    public DelegateManager(Class<T> theClass) {
88ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        mClass = theClass;
89ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
90ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
91ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    /**
92ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * Returns the delegate from the given native int.
93ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * <p>
94ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * If the int is zero, then this will always return null.
95ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * <p>
96ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * If the int is non zero and the delegate is not found, this will throw an assert.
97ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     *
98ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * @param native_object the native int.
99ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * @return the delegate or null if not found.
100ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     */
1019fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta    @Nullable
102852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    public T getDelegate(long native_object) {
103ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        if (native_object > 0) {
104852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            Object delegate;
105852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            synchronized (DelegateManager.class) {
106852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez                delegate = sDelegates.get(native_object);
107852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            }
108ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
109ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (Debug.DEBUG) {
110ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                if (delegate == null) {
111c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez                    System.err.println("Unknown " + mClass.getSimpleName() + " with int " +
112ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                            native_object);
113ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                }
114ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
115ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
116ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            assert delegate != null;
117852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            //noinspection unchecked
118852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            return (T)delegate;
119ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
120ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        return null;
121ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
122ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
123ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    /**
124ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * Adds a delegate to the manager and returns the native int used to identify it.
125ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * @param newDelegate the delegate to add
126ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * @return a unique native int to identify the delegate
127ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     */
128852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    public long addNewDelegate(T newDelegate) {
129852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez        long native_object = sDelegateCounter.getAndIncrement();
130852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez        synchronized (DelegateManager.class) {
131852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            sDelegates.put(native_object, newDelegate);
1328b5212450d394c8fb40e80b322369d1d7db3e5dbDiego Perez            // Only for development: assert !sJavaReferences.contains(newDelegate);
133852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            sJavaReferences.add(newDelegate);
134852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez        }
135ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
136ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        if (Debug.DEBUG) {
137c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez            System.out.println(
138c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez                    "New " + mClass.getSimpleName() + " " +
139c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez                            "with int " +
140c445f85ce5fd6933ab4bbac7226942a925e2db2eDiego Perez                            native_object);
141ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
142ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
143ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        return native_object;
144ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
145ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
146ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    /**
147ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * Removes the main reference on the given delegate.
148ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     * @param native_object the native integer representing the delegate.
149ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski     */
150852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    public void removeJavaReferenceFor(long native_object) {
151852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez        synchronized (DelegateManager.class) {
152852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            T delegate = getDelegate(native_object);
153ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
154852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            if (Debug.DEBUG) {
155852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez                System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
156852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez                        " with int " + native_object);
157852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            }
158852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez
159852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            sJavaReferences.remove(delegate);
160ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
161852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    }
162ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
163852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez    public synchronized static void dump(PrintStream out) {
164852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez        for (Object reference : sJavaReferences) {
165852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            int idx = sDelegates.indexOfValue(reference);
166852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez            out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
167852bc917136aef751cfb82bef74a9e66bafdd3c8Diego Perez        }
168ca7b05455262ebd3dd5e8b1969b1b62a93233f09Diego Perez        out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
169ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
170ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski}
171