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.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 
     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
    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      */
    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      */
    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 
    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