/*
**        DroidPlugin Project
**
** Copyright(c) 2015 Andy Zhang <[email protected]>
**
** This file is part of DroidPlugin.
**
** DroidPlugin is free software: you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation, either
** version 3 of the License, or (at your option) any later version.
**
** DroidPlugin is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with DroidPlugin.  If not, see <http://www.gnu.org/licenses/lgpl.txt>
**
**/

package com.morgoo.droidplugin.hook.handle;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;

import com.morgoo.droidplugin.hook.BaseHookHandle;
import com.morgoo.droidplugin.hook.HookedMethodHandler;
import com.morgoo.droidplugin.pm.PluginManager;
import com.morgoo.droidplugin.reflect.MethodUtils;
import com.morgoo.helper.Log;
import com.morgoo.helper.compat.IPackageDataObserverCompat;
import com.morgoo.helper.compat.ParceledListSliceCompat;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Andy Zhang([email protected]) on 2015/2/28.
 */
public class IPackageManagerHookHandle extends BaseHookHandle {

    private static final String TAG = IPackageManagerHookHandle.class.getSimpleName();

    public IPackageManagerHookHandle(Context hostContext) {
        super(hostContext);
    }

    @Override
    protected void init() {
        sHookedMethodHandlers.put("getPackageInfo", new getPackageInfo(mHostContext));
        sHookedMethodHandlers.put("getPackageUid", new getPackageUid(mHostContext));
        sHookedMethodHandlers.put("getPackageGids", new getPackageGids(mHostContext));
        sHookedMethodHandlers.put("currentToCanonicalPackageNames", new currentToCanonicalPackageNames(mHostContext));
        sHookedMethodHandlers.put("canonicalToCurrentPackageNames", new canonicalToCurrentPackageNames(mHostContext));
        sHookedMethodHandlers.put("getPermissionInfo", new getPermissionInfo(mHostContext));
        sHookedMethodHandlers.put("queryPermissionsByGroup", new queryPermissionsByGroup(mHostContext));
        sHookedMethodHandlers.put("getPermissionGroupInfo", new getPermissionGroupInfo(mHostContext));
        sHookedMethodHandlers.put("getAllPermissionGroups", new getAllPermissionGroups(mHostContext));
        sHookedMethodHandlers.put("getApplicationInfo", new getApplicationInfo(mHostContext));
        sHookedMethodHandlers.put("getActivityInfo", new getActivityInfo(mHostContext));
        sHookedMethodHandlers.put("getReceiverInfo", new getReceiverInfo(mHostContext));
        sHookedMethodHandlers.put("getServiceInfo", new getServiceInfo(mHostContext));
        sHookedMethodHandlers.put("getProviderInfo", new getProviderInfo(mHostContext));
        sHookedMethodHandlers.put("checkPermission", new checkPermission(mHostContext));
        sHookedMethodHandlers.put("checkUidPermission", new checkUidPermission(mHostContext));
        sHookedMethodHandlers.put("addPermission", new addPermission(mHostContext));
        sHookedMethodHandlers.put("removePermission", new removePermission(mHostContext));
        sHookedMethodHandlers.put("grantPermission", new grantPermission(mHostContext));
        sHookedMethodHandlers.put("revokePermission", new revokePermission(mHostContext));
        sHookedMethodHandlers.put("checkSignatures", new checkSignatures(mHostContext));
        sHookedMethodHandlers.put("getPackagesForUid", new getPackagesForUid(mHostContext));
        sHookedMethodHandlers.put("getNameForUid", new getNameForUid(mHostContext));
        sHookedMethodHandlers.put("getUidForSharedUser", new getUidForSharedUser(mHostContext));
        sHookedMethodHandlers.put("getFlagsForUid", new getFlagsForUid(mHostContext));
        sHookedMethodHandlers.put("resolveIntent", new resolveIntent(mHostContext));
        sHookedMethodHandlers.put("queryIntentActivities", new queryIntentActivities(mHostContext));
        sHookedMethodHandlers.put("queryIntentActivityOptions", new queryIntentActivityOptions(mHostContext));
        sHookedMethodHandlers.put("queryIntentReceivers", new queryIntentReceivers(mHostContext));
        sHookedMethodHandlers.put("resolveService", new resolveService(mHostContext));
        sHookedMethodHandlers.put("queryIntentServices", new queryIntentServices(mHostContext));
        sHookedMethodHandlers.put("queryIntentContentProviders", new queryIntentContentProviders(mHostContext));
        sHookedMethodHandlers.put("getInstalledPackages", new getInstalledPackages(mHostContext));
        sHookedMethodHandlers.put("getPackagesHoldingPermissions", new getPackagesHoldingPermissions(mHostContext));
        sHookedMethodHandlers.put("getInstalledApplications", new getInstalledApplications(mHostContext));
        sHookedMethodHandlers.put("getPersistentApplications", new getPersistentApplications(mHostContext));
        sHookedMethodHandlers.put("resolveContentProvider", new resolveContentProvider(mHostContext));
        sHookedMethodHandlers.put("querySyncProviders", new querySyncProviders(mHostContext));
        sHookedMethodHandlers.put("queryContentProviders", new queryContentProviders(mHostContext));
        sHookedMethodHandlers.put("getInstrumentationInfo", new getInstrumentationInfo(mHostContext));
        sHookedMethodHandlers.put("queryInstrumentation", new queryInstrumentation(mHostContext));
        sHookedMethodHandlers.put("getInstallerPackageName", new getInstallerPackageName(mHostContext));
        sHookedMethodHandlers.put("addPackageToPreferred", new addPackageToPreferred(mHostContext));
        sHookedMethodHandlers.put("removePackageFromPreferred", new removePackageFromPreferred(mHostContext));
        sHookedMethodHandlers.put("getPreferredPackages", new getPreferredPackages(mHostContext));
        sHookedMethodHandlers.put("resetPreferredActivities", new resetPreferredActivities(mHostContext));
        sHookedMethodHandlers.put("getLastChosenActivity", new getLastChosenActivity(mHostContext));
        sHookedMethodHandlers.put("setLastChosenActivity", new setLastChosenActivity(mHostContext));
        sHookedMethodHandlers.put("addPreferredActivity", new addPreferredActivity(mHostContext));
        sHookedMethodHandlers.put("replacePreferredActivity", new replacePreferredActivity(mHostContext));
        sHookedMethodHandlers.put("clearPackagePreferredActivities", new clearPackagePreferredActivities(mHostContext));
        sHookedMethodHandlers.put("getPreferredActivities", new getPreferredActivities(mHostContext));
        sHookedMethodHandlers.put("getHomeActivities", new getHomeActivities(mHostContext));
        sHookedMethodHandlers.put("setComponentEnabledSetting", new setComponentEnabledSetting(mHostContext));
        sHookedMethodHandlers.put("getComponentEnabledSetting", new getComponentEnabledSetting(mHostContext));
        sHookedMethodHandlers.put("setApplicationEnabledSetting", new setApplicationEnabledSetting(mHostContext));
        sHookedMethodHandlers.put("getApplicationEnabledSetting", new getApplicationEnabledSetting(mHostContext));
        sHookedMethodHandlers.put("setPackageStoppedState", new setPackageStoppedState(mHostContext));
        sHookedMethodHandlers.put("deleteApplicationCacheFiles", new deleteApplicationCacheFiles(mHostContext));
        sHookedMethodHandlers.put("clearApplicationUserData", new clearApplicationUserData(mHostContext));
        sHookedMethodHandlers.put("getPackageSizeInfo", new getPackageSizeInfo(mHostContext));
        sHookedMethodHandlers.put("performDexOpt", new performDexOpt(mHostContext));
        sHookedMethodHandlers.put("movePackage", new movePackage(mHostContext));
    }

    private class getPackageInfo extends HookedMethodHandler {
        public getPackageInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3,  4.01, 4.0.3_r1
            /*public PackageInfo getPackageInfo(String packageName, int flags) throws RemoteException;*/

            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
         /*public PackageInfo getPackageInfo(String packageName, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                String packageName = null;
                if (args.length > index0) {
                    if (args[index0] != null && args[index0] instanceof String) {
                        packageName = (String) args[index0];
                    }
                }

                int flags = 0;
                if (args.length > index1) {
                    if (args[index1] != null && args[index1] instanceof Integer) {
                        flags = (Integer) args[index1];
                    }
                }

                if (packageName != null) {
                    PackageInfo packageInfo = null;
                    try {
                        packageInfo = PluginManager.getInstance().getPackageInfo(packageName, flags);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (packageInfo != null) {
                        setFakedResult(packageInfo);
                        return true;
                    } else {
                        Log.i(TAG, "getPackageInfo(%s) fail,pkginfo is null", packageName);
                    }
                }

            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getPackageUid extends HookedMethodHandler {
        public getPackageUid(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1
            /*public int getPackageUid(String packageName) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1,  5.0.2_r1
            /*public int getPackageUid(String packageName, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0;
                String packageName = null;
                if (args.length > index0) {
                    if (args[index0] != null && args[index0] instanceof String) {
                        packageName = (String) args[index0];
                    }
                    if (packageName != null && PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index0] = mHostContext.getPackageName();
                    }
                }

            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getPackageGids extends HookedMethodHandler {
        public getPackageGids(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
            /*public int[] getPackageGids(String packageName) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0;
                String packageName = null;
                if (args.length > index0) {
                    if (args[index0] != null && args[index0] instanceof String) {
                        packageName = (String) args[index0];
                    }
                    if (packageName != null && PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index0] = mHostContext.getPackageName();
                    }
                }

            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class currentToCanonicalPackageNames extends HookedMethodHandler {
        public currentToCanonicalPackageNames(Context context) {
            super(context);
        }
        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public String[] currentToCanonicalPackageNames(String[] names) throws RemoteException;*/
    }

    private class canonicalToCurrentPackageNames extends HookedMethodHandler {
        public canonicalToCurrentPackageNames(Context context) {
            super(context);
        }

        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public String[] canonicalToCurrentPackageNames(String[] names) throws RemoteException;*/
    }

    private class getPermissionInfo extends HookedMethodHandler {
        public getPermissionInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
              /*public PermissionInfo getPermissionInfo(String name, int flags) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length > 1 && args[index0] instanceof String && args[index1] instanceof Integer) {
                    String packageName = (String) args[index0];
                    int flags = (Integer) args[index1];
                    PermissionInfo permissionInfo = PluginManager.getInstance().getPermissionInfo(packageName, flags);
                    if (permissionInfo != null) {
                        setFakedResult(permissionInfo);
                        return true;
                    }
                }
            }

            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class queryPermissionsByGroup extends HookedMethodHandler {
        public queryPermissionsByGroup(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
            /*public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) throws RemoteException;*/
            if (args != null && invokeResult instanceof List) {
                final int index0 = 0, index1 = 1;
                if (args.length > 1 && args[index0] instanceof String && args[index1] instanceof Integer) {
                    String group = (String) args[index0];
                    int flags = (Integer) args[index1];
                    List<PermissionInfo> infos = PluginManager.getInstance().queryPermissionsByGroup(group, flags);
                    if (infos != null && infos.size() > 0) {
                        List old = (List) invokeResult;
                        old.addAll(infos);
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class getPermissionGroupInfo extends HookedMethodHandler {
        public getPermissionGroupInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length > 1 && args[index0] instanceof String && args[index1] instanceof Integer) {
                    String name = (String) args[index0];
                    int flags = (Integer) args[index1];
                    PermissionGroupInfo permissionInfo = PluginManager.getInstance().getPermissionGroupInfo(name, flags);
                    if (permissionInfo != null) {
                        setFakedResult(permissionInfo);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getAllPermissionGroups extends HookedMethodHandler {
        public getAllPermissionGroups(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*  public List<PermissionGroupInfo> getAllPermissionGroups(int flags) throws RemoteException;*/
            if (args != null && invokeResult instanceof List) {
                final int index = 0;
                if (args.length > index && args[index] instanceof Integer) {
                    int flags = (Integer) args[index];
                    List<PermissionGroupInfo> infos = PluginManager.getInstance().getAllPermissionGroups(flags);
                    if (infos != null && infos.size() > 0) {
                        List old = (List) invokeResult;
                        old.addAll(infos);
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class getApplicationInfo extends HookedMethodHandler {
        public getApplicationInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public ApplicationInfo getApplicationInfo(String packageName, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof String && args[index1] instanceof Integer) {
                    String packageName = (String) args[index0];
                    int flags = (Integer) args[index1];
                    ApplicationInfo info = PluginManager.getInstance().getApplicationInfo(packageName, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getActivityInfo extends HookedMethodHandler {
        public getActivityInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public ActivityInfo getActivityInfo(ComponentName className, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public ActivityInfo getActivityInfo(ComponentName className, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof ComponentName && args[index1] instanceof Integer) {
                    ComponentName className = (ComponentName) args[index0];
                    int flags = (Integer) args[index1];
                    ActivityInfo info = PluginManager.getInstance().getActivityInfo(className, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getReceiverInfo extends HookedMethodHandler {
        public getReceiverInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /*public ActivityInfo getReceiverInfo(ComponentName className, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public ActivityInfo getReceiverInfo(ComponentName className, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof ComponentName && args[index1] instanceof Integer) {
                    ComponentName className = (ComponentName) args[index0];
                    int flags = (Integer) args[index1];
                    ActivityInfo info = PluginManager.getInstance().getReceiverInfo(className, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getServiceInfo extends HookedMethodHandler {
        public getServiceInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public ServiceInfo getServiceInfo(ComponentName className, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof ComponentName && args[index1] instanceof Integer) {
                    ComponentName className = (ComponentName) args[index0];
                    int flags = (Integer) args[index1];
                    ServiceInfo info = PluginManager.getInstance().getServiceInfo(className, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getProviderInfo extends HookedMethodHandler {
        public getProviderInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3
        /*public ProviderInfo getProviderInfo(ComponentName className, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ProviderInfo getProviderInfo(ComponentName className, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof ComponentName && args[index1] instanceof Integer) {
                    ComponentName className = (ComponentName) args[index0];
                    int flags = (Integer) args[index1];
                    ProviderInfo info = PluginManager.getInstance().getProviderInfo(className, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class checkPermission extends HookedMethodHandler {
        public checkPermission(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public int checkPermission(String permName, String pkgName) throws RemoteException;*/
            if (args != null) {
                final int index = 1;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index] = mHostContext.getPackageName();
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class checkUidPermission extends HookedMethodHandler {
        public checkUidPermission(Context context) {
            super(context);
        }
        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public int checkUidPermission(String permName, int uid) throws RemoteException;*/
    }

    private class addPermission extends HookedMethodHandler {
        public addPermission(Context context) {
            super(context);
        }

        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public boolean addPermission(PermissionInfo info) throws RemoteException;*/
    }

    private class removePermission extends HookedMethodHandler {
        public removePermission(Context context) {
            super(context);
        }

        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void removePermission(String name) throws RemoteException;*/
    }

    private class grantPermission extends HookedMethodHandler {
        public grantPermission(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
            //NO
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public void grantPermission(String packageName, String permissionName) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index] = mHostContext.getPackageName();
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class revokePermission extends HookedMethodHandler {
        public revokePermission(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
            //NO
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void revokePermission(String packageName, String permissionName) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index] = mHostContext.getPackageName();
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class checkSignatures extends HookedMethodHandler {
        public checkSignatures(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public int checkSignatures(String pkg1, String pkg2) throws android.os.RemoteException;*/
            final int index0 = 0, index1 = 1;
            String pkg0 = null, pkg1 = null;
            if (args != null && args[index0] != null && args[index0] instanceof String) {
                pkg0 = (String) args[index0];
            }

            if (args != null && args[index1] != null && args[index1] instanceof String) {
                pkg1 = (String) args[index1];
            }

            if (!TextUtils.isEmpty(pkg0) && !TextUtils.isEmpty(pkg1)) {
                PluginManager instance = PluginManager.getInstance();
                if (instance.isPluginPackage(pkg0) && instance.isPluginPackage(pkg1)) {
                    int result = instance.checkSignatures(pkg0, pkg1);
                    setFakedResult(result);
                    return true;
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getPackagesForUid extends HookedMethodHandler {
        public getPackagesForUid(Context context) {
            super(context);
        }
        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public  String[] getPackagesForUid(int uid) throws RemoteException*/
    }

    private class getNameForUid extends HookedMethodHandler {
        public getNameForUid(Context context) {
            super(context);
        }
        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public String getNameForUid(int uid) throws RemoteException;*/
    }

    private class getUidForSharedUser extends HookedMethodHandler {
        public getUidForSharedUser(Context context) {
            super(context);
        }
        //API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*  public int getUidForSharedUser(String sharedUserName) throws RemoteException;*/
    }

    private class getFlagsForUid extends HookedMethodHandler {
        public getFlagsForUid(Context context) {
            super(context);
        }
        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1
//        NO
        //API 4.4_r1, 5.0.2_r1
        /*public int getFlagsForUid(int uid) throws android.os.RemoteException;*/
    }

    private class resolveIntent extends HookedMethodHandler {
        public resolveIntent(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1, index2 = 2;
                Intent intent = null;
                if (args.length > index0) {
                    if (args[index0] instanceof Intent) {
                        intent = (Intent) args[index0];
                    }
                }

                String resolvedType = null;
                if (args.length > index1) {
                    if (args[index1] instanceof String) {
                        resolvedType = (String) args[index1];
                    }
                }

                Integer flags = 0;
                if (args.length > index2) {
                    if (args[index2] instanceof Integer) {
                        flags = (Integer) args[index2];
                    }
                }

                if (intent != null) {
                    ResolveInfo info = PluginManager.getInstance().resolveIntent(intent, resolvedType, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }

            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class queryIntentActivities extends HookedMethodHandler {
        public queryIntentActivities(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;*/
            if (args != null && invokeResult instanceof List) {
                final int index0 = 0, index1 = 1, index2 = 2;
                Intent intent = null;
                if (args.length > index0) {
                    if (args[index0] instanceof Intent) {
                        intent = (Intent) args[index0];
                    }
                }

                String resolvedType = null;
                if (args.length > index1) {
                    if (args[index1] instanceof String) {
                        resolvedType = (String) args[index1];
                    }
                }

                Integer flags = 0;
                if (args.length > index2) {
                    if (args[index2] instanceof Integer) {
                        flags = (Integer) args[index2];
                    }
                }

                if (intent != null) {
                    List<ResolveInfo> infos = PluginManager.getInstance().queryIntentActivities(intent, resolvedType, flags);
                    if (infos != null && infos.size() > 0) {
                        List old = (List) invokeResult;
                        old.addAll(infos);
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class queryIntentActivityOptions extends HookedMethodHandler {
        public queryIntentActivityOptions(Context context) {
            super(context);
        }
        //API 2.3, 4.01, 4.0.3_r1
        /*public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags) throws RemoteException;*/
        //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) throws RemoteException;*/
        //TODO 这里需要实现。查询插件的结果,并入到返回值中。
    }

    private class queryIntentReceivers extends HookedMethodHandler {
        public queryIntentReceivers(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /*  public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;*/
            if (args != null && invokeResult instanceof List) {
                final int index0 = 0, index1 = 1, index2 = 2;
                Intent intent = null;
                if (args.length > index0) {
                    if (args[index0] instanceof Intent) {
                        intent = (Intent) args[index0];
                    }
                }

                String resolvedType = null;
                if (args.length > index1) {
                    if (args[index1] instanceof String) {
                        resolvedType = (String) args[index1];
                    }
                }

                Integer flags = 0;
                if (args.length > index2) {
                    if (args[index2] instanceof Integer) {
                        flags = (Integer) args[index2];
                    }
                }

                if (intent != null) {
                    List<ResolveInfo> infos = PluginManager.getInstance().queryIntentReceivers(intent, resolvedType, flags);
                    if (infos != null && infos.size() > 0) {
                        List old = (List) invokeResult;
                        old.addAll(infos);
                        setFakedResult(invokeResult);
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class resolveService extends HookedMethodHandler {
        public resolveService(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public ResolveInfo resolveService(Intent intent, String resolvedType, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1, index2 = 2;
                Intent intent = null;
                if (args.length > index0) {
                    if (args[index0] instanceof Intent) {
                        intent = (Intent) args[index0];
                    }
                }

                String resolvedType = null;
                if (args.length > index1) {
                    if (args[index1] instanceof String) {
                        resolvedType = (String) args[index1];
                    }
                }

                Integer flags = 0;
                if (args.length > index2) {
                    if (args[index2] instanceof Integer) {
                        flags = (Integer) args[index2];
                    }
                }

                if (intent != null) {
                    ResolveInfo info = PluginManager.getInstance().resolveService(intent, resolvedType, flags);
                    if (info != null) {
                        setFakedResult(info);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class queryIntentServices extends HookedMethodHandler {
        public queryIntentServices(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3
        /*public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) throwsRemoteException;*/
            if (args != null && invokeResult instanceof List) {
                final int index0 = 0, index1 = 1, index2 = 2;
                Intent intent = null;
                if (args.length > index0) {
                    if (args[index0] instanceof Intent) {
                        intent = (Intent) args[index0];
                    }
                }

                String resolvedType = null;
                if (args.length > index1) {
                    if (args[index1] instanceof String) {
                        resolvedType = (String) args[index1];
                    }
                }

                Integer flags = 0;
                if (args.length > index2) {
                    if (args[index2] instanceof Integer) {
                        flags = (Integer) args[index2];
                    }
                }

                if (intent != null) {
                    List<ResolveInfo> infos = PluginManager.getInstance().queryIntentServices(intent, resolvedType, flags);
                    if (infos != null && infos.size() > 0) {
                        List old = (List) invokeResult;
                        old.addAll(infos);
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class queryIntentContentProviders extends HookedMethodHandler {
        public queryIntentContentProviders(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //ONLY FOR API 4.4_r1, 5.0.2_r1
        /*public List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;*/
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (args != null && invokeResult instanceof List) {
                    final int index0 = 0, index1 = 1, index2 = 2;
                    Intent intent = null;
                    if (args.length > index0) {
                        if (args[index0] instanceof Intent) {
                            intent = (Intent) args[index0];
                        }
                    }

                    String resolvedType = null;
                    if (args.length > index1) {
                        if (args[index1] instanceof String) {
                            resolvedType = (String) args[index1];
                        }
                    }

                    Integer flags = 0;
                    if (args.length > index2) {
                        if (args[index2] instanceof Integer) {
                            flags = (Integer) args[index2];
                        }
                    }

                    if (intent != null) {
                        List<ResolveInfo> infos = PluginManager.getInstance().queryIntentContentProviders(intent, resolvedType, flags);
                        if (infos != null && infos.size() > 0) {
                            List old = (List) invokeResult;
                            old.addAll(infos);
                        }
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class getInstalledPackages extends HookedMethodHandler {
        public getInstalledPackages(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3
        /* public List<PackageInfo> getInstalledPackages(int flags) throws RemoteException;*/

            //API 4.01, 4.0.3_r1, 4.1.1_r1
        /*public ParceledListSlice getInstalledPackages(int flags, String lastRead) */

            //API 4.2_r1
        /* public ParceledListSlice getInstalledPackages(int flags, String lastRead, int userId) throws RemoteException*/

            //API 4.3_r1, 4.4_r1,5.0.2_r1
        /*public ParceledListSlice getInstalledPackages(int flags, int userId) throws RemoteException;*/
            try {
                if (invokeResult != null && ParceledListSliceCompat.isParceledListSlice(invokeResult)) {
                    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                        Method getListMethod = MethodUtils.getAccessibleMethod(invokeResult.getClass(), "getList");
                        List data = (List) getListMethod.invoke(invokeResult);
                        final int index0 = 0;
                        if (args.length > index0 && args[index0] instanceof Integer) {
                            int flags = (Integer) args[index0];
                            List<PackageInfo> infos = PluginManager.getInstance().getInstalledPackages(flags);
                            if (infos != null && infos.size() > 0) {
                                data.addAll(infos);
                            }
                        }
                    } else {
                        Method isLastSliceMethod = invokeResult.getClass().getMethod("isLastSlice");
                        Method setLastSlice = invokeResult.getClass().getMethod("setLastSlice", boolean.class);
                        Method appendMethod = invokeResult.getClass().getMethod("append", Parcelable.class);
                        Method populateList = invokeResult.getClass().getMethod("populateList", List.class, Parcelable.Creator.class);
                        if (!setLastSlice.isAccessible()) {
                            setLastSlice.setAccessible(true);
                        }
                        if (!populateList.isAccessible()) {
                            populateList.setAccessible(true);
                        }
                        if (!isLastSliceMethod.isAccessible()) {
                            isLastSliceMethod.setAccessible(true);
                        }
                        if (!appendMethod.isAccessible()) {
                            appendMethod.setAccessible(true);
                        }
                        boolean isLastSlice = (Boolean) isLastSliceMethod.invoke(invokeResult);
                        if (isLastSlice) {
                            final int index0 = 0;
                            if (args.length > index0 && args[index0] instanceof Integer) {
                                int flags = (Integer) args[index0];
                                List<PackageInfo> infos = PluginManager.getInstance().getInstalledPackages(flags);
                                if (infos != null && infos.size() > 0) {
                                    final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
                                    populateList.invoke(invokeResult, packageInfos, PackageInfo.CREATOR);
                                    packageInfos.addAll(infos);
                                    Object parceledListSlice = invokeResult.getClass().newInstance();
                                    for (PackageInfo packageInfo : packageInfos) {
                                        appendMethod.invoke(parceledListSlice, packageInfo);
                                    }
                                    setLastSlice.invoke(parceledListSlice, true);
                                    setFakedResult(parceledListSlice);
                                }
                            }
                        }
                    }
                } else if (invokeResult instanceof List) {
                    final int index0 = 0;
                    if (args.length > index0 && args[index0] instanceof Integer) {
                        int flags = (Integer) args[index0];
                        List<PackageInfo> infos = PluginManager.getInstance().getInstalledPackages(flags);
                        if (infos != null && infos.size() > 0) {
                            List old = (List) invokeResult;
                            old.addAll(infos);
                        }
                    }
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class getPackagesHoldingPermissions extends HookedMethodHandler {
        public getPackagesHoldingPermissions(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1 NO
        //API 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ParceledListSlice getPackagesHoldingPermissions(String[] permissions, int flags, int userId) throws RemoteException;*/
    }

    private class getInstalledApplications extends HookedMethodHandler {
        public getInstalledApplications(Context context) {
            super(context);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            //API 2.3
        /* public List<ApplicationInfo> getInstalledApplications(int flags) throws RemoteException;*/
            //API 4.01, 4.0.3_r1
        /*public ParceledListSlice getInstalledApplications(int flags, java.lang.String lastRead) throws RemoteException;*/

            //API 4.1.1_r1 , 4.2_r1
        /*  public ParceledListSlice getInstalledApplications(int flags, java.lang.String lastRead, int userId) throws RemoteException*/

            //API 4.3_r1,4.4_r1, 5.0.2_r1
        /* public ParceledListSlice getInstalledApplications(int flags, int userId) throws RemoteException */
            try {
                if (ParceledListSliceCompat.isParceledListSlice(invokeResult)) {
                    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                        Method getListMethod = MethodUtils.getAccessibleMethod(invokeResult.getClass(), "getList");
                        List data = (List) getListMethod.invoke(invokeResult);
                        final int index0 = 0;
                        if (args.length > index0 && args[index0] instanceof Integer) {
                            int flags = (Integer) args[index0];
                            List<ApplicationInfo> infos = PluginManager.getInstance().getInstalledApplications(flags);
                            if (infos != null && infos.size() > 0) {
                                data.addAll(infos);
                            }
                        }
                    } else {
                        Method isLastSliceMethod = invokeResult.getClass().getMethod("isLastSlice");
                        Method setLastSlice = invokeResult.getClass().getMethod("setLastSlice", boolean.class);
                        Method appendMethod = invokeResult.getClass().getMethod("append", Parcelable.class);
                        Method populateList = invokeResult.getClass().getMethod("populateList", List.class, Parcelable.Creator.class);
                        if (!setLastSlice.isAccessible()) {
                            setLastSlice.setAccessible(true);
                        }
                        if (!populateList.isAccessible()) {
                            populateList.setAccessible(true);
                        }
                        if (!isLastSliceMethod.isAccessible()) {
                            isLastSliceMethod.setAccessible(true);
                        }
                        if (!appendMethod.isAccessible()) {
                            appendMethod.setAccessible(true);
                        }
                        boolean isLastSlice = (Boolean) isLastSliceMethod.invoke(invokeResult);
                        if (isLastSlice) {
                            final int index0 = 0;
                            if (args.length > index0 && args[index0] instanceof Integer) {
                                int flags = (Integer) args[index0];
                                List<ApplicationInfo> infos = PluginManager.getInstance().getInstalledApplications(flags);
                                if (infos != null && infos.size() > 0) {
                                    final List<ApplicationInfo> packageInfos = new ArrayList<ApplicationInfo>();
                                    populateList.invoke(invokeResult, packageInfos, ApplicationInfo.CREATOR);
                                    packageInfos.addAll(infos);
                                    Object parceledListSlice = invokeResult.getClass().newInstance();
                                    for (ApplicationInfo info : packageInfos) {
                                        appendMethod.invoke(parceledListSlice, info);
                                    }
                                    setLastSlice.invoke(parceledListSlice, true);
                                    setFakedResult(parceledListSlice);
                                }
                            }
                        }
                    }
                } else if (invokeResult instanceof List) {
                    final int index0 = 0;
                    if (args.length > index0 && args[index0] instanceof Integer) {
                        int flags = (Integer) args[index0];
                        List<ApplicationInfo> infos = PluginManager.getInstance().getInstalledApplications(flags);
                        if (infos != null && infos.size() > 0) {
                            List old = (List) invokeResult;
                            old.addAll(infos);
                        }
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "fake getInstalledApplications", e);
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class getPersistentApplications extends HookedMethodHandler {
        public getPersistentApplications(Context context) {
            super(context);
        }
        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*  public List<ApplicationInfo> getPersistentApplications(int flags) throws RemoteException;*/
    }

    private class resolveContentProvider extends HookedMethodHandler {
        public resolveContentProvider(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /*public ProviderInfo resolveContentProvider(String name, int flags) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public ProviderInfo resolveContentProvider(String name, int flags, int userId) throws RemoteException*/

            return super.beforeInvoke(receiver, method, args);
        }

        @Override
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
            if (args != null) {
                if (invokeResult == null) {
                    final int index0 = 0, index1 = 1;
                    if (args.length >= 2 && args[index0] instanceof String && args[index1] instanceof Integer) {
                        String name = (String) args[index0];
                        Integer flags = (Integer) args[index1];
                        ProviderInfo info = PluginManager.getInstance().resolveContentProvider(name, flags);
                        if (info != null) {
                            setFakedResult(info);
                        }
                    }
                }
            }
            super.afterInvoke(receiver, method, args, invokeResult);
        }
    }

    private class querySyncProviders extends HookedMethodHandler {
        public querySyncProviders(Context context) {
            super(context);
        }
        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*  public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) throws RemoteException;*/
        //TODO 查询插件的结果并入到返回值中。
    }

    private class queryContentProviders extends HookedMethodHandler {
        public queryContentProviders(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) throws RemoteException;*/
        //TODO 查询插件的结果并入到返回值中。
    }

    private class getInstrumentationInfo extends HookedMethodHandler {
        public getInstrumentationInfo(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) throws RemoteException;*/
        //FIXME 自动化测试相关的东西,先不处理。
    }

    private class queryInstrumentation extends HookedMethodHandler {
        public queryInstrumentation(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public List<InstrumentationInfo> queryInstrumentation(String targetPackage, int flags) throws RemoteException;*/
        //FIXME 自动化测试相关的东西,先不处理。
    }

    private class getInstallerPackageName extends HookedMethodHandler {
        public getInstallerPackageName(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public String getInstallerPackageName(String packageName) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        setFakedResult(mHostContext.getPackageName());
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class addPackageToPreferred extends HookedMethodHandler {
        public addPackageToPreferred(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void addPackageToPreferred(String packageName) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index] = mHostContext.getPackageName();
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class removePackageFromPreferred extends HookedMethodHandler {
        public removePackageFromPreferred(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
         /*public void removePackageFromPreferred(String packageName) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index] = mHostContext.getPackageName();
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getPreferredPackages extends HookedMethodHandler {
        public getPreferredPackages(Context context) {
            super(context);
        }
        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public List<PackageInfo> getPreferredPackages(int flags) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class resetPreferredActivities extends HookedMethodHandler {
        public resetPreferredActivities(Context context) {
            super(context);
        }
        //API  4.3_r1, 4.4_r1, 5.0.2_r1
        /* public void resetPreferredActivities(int userId) throws android.os.RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class getLastChosenActivity extends HookedMethodHandler {
        public getLastChosenActivity(Context context) {
            super(context);
        }
        //API 4.4_r1, 5.0.2_r1
        /*public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class setLastChosenActivity extends HookedMethodHandler {
        public setLastChosenActivity(Context context) {
            super(context);
        }

        //API 4.4_r1, 5.0.2_r1
        /*public void setLastChosenActivity(Intent intent, String resolvedType, int flags, IntentFilter filter, int match, ComponentName activity) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class addPreferredActivity extends HookedMethodHandler {
        public addPreferredActivity(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1
        /* public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) throws RemoteException;*/
        //API  4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class replacePreferredActivity extends HookedMethodHandler {
        public replacePreferredActivity(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1
        /*public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) throws RemoteException;*/

        //API 5.0.2_r1
        /*public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class clearPackagePreferredActivities extends HookedMethodHandler {
        public clearPackagePreferredActivities(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void clearPackagePreferredActivities(String packageName) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        args[index] = mHostContext.getPackageName();
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getPreferredActivities extends HookedMethodHandler {
        public getPreferredActivities(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    //ONLY for 4.4_r1 or later
    private class getHomeActivities extends HookedMethodHandler {
        public getHomeActivities(Context context) {
            super(context);
        }

        //API 4.4_r1, 5.0.2_r1
        /* public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates) throws RemoteException;*/
        //这里插件没有结果的,所以就不处理了。
    }

    private class setComponentEnabledSetting extends HookedMethodHandler {
        public setComponentEnabledSetting(Context context) {
            super(context);
        }

        //API 2.3
        /* public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) throws RemoteException;*/

        //API 4.01

        //API 4.0.3_r1

        //API 4.1.1_r1

        //API 4.2_r1

        //API 4.3_r1

        //API 4.4_r1

        //API 5.0.2_r1
        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            final int index = 0;
            if (args != null && args.length > index && args[index] instanceof ComponentName) {
                ComponentName componentName = (ComponentName) args[index];
                if (PluginManager.getInstance().isPluginPackage(componentName)) {
                    setFakedResult(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
                    return true;
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getComponentEnabledSetting extends HookedMethodHandler {
        public getComponentEnabledSetting(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1
        /*public int getComponentEnabledSetting(ComponentName componentName) throws RemoteException;*/
        //API 4.2_r1, 4.3_r1,4.4_r1, 5.0.2_r1
        /*public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags, int userId) throws RemoteException*/

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            final int index = 0;
            if (args != null && args.length > index && args[index] instanceof ComponentName) {
                ComponentName componentName = (ComponentName) args[index];
                if (PluginManager.getInstance().isPluginPackage(componentName)) {
                    setFakedResult(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
                    return true;
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class setApplicationEnabledSetting extends HookedMethodHandler {
        public setApplicationEnabledSetting(Context context) {
            super(context);
        }

        //API 2.3
        /*public void setApplicationEnabledSetting(String packageName, int newState, int flags) throws RemoteException;*/
        //API  4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1
        /*public void setApplicationEnabledSetting(String packageName, int newState, int flags, int userId) throws RemoteException*/
        //API  4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void setApplicationEnabledSetting(String packageName, int newState, int flags, int userId, String callingPackage) throws RemoteException;*/
        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            final int index = 0;
            if (args != null && args.length > index && args[index] instanceof String) {
                String packageName = (String) args[index];
                if (PluginManager.getInstance().isPluginPackage(packageName)) {
                    return true;
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getApplicationEnabledSetting extends HookedMethodHandler {
        public getApplicationEnabledSetting(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1,
        /* public int getApplicationEnabledSetting(String packageName) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public int getApplicationEnabledSetting(String packageName, int userId) throws RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        //DO NOTHING
                        setFakedResult(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class setPackageStoppedState extends HookedMethodHandler {
        public setPackageStoppedState(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 4.01, 4.0.3_r1
        /* public void setPackageStoppedState(String packageName, boolean stopped) throws RemoteException;*/

            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void setPackageStoppedState(java.lang.String packageName, boolean stopped, int userId) throws android.os.RemoteException;*/
            if (args != null) {
                final int index = 0;
                if (args.length > index && args[index] instanceof String) {
                    String packageName = (String) args[index];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        //DO NOTHING
                        PluginManager.getInstance().forceStopPackage(packageName);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class deleteApplicationCacheFiles extends HookedMethodHandler {
        public deleteApplicationCacheFiles(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /* public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof String && IPackageDataObserverCompat.isIPackageDataObserver(args[index1])) {
                    String packageName = (String) args[index0];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        final Object observer = args[index1];
                        PluginManager.getInstance().deleteApplicationCacheFiles(packageName, observer);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class clearApplicationUserData extends HookedMethodHandler {
        public clearApplicationUserData(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1
        /* public void clearApplicationUserData(String packageName, IPackageDataObserver observer) throws RemoteException;*/
            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void clearApplicationUserData(String packageName, IPackageDataObserver observer, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                if (args.length >= 2 && args[index0] instanceof String && IPackageDataObserverCompat.isIPackageDataObserver(args[index1])) {
                    String packageName = (String) args[index0];
                    if (PluginManager.getInstance().isPluginPackage(packageName)) {
                        final Object observer = args[index1];
                        PluginManager.getInstance().clearApplicationUserData(packageName, observer);
                        return true;
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class getPackageSizeInfo extends HookedMethodHandler {
        public getPackageSizeInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1
        /*public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) throws RemoteException;*/
            //API 4.2_r1 4.3_r1, 4.4_r1, 5.0.2_r1
        /*public void getPackageSizeInfo(String packageName, int userHandle, IPackageStatsObserver observer) throws RemoteException;*/
            //TODO 获取包大小。
            return super.beforeInvoke(receiver, method, args);
        }
    }

    private class performDexOpt extends HookedMethodHandler {
        public performDexOpt(Context context) {
            super(context);
        }

        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1
        /*  public boolean performDexOpt(String packageName) throws RemoteException;*/
        //API 5.0.2_r1 NO
    }

    private class movePackage extends HookedMethodHandler {
        public movePackage(Context context) {
            super(context);
        }
        //API 2.3, 4.01, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
        /*  public void movePackage(String packageName, IPackageMoveObserver observer, int flags) throws RemoteException;*/
    }
}