package com.lody.virtual.client.hook.providers; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.IInterface; import android.os.ParcelFileDescriptor; import com.lody.virtual.helper.utils.VLog; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import mirror.android.content.IContentProvider; /** * @author Lody */ public class ProviderHook implements InvocationHandler { private static final Map<String, HookFetcher> PROVIDER_MAP = new HashMap<>(); static { PROVIDER_MAP.put("settings", new HookFetcher() { @Override public ProviderHook fetch(boolean external, IInterface provider) { return new SettingsProviderHook(provider); } }); PROVIDER_MAP.put("downloads", new HookFetcher() { @Override public ProviderHook fetch(boolean external, IInterface provider) { return new DownloadProviderHook(provider); } }); } protected final Object mBase; public ProviderHook(Object base) { this.mBase = base; } private static HookFetcher fetchHook(String authority) { HookFetcher fetcher = PROVIDER_MAP.get(authority); if (fetcher == null) { fetcher = new HookFetcher() { @Override public ProviderHook fetch(boolean external, IInterface provider) { if (external) { return new ExternalProviderHook(provider); } return new InternalProviderHook(provider); } }; } return fetcher; } private static IInterface createProxy(IInterface provider, ProviderHook hook) { if (provider == null || hook == null) { return null; } return (IInterface) Proxy.newProxyInstance(provider.getClass().getClassLoader(), new Class[]{ IContentProvider.TYPE, }, hook); } public static IInterface createProxy(boolean external, String authority, IInterface provider) { if (provider instanceof Proxy && Proxy.getInvocationHandler(provider) instanceof ProviderHook) { return provider; } ProviderHook.HookFetcher fetcher = ProviderHook.fetchHook(authority); if (fetcher != null) { ProviderHook hook = fetcher.fetch(external, provider); IInterface proxyProvider = ProviderHook.createProxy(provider, hook); if (proxyProvider != null) { provider = proxyProvider; } } return provider; } public Bundle call(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (Bundle) method.invoke(mBase, args); } public Uri insert(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (Uri) method.invoke(mBase, args); } public Cursor query(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (Cursor) method.invoke(mBase, args); } public String getType(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (String) method.invoke(mBase, args); } public int bulkInsert(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (int) method.invoke(mBase, args); } public int delete(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (int) method.invoke(mBase, args); } public int update(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (int) method.invoke(mBase, args); } public ParcelFileDescriptor openFile(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (ParcelFileDescriptor) method.invoke(mBase, args); } public AssetFileDescriptor openAssetFile(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { return (AssetFileDescriptor) method.invoke(mBase, args); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { processArgs(method, args); } catch (Throwable e) { e.printStackTrace(); } try { String name = method.getName(); if ("call".equals(name)) { return call(method, args); } else if ("insert".equals(name)) { return insert(method, args); } else if ("getType".equals(name)) { return getType(method, args); } else if ("insert".equals(name)) { return insert(method, args); } else if ("delete".equals(name)) { return bulkInsert(method, args); } else if ("delete".equals(name)) { return bulkInsert(method, args); } else if ("bulkInsert".equals(name)) { return bulkInsert(method, args); } else if ("update".equals(name)) { return update(method, args); } else if ("openFile".equals(name)) { return openFile(method, args); } else if ("openAssetFile".equals(name)) { return openAssetFile(method, args); } else if ("query".equals(name)) { return query(method, args); } return method.invoke(mBase, args); } catch (Throwable e) { VLog.d("ProviderHook", "call: %s (%s) with error", method.getName(), Arrays.toString(args)); if (e instanceof InvocationTargetException) { throw e.getCause(); } throw e; } } protected void processArgs(Method method, Object... args) { } public interface HookFetcher { ProviderHook fetch(boolean external, IInterface provider); } }