14f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet/*
24f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Copyright (C) 2010 The Android Open Source Project
34f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
44f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License");
54f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * you may not use this file except in compliance with the License.
64f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * You may obtain a copy of the License at
74f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
84f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *      http://www.apache.org/licenses/LICENSE-2.0
94f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
104f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Unless required by applicable law or agreed to in writing, software
114f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS,
124f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * See the License for the specific language governing permissions and
144f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * limitations under the License.
154f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */
164f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet
17c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohetpackage com.android.layoutlib.bridge.impl;
184f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet
19f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohetimport com.android.layoutlib.bridge.util.Debug;
20cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport com.android.layoutlib.bridge.util.SparseWeakArray;
21cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet
224f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohetimport android.util.SparseArray;
234f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet
24cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport java.lang.ref.WeakReference;
25cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport java.util.ArrayList;
26cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohetimport java.util.List;
27cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet
284f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet/**
294f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Manages native delegates.
304f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
314f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * This is used in conjunction with layoublib_create: certain Android java classes are mere
324f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * wrappers around a heavily native based implementation, and we need a way to run these classes
334f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * in our Eclipse rendering framework without bringing all the native code from the Android
344f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * platform.
354f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
364f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
374f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * native methods by "delegate calls".
384f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
394f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * For example, a native method android.graphics.Matrix.init(...) will actually become
404f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * a call to android.graphics.Matrix_Delegate.init(...).
414f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
424f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * The Android java classes that use native code uses an int (Java side) to reference native
434f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * objects. This int is generally directly the pointer to the C structure counterpart.
444f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Typically a creation method will return such an int, and then this int will be passed later
454f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * to a Java method to identify the C object to manipulate.
464f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
474f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
484f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * int -> Delegate class link.
494f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
504f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Native methods usually always have the int as parameters. The first thing the delegate method
514f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * will do is call {@link #getDelegate(int)} to get the Java object matching the int.
524f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
534f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * Typical native init methods are returning a new int back to the Java class, so
54cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * {@link #addNewDelegate(Object)} does the same.
55cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *
56cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * The JNI references are counted, so we do the same through a {@link WeakReference}. Because
57cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * the Java object needs to count as a reference (even though it only holds an int), we use the
58cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * following mechanism:
59cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *
60cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(int)} adds and removes
61cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *   the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming
62cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *   the delegate.
63cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *
64cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a
65cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *   {@link WeakReference} to the delegate. This allows the delegate to be deleted automatically
66cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *   when nothing references it. This means that any class that holds a delegate (except for the
67cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *   Java main class) must not use the int but the Delegate class instead. The integers must
68cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet *   only be used in the API between the main Java class and the Delegate.
694f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet *
704f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet * @param <T> the delegate class to manage
714f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet */
724f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohetpublic final class DelegateManager<T> {
73f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet    private final Class<T> mClass;
74cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet    private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>();
75cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet    /** list used to store delegates when their main object holds a reference to them.
76cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet     * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
77cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet     * @see #addNewDelegate(Object)
78cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet     * @see #removeJavaReferenceFor(int)
79cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet     */
80cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet    private final List<T> mJavaReferences = new ArrayList<T>();
814f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    private int mDelegateCounter = 0;
824f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet
83f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet    public DelegateManager(Class<T> theClass) {
84f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet        mClass = theClass;
85f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet    }
86f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
874f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    /**
884f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     * Returns the delegate from the given native int.
89d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet     * <p>
90d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet     * If the int is zero, then this will always return null.
91d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet     * <p>
92d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet     * If the int is non zero and the delegate is not found, this will throw an assert.
93d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet     *
944f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     * @param native_object the native int.
954f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     * @return the delegate or null if not found.
964f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     */
974f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    public T getDelegate(int native_object) {
98d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet        if (native_object > 0) {
99d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            T delegate =  mDelegates.get(native_object);
100f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
101f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet            if (Debug.DEBUG) {
102f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet                if (delegate == null) {
103f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet                    System.out.println("Unknown " + mClass.getSimpleName() + " with int " +
104f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet                            native_object);
105f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet                }
106f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet            }
107f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
108d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            assert delegate != null;
109d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            return delegate;
110d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet        }
111d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet        return null;
1124f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    }
1134f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet
1144f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    /**
1154f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     * Adds a delegate to the manager and returns the native int used to identify it.
1164f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     * @param newDelegate the delegate to add
1174f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     * @return a unique native int to identify the delegate
1184f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     */
119cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet    public int addNewDelegate(T newDelegate) {
1205a09488a158b669577cd8eb557ce4feb62929e75Xavier Ducrohet        int native_object = ++mDelegateCounter;
1215a09488a158b669577cd8eb557ce4feb62929e75Xavier Ducrohet        mDelegates.put(native_object, newDelegate);
122cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet        assert !mJavaReferences.contains(newDelegate);
123cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet        mJavaReferences.add(newDelegate);
124f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
125f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet        if (Debug.DEBUG) {
126f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet            System.out.println("New " + mClass.getSimpleName() + " with int " + native_object);
127f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet        }
128f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
1295a09488a158b669577cd8eb557ce4feb62929e75Xavier Ducrohet        return native_object;
1304f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    }
1314f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet
1324f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    /**
133cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet     * Removes the main reference on the given delegate.
134cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet     * @param native_object the native integer representing the delegate.
1354f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet     */
136cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet    public void removeJavaReferenceFor(int native_object) {
137cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet        T delegate = getDelegate(native_object);
138f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
139f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet        if (Debug.DEBUG) {
140f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet            System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
141f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet                    " with int " + native_object);
142f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet        }
143f0a53435f14d23d9555fc46014352ee6a7baa647Xavier Ducrohet
144cc4977d0fdaf657907912fd6cc2f9426dc8d2e36Xavier Ducrohet        mJavaReferences.remove(delegate);
1454f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet    }
1464f291d33e14e62b3301acc056a82fe206c74835fXavier Ducrohet}
147