1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2010 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License.
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License.
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage com.android.layoutlib.bridge.impl;
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.util.Debug;
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.util.SparseWeakArray;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
22476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.Nullable;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.util.SparseArray;
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
25fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perezimport java.io.PrintStream;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.lang.ref.WeakReference;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.ArrayList;
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.List;
29fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perezimport java.util.concurrent.atomic.AtomicLong;
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Manages native delegates.
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This is used in conjunction with layoublib_create: certain Android java classes are mere
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * wrappers around a heavily native based implementation, and we need a way to run these classes
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * in our Eclipse rendering framework without bringing all the native code from the Android
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * platform.
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * native methods by "delegate calls".
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * For example, a native method android.graphics.Matrix.init(...) will actually become
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * a call to android.graphics.Matrix_Delegate.init(...).
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * The Android java classes that use native code uses an int (Java side) to reference native
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * objects. This int is generally directly the pointer to the C structure counterpart.
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Typically a creation method will return such an int, and then this int will be passed later
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * to a Java method to identify the C object to manipulate.
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * int -> Delegate class link.
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Native methods usually always have the int as parameters. The first thing the delegate method
54d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Gupta * will do is call {@link #getDelegate(long)} to get the Java object matching the int.
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Typical native init methods are returning a new int back to the Java class, so
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * {@link #addNewDelegate(Object)} does the same.
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * The JNI references are counted, so we do the same through a {@link WeakReference}. Because
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * the Java object needs to count as a reference (even though it only holds an int), we use the
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * following mechanism:
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
63d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Gupta * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *   the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *   the delegate.
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *   {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *   when nothing references it. This means that any class that holds a delegate (except for the
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *   Java main class) must not use the int but the Delegate class instead. The integers must
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *   only be used in the API between the main Java class and the Delegate.
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param <T> the delegate class to manage
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic final class DelegateManager<T> {
76d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Gupta    @SuppressWarnings("FieldCanBeLocal")
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final Class<T> mClass;
78fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>();
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** list used to store delegates when their main object holds a reference to them.
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @see #addNewDelegate(Object)
82d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Gupta     * @see #removeJavaReferenceFor(long)
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
84fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    private static final List<Object> sJavaReferences = new ArrayList<>();
85fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    private static final AtomicLong sDelegateCounter = new AtomicLong(1);
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public DelegateManager(Class<T> theClass) {
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mClass = theClass;
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns the delegate from the given native int.
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p>
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If the int is zero, then this will always return null.
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p>
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If the int is non zero and the delegate is not found, this will throw an assert.
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param native_object the native int.
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the delegate or null if not found.
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
101d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Gupta    @Nullable
102fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    public T getDelegate(long native_object) {
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (native_object > 0) {
104fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            Object delegate;
105fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            synchronized (DelegateManager.class) {
106fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez                delegate = sDelegates.get(native_object);
107fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            }
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (Debug.DEBUG) {
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (delegate == null) {
111511e48728a4b8f8736fcb2ea28dd1ab7cc699972Diego Perez                    System.err.println("Unknown " + mClass.getSimpleName() + " with int " +
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            native_object);
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            assert delegate != null;
117fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            //noinspection unchecked
118fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            return (T)delegate;
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Adds a delegate to the manager and returns the native int used to identify it.
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param newDelegate the delegate to add
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return a unique native int to identify the delegate
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
128fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    public long addNewDelegate(T newDelegate) {
129fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez        long native_object = sDelegateCounter.getAndIncrement();
130fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez        synchronized (DelegateManager.class) {
131fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            sDelegates.put(native_object, newDelegate);
132fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            assert !sJavaReferences.contains(newDelegate);
133fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            sJavaReferences.add(newDelegate);
134fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez        }
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (Debug.DEBUG) {
137511e48728a4b8f8736fcb2ea28dd1ab7cc699972Diego Perez            System.out.println(
138511e48728a4b8f8736fcb2ea28dd1ab7cc699972Diego Perez                    "New " + mClass.getSimpleName() + " " +
139511e48728a4b8f8736fcb2ea28dd1ab7cc699972Diego Perez                            "with int " +
140511e48728a4b8f8736fcb2ea28dd1ab7cc699972Diego Perez                            native_object);
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return native_object;
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Removes the main reference on the given delegate.
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param native_object the native integer representing the delegate.
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
150fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    public void removeJavaReferenceFor(long native_object) {
151fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez        synchronized (DelegateManager.class) {
152fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            T delegate = getDelegate(native_object);
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
154fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            if (Debug.DEBUG) {
155fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez                System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
156fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez                        " with int " + native_object);
157fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            }
158fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez
159fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            sJavaReferences.remove(delegate);
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
161fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    }
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
163fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez    public synchronized static void dump(PrintStream out) {
164fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez        for (Object reference : sJavaReferences) {
165fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            int idx = sDelegates.indexOfValue(reference);
166fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez            out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
167fc970ab65ad8c860dd0654d59b70bbe271f95784Diego Perez        }
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
170