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