/*
* Copyright 2011 yingxinwu.g@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xink.vpn.wrapper;
import java.lang.reflect.Method;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import dalvik.system.PathClassLoader;
public abstract class AbstractWrapper implements Cloneable {
/**
* subclass to customize stub object creation
*/
protected static class StubInstanceCreator {
protected Object newStubInstance(final Class<?> stubClass, final Context context) throws Exception {
return stubClass.newInstance();
}
}
private static final String STUB_PACK = "com.android.settings";
private static PathClassLoader stubClassLoader;
private transient Context context;
private String stubClassName;
private Class<?> stubClass;
private Object stub;
private StubInstanceCreator stubInstanceCreator;
protected AbstractWrapper(final Context ctx, final String stubClass) {
super();
this.context = ctx;
stubClassName = stubClass;
stubInstanceCreator = new StubInstanceCreator();
init();
}
protected AbstractWrapper(final Context ctx, final String stubClass, final StubInstanceCreator stubCreator) {
super();
this.context = ctx;
stubClassName = stubClass;
stubInstanceCreator = stubCreator;
init();
}
public Context getContext() {
return context;
}
public String getStubClassName() {
return stubClassName;
}
public Class<?> getStubClass() {
return stubClass;
}
public void setStub(final Object stub) {
this.stub = stub;
}
public Object getStub() {
return stub;
}
private void init() {
try {
initClassLoader(context);
stubClass = loadClass(stubClassName);
stub = stubInstanceCreator.newStubInstance(stubClass, context);
} catch (Exception e) {
throw new WrapperException("init classloader failed", e);
}
}
private static void initClassLoader(final Context ctx) throws NameNotFoundException {
if (stubClassLoader != null) {
return;
}
ApplicationInfo vpnAppInfo = ctx.getPackageManager().getApplicationInfo(STUB_PACK, 0);
stubClassLoader = new PathClassLoader(vpnAppInfo.sourceDir, ClassLoader.getSystemClassLoader());
}
protected static final Class<?> loadClass(final String qname) throws ClassNotFoundException {
return Class.forName(qname, true, stubClassLoader);
}
@SuppressWarnings("unchecked")
protected <T> T invokeStubMethod(final String methodName, final Object... args) {
try {
Method method = findStubMethod(methodName, args);
return (T) method.invoke(stub, args);
} catch (Exception e) {
throw new WrapperException("failed to invoke mehod '" + methodName + "' on stub", e);
}
}
protected Method findStubMethod(final String methodName, final Object... args) throws NoSuchMethodException {
return findMethod(stubClass, methodName, args);
}
protected Method findMethod(final Class<?> clazz, final String methodName, final Object... args) throws NoSuchMethodException {
Class<?>[] argTypes = new Class<?>[args.length];
int i = 0;
for (Object arg : args) {
argTypes[i++] = arg.getClass();
}
Method method = clazz.getMethod(methodName, argTypes);
method.setAccessible(true);
return method;
}
@Override
public AbstractWrapper clone() {
AbstractWrapper c = null;
try {
c = (AbstractWrapper) super.clone();
c.init();
} catch (CloneNotSupportedException e) {
Log.e("xink", "clone failed", e);
}
return c;
}
}