PageRenderTime 35ms CodeModel.GetById 9ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/talkback_preics/src/com/google/android/marvin/talkback/ClassLoadingManager.java

http://eyes-free.googlecode.com/
Java | 212 lines | 111 code | 21 blank | 80 comment | 13 complexity | ab883adfaccf03cf87957702dab642c3 MD5 | raw file
  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
 17package com.google.android.marvin.talkback;
 18
 19import com.google.android.marvin.talkback.TalkBackService.InfrastructureStateListener;
 20
 21import android.content.Context;
 22import android.content.pm.PackageInfo;
 23import android.content.pm.PackageManager.NameNotFoundException;
 24import android.util.Log;
 25
 26import java.util.HashMap;
 27import java.util.HashSet;
 28import java.util.List;
 29
 30/**
 31 * This class manages efficient loading of classes.
 32 *
 33 * @author svetoslavganov@google.com (Svetoslav R. Ganov)
 34 */
 35public class ClassLoadingManager implements InfrastructureStateListener {
 36
 37    /**
 38     * Tag used for logging.
 39     */
 40    private static final String LOG_TAG = "ClassLoadingManager";
 41
 42    /**
 43     * The singleton instance of this class.
 44     */
 45    private static ClassLoadingManager sInstance;
 46
 47    /**
 48     * Mapping from class names to classes form outside packages.
 49     */
 50    private final HashMap<String, Class<?>> mClassNameToOutsidePackageClassMap = new HashMap<String, Class<?>>();
 51
 52    /**
 53     * A set of classes not found to be loaded. Used to avoid multiple attempts
 54     * that will fail.
 55     */
 56    private final HashSet<String> mNotFoundClassesSet = new HashSet<String>();
 57
 58    /**
 59     * Cache of installed packages to avoid class loading attempts
 60     */
 61    private final HashSet<String> mInstalledPackagesSet = new HashSet<String>();
 62
 63    /**
 64     * Lock for mutex access to the cache.
 65     */
 66    private final Object mLock = new Object();
 67
 68    /**
 69     * {@link Context} for accessing
 70     */
 71    private final Context mContext;
 72
 73    /**
 74     * Monitor to keep track of the installed packages;
 75     */
 76    private final BasePackageMonitor mPackageMonitor;
 77
 78    /**
 79     * The singleton instance of this class.
 80     * 
 81     * @return The singleton instance of this class.
 82     */
 83    public static ClassLoadingManager getInstance() {
 84        if (sInstance == null) {
 85            sInstance = new ClassLoadingManager();
 86        }
 87        return sInstance;
 88    }
 89
 90    /**
 91     * Creates a new instance.
 92     */
 93    private ClassLoadingManager() {
 94        mContext = TalkBackService.asContext();
 95        mPackageMonitor = new BasePackageMonitor() {
 96            @Override
 97            protected void onPackageAdded(String packageName) {
 98                synchronized (mLock) {
 99                    mInstalledPackagesSet.add(packageName);
100                    // TODO (svetoslavganov): we can be more efficient
101                    mNotFoundClassesSet.clear();
102                }
103            }
104
105            @Override
106            protected void onPackageRemoved(String packageName) {
107                synchronized (mLock) {
108                    mInstalledPackagesSet.remove(packageName);
109                }
110            }
111        };
112    }
113
114    @Override
115    public void onInfrastructureStateChange(boolean isInitialized) {
116        if (isInitialized) {
117            buildInstalledPackagesCache();
118            mPackageMonitor.register(mContext);
119        } else {
120            clearInstalledPackagesCache();
121            mPackageMonitor.unregister();
122        }
123    }
124
125    /**
126     * Builds a cache of installed packages.
127     */
128    private void buildInstalledPackagesCache() {
129        synchronized (mLock) {
130            List<PackageInfo> installedPackages = mContext.getPackageManager()
131                    .getInstalledPackages(0);
132            for (PackageInfo installedPackage : installedPackages) {
133                mInstalledPackagesSet.add(installedPackage.packageName);
134            }
135        }
136    }
137
138    /**
139     * Clears the installed package cache.
140     */
141    private void clearInstalledPackagesCache() {
142        synchronized (mLock) {
143            mInstalledPackagesSet.clear();
144        }
145    }
146
147    /**
148     * Returns a class by given <code>className</code>. The loading proceeds as
149     * follows: </br> 1. Try to load with the current context class loader (it
150     * caches loaded classes). </br> 2. If (1) fails try if we have loaded the
151     * class before and return it if that is the cases. </br> 3. If (2) failed,
152     * try to create a package context and load the class. </p> Note: If the
153     * package name is null and an attempt for loading of a package context is
154     * required the it is extracted from the class name.
155     * 
156     * @param context The context from which to first try loading the class.
157     * @param className The name of the class to load.
158     * @param packageName The name of the package to which the class belongs.
159     * @return The class if loaded successfully, null otherwise.
160     */
161    public Class<?> loadOrGetCachedClass(Context context, String className, String packageName) {
162        // if we failed once loading this class - no bother trying again
163        if (mNotFoundClassesSet.contains(className)) {
164            return null;
165        }
166        try {
167            // try the current ClassLoader first
168            return context.getClassLoader().loadClass(className);
169        } catch (ClassNotFoundException cnfe) {
170            // do we have a cached class
171            Class<?> clazz = mClassNameToOutsidePackageClassMap.get(className);
172            if (clazz != null) {
173                return clazz;
174            }
175            // no package - get it from the class name
176            if (packageName == null) {
177                int lastDotIndex = className.lastIndexOf(".");
178                if (lastDotIndex > -1) {
179                    packageName = className.substring(0, lastDotIndex);
180                } else {
181                    return null;
182                }
183            }
184            // no cached class - check if the package is installed first
185            if (!mInstalledPackagesSet.contains(packageName)) {
186                return null;
187            }
188            // all failed - try via creating a package context
189            try {
190                int flags = (Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
191                try {
192                    Context packageContext = context.getApplicationContext().createPackageContext(
193                            packageName, flags);
194                    clazz = packageContext.getClassLoader().loadClass(className);
195                    mClassNameToOutsidePackageClassMap.put(className, clazz);
196                } catch (NullPointerException npe) {
197                    String s = "";
198                }
199                return clazz;
200            } catch (NameNotFoundException nnfe) {
201                Log.e(LOG_TAG, "Error during loading an event source class: " + className + " "
202                        + nnfe);
203            } catch (ClassNotFoundException cnfe2) {
204                Log.e(LOG_TAG, "Error during loading an event source class: " + className + " "
205                        + cnfe);
206                mNotFoundClassesSet.add(className);
207            }
208
209            return null;
210        }
211    }
212}