1 /* 2 * Copyright (C) 2011 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 18 package androidx.media.filterfw; 19 20 import android.util.Log; 21 22 import dalvik.system.PathClassLoader; 23 24 import java.lang.reflect.Constructor; 25 import java.util.HashSet; 26 27 public class FilterFactory { 28 29 private static FilterFactory mSharedFactory; 30 private HashSet<String> mPackages = new HashSet<String>(); 31 32 private static ClassLoader mCurrentClassLoader; 33 private static HashSet<String> mLibraries; 34 private static Object mClassLoaderGuard; 35 36 static { 37 mCurrentClassLoader = Thread.currentThread().getContextClassLoader(); 38 mLibraries = new HashSet<String>(); 39 mClassLoaderGuard = new Object(); 40 } 41 42 private static final String TAG = "FilterFactory"; 43 private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE); 44 sharedFactory()45 public static FilterFactory sharedFactory() { 46 if (mSharedFactory == null) { 47 mSharedFactory = new FilterFactory(); 48 } 49 return mSharedFactory; 50 } 51 52 /** 53 * Adds a new Java library to the list to be scanned for filters. 54 * libraryPath must be an absolute path of the jar file. This needs to be 55 * static because only one classloader per process can open a shared native 56 * library, which a filter may well have. 57 */ addFilterLibrary(String libraryPath)58 public static void addFilterLibrary(String libraryPath) { 59 if (mLogVerbose) Log.v(TAG, "Adding filter library " + libraryPath); 60 synchronized(mClassLoaderGuard) { 61 if (mLibraries.contains(libraryPath)) { 62 if (mLogVerbose) Log.v(TAG, "Library already added"); 63 return; 64 } 65 mLibraries.add(libraryPath); 66 // Chain another path loader to the current chain 67 mCurrentClassLoader = new PathClassLoader(libraryPath, mCurrentClassLoader); 68 } 69 } 70 addPackage(String packageName)71 public void addPackage(String packageName) { 72 if (mLogVerbose) Log.v(TAG, "Adding package " + packageName); 73 /* TODO: This should use a getPackage call in the caller's context, but no such method 74 exists. 75 Package pkg = Package.getPackage(packageName); 76 if (pkg == null) { 77 throw new IllegalArgumentException("Unknown filter package '" + packageName + "'!"); 78 } 79 */ 80 mPackages.add(packageName); 81 } 82 isFilterAvailable(String className)83 public boolean isFilterAvailable(String className) { 84 return getFilterClass(className) != null; 85 } 86 createFilterByClassName(String className, String filterName, MffContext context)87 public Filter createFilterByClassName(String className, String filterName, MffContext context) { 88 if (mLogVerbose) Log.v(TAG, "Looking up class " + className); 89 Class<? extends Filter> filterClass = getFilterClass(className); 90 if (filterClass == null) { 91 throw new IllegalArgumentException("Unknown filter class '" + className + "'!"); 92 } 93 return createFilterByClass(filterClass, filterName, context); 94 } 95 createFilterByClass(Class<? extends Filter> filterClass, String filterName, MffContext context)96 public Filter createFilterByClass(Class<? extends Filter> filterClass, 97 String filterName, MffContext context) { 98 // Look for the correct constructor 99 Constructor<? extends Filter> filterConstructor = null; 100 try { 101 filterConstructor = filterClass.getConstructor(MffContext.class, String.class); 102 } catch (NoSuchMethodException e) { 103 throw new IllegalArgumentException("The filter class '" + filterClass 104 + "' does not have a constructor of the form <init>(MffContext, String)!"); 105 } 106 107 // Construct the filter 108 Filter filter = null; 109 try { 110 filter = filterConstructor.newInstance(context, filterName); 111 } catch (Throwable t) { 112 throw new RuntimeException("Error creating filter " + filterName + "!", t); 113 } 114 115 if (filter == null) { 116 throw new IllegalArgumentException("Could not construct the filter '" 117 + filterName + "'!"); 118 } 119 return filter; 120 } 121 getFilterClass(String name)122 private Class<? extends Filter> getFilterClass(String name) { 123 Class<?> filterClass = null; 124 125 // Look for the class in the imported packages 126 for (String packageName : mPackages) { 127 try { 128 if (mLogVerbose) Log.v(TAG, "Trying "+ packageName + "." + name); 129 synchronized(mClassLoaderGuard) { 130 filterClass = mCurrentClassLoader.loadClass(packageName + "." + name); 131 } 132 } catch (ClassNotFoundException e) { 133 continue; 134 } 135 // Exit loop if class was found. 136 if (filterClass != null) { 137 break; 138 } 139 } 140 Class<? extends Filter> result = null; 141 try { 142 if (filterClass != null) { 143 result = filterClass.asSubclass(Filter.class); 144 } 145 } catch (ClassCastException e) { 146 // Leave result == null 147 } 148 return result; 149 } 150 } 151