[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.idea\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "import java.security.acl.Group\n\napply plugin: 'com.android.application'\napply plugin: 'me.tatarka.retrolambda'\n\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"25.0.2\"\n    defaultConfig {\n        applicationId \"io.virtualapp\"\n        minSdkVersion 15\n        targetSdkVersion 22\n        versionCode 24\n        versionName \"1.2.5\"\n        multiDexEnabled true\n        android {\n            defaultConfig {\n                ndk {\n                    abiFilters \"armeabi\", \"armeabi-v7a\", \"x86\"\n                }\n            }\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n}\n\nandroid {\n    lintOptions {\n        checkReleaseBuilds false\n        // Or, if you prefer, you can continue to check for errors in release builds,\n        // but continue the build even when errors are found:\n        abortOnError false\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    compile project(':lib')\n    //Android Lib\n    compile 'com.android.support:multidex:1.0.1'\n    compile 'com.android.support:appcompat-v7:25.0.1'\n    compile 'com.melnykov:floatingactionbutton:1.3.0'\n    compile 'com.android.support:recyclerview-v7:25.0.1'\n    compile 'com.android.support:percent:25.0.1'\n    compile 'com.android.support:design:25.0.1'\n    compile 'com.android.support:cardview-v7:25.0.1'\n    //Promise Support\n    compile 'org.jdeferred:jdeferred-android-aar:1.2.4'\n    // ThirdParty\n    compile 'com.jonathanfinerty.once:once:1.0.3'\n    compile 'com.flurry.android:analytics:6.9.2'\n    compile 'com.kyleduo.switchbutton:library:1.4.6'\n    compile ('com.google.android.gms:play-services-ads:11.0.0'){\n        exclude group:'com.android.support'\n    }\n\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/lody/Desktop/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"io.virtualapp\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <application\n        android:name=\".VApp\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\".splash.SplashActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/AppTheme\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".home.HomeActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/UITheme\" />\n\n        <activity\n            android:name=\".home.ListAppActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/UITheme\" />\n\n        <activity\n            android:name=\".home.LoadingActivity\"\n            android:excludeFromRecents=\"true\"\n            android:noHistory=\"true\"\n            android:screenOrientation=\"portrait\"\n            android:taskAffinity=\"va.task.loading\"\n            android:theme=\"@style/TransparentTheme\" />\n\n    </application>\n\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/VApp.java",
    "content": "package io.virtualapp;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.os.Build;\nimport android.support.multidex.MultiDexApplication;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.flurry.android.FlurryAgent;\nimport com.google.android.gms.ads.MobileAds;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport io.virtualapp.delegate.MyAppRequestListener;\nimport io.virtualapp.delegate.MyComponentDelegate;\nimport io.virtualapp.delegate.MyPhoneInfoDelegate;\nimport io.virtualapp.delegate.MyTaskDescriptionDelegate;\nimport jonathanfinerty.once.Once;\n\n/**\n * @author Lody\n */\n// 这里的函数会被多次调用，特性来自于 process:\npublic class VApp extends MultiDexApplication {\n\n    private static VApp gApp;\n    private SharedPreferences mPreferences;\n\n    public static VApp getApp() {\n        return gApp;\n    }\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n        mPreferences = base.getSharedPreferences(\"va\", Context.MODE_MULTI_PROCESS);\n        VASettings.ENABLE_IO_REDIRECT = true;\n        VASettings.ENABLE_INNER_SHORTCUT = false;\n        try {\n            VirtualCore.get().startup(base);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void onCreate() {\n        gApp = this;\n        super.onCreate();\n        VirtualCore virtualCore = VirtualCore.get();\n        virtualCore.initialize(new VirtualCore.VirtualInitializer() {\n\n            @Override\n            public void onMainProcess() {\n                Once.initialise(VApp.this);\n                MobileAds.initialize(VApp.this, \"ca-app-pub-1609791120068944~5426483711\");\n                new FlurryAgent.Builder()\n                        .withLogEnabled(true)\n                        .withListener(() -> {\n                            // nothing\n                        })\n                        .build(VApp.this, \"48RJJP7ZCZZBB6KMMWW5\");\n            }\n\n            @Override\n            public void onVirtualProcess() {\n                //listener components\n                virtualCore.setComponentDelegate(new MyComponentDelegate());\n                //fake phone imei,macAddress,BluetoothAddress\n                virtualCore.setPhoneInfoDelegate(new MyPhoneInfoDelegate());\n                //fake task description's icon and title\n                virtualCore.setTaskDescriptionDelegate(new MyTaskDescriptionDelegate());\n            }\n\n            @Override\n            public void onServerProcess() {\n                virtualCore.setAppRequestListener(new MyAppRequestListener(VApp.this));\n                virtualCore.addVisibleOutsidePackage(\"com.tencent.mobileqq\");\n                virtualCore.addVisibleOutsidePackage(\"com.tencent.mobileqqi\");\n                virtualCore.addVisibleOutsidePackage(\"com.tencent.minihd.qq\");\n                virtualCore.addVisibleOutsidePackage(\"com.tencent.qqlite\");\n                virtualCore.addVisibleOutsidePackage(\"com.facebook.katana\");\n                virtualCore.addVisibleOutsidePackage(\"com.whatsapp\");\n                virtualCore.addVisibleOutsidePackage(\"com.tencent.mm\");\n                virtualCore.addVisibleOutsidePackage(\"com.immomo.momo\");\n            }\n        });\n    }\n\n    public static SharedPreferences getPreferences() {\n        return getApp().mPreferences;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/VCommends.java",
    "content": "package io.virtualapp;\n\n/**\n * @author Lody\n */\npublic class VCommends {\n\n\tpublic static final String TAG_NEW_VERSION = \"First launch new Version\";\n\tpublic static final String TAG_SHOW_ADD_APP_GUIDE = \"Should show add app guide\";\n\n\tpublic static final int REQUEST_SELECT_APP = 5;\n\n\tpublic static final String EXTRA_APP_INFO_LIST = \"va.extra.APP_INFO_LIST\";\n\n\tpublic static final String TAG_ASK_INSTALL_GMS = \"va.extra.ASK_INSTALL_GMS\";\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/BasePresenter.java",
    "content": "package io.virtualapp.abs;\n\n/**\n * @author Lody\n */\npublic interface BasePresenter {\n\tvoid start();\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/BaseView.java",
    "content": "package io.virtualapp.abs;\n\nimport android.app.Activity;\nimport android.content.Context;\n\n/**\n * @author Lody\n */\npublic interface BaseView<T> {\n    Activity getActivity();\n    Context getContext();\n\tvoid setPresenter(T presenter);\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/Callback.java",
    "content": "package io.virtualapp.abs;\n\n/**\n * @author Lody\n */\n\npublic interface Callback<T> {\n    void callback(T result);\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/Value.java",
    "content": "package io.virtualapp.abs;\n\n/**\n * @author Lody\n */\n\npublic class Value<T> {\n    public T val;\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/nestedadapter/RecyclerViewAdapterWrapper.java",
    "content": "package io.virtualapp.abs.nestedadapter;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.ViewGroup;\n\npublic class RecyclerViewAdapterWrapper extends RecyclerView.Adapter {\n\n    protected final RecyclerView.Adapter wrapped;\n\n    public RecyclerViewAdapterWrapper(RecyclerView.Adapter wrapped) {\n        super();\n        this.wrapped = wrapped;\n        this.wrapped.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {\n            public void onChanged() {\n                notifyDataSetChanged();\n            }\n\n            public void onItemRangeChanged(int positionStart, int itemCount) {\n                notifyItemRangeChanged(positionStart, itemCount);\n            }\n\n            public void onItemRangeInserted(int positionStart, int itemCount) {\n                notifyItemRangeInserted(positionStart, itemCount);\n            }\n\n            public void onItemRangeRemoved(int positionStart, int itemCount) {\n                notifyItemRangeRemoved(positionStart, itemCount);\n            }\n\n            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {\n                notifyItemMoved(fromPosition, toPosition);\n            }\n        });\n    }\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        return wrapped.onCreateViewHolder(parent, viewType);\n    }\n\n\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n        wrapped.onBindViewHolder(holder, position);\n    }\n\n    @Override\n    public int getItemCount() {\n        return wrapped.getItemCount();\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        return wrapped.getItemViewType(position);\n    }\n\n    @Override\n    public void setHasStableIds(boolean hasStableIds) {\n        wrapped.setHasStableIds(hasStableIds);\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return wrapped.getItemId(position);\n    }\n\n    @Override\n    public void onViewRecycled(RecyclerView.ViewHolder holder) {\n        wrapped.onViewRecycled(holder);\n    }\n\n    @Override\n    public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {\n        return wrapped.onFailedToRecycleView(holder);\n    }\n\n    @Override\n    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {\n        wrapped.onViewAttachedToWindow(holder);\n    }\n\n    @Override\n    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {\n        wrapped.onViewDetachedFromWindow(holder);\n    }\n\n    @Override\n    public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {\n        wrapped.registerAdapterDataObserver(observer);\n    }\n\n    @Override\n    public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {\n        wrapped.unregisterAdapterDataObserver(observer);\n    }\n\n    @Override\n    public void onAttachedToRecyclerView(RecyclerView recyclerView) {\n        wrapped.onAttachedToRecyclerView(recyclerView);\n    }\n\n    @Override\n    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {\n        wrapped.onDetachedFromRecyclerView(recyclerView);\n    }\n\n    public RecyclerView.Adapter getWrappedAdapter() {\n        return wrapped;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/nestedadapter/SmartRecyclerAdapter.java",
    "content": "package io.virtualapp.abs.nestedadapter;\n\nimport android.support.annotation.NonNull;\nimport android.support.v7.widget.GridLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.StaggeredGridLayoutManager;\nimport android.view.View;\nimport android.view.ViewGroup;\n\npublic class SmartRecyclerAdapter extends RecyclerViewAdapterWrapper {\n    public static final int TYPE_HEADER = -1;\n    public static final int TYPE_FOOTER = -2;\n\n    private RecyclerView.LayoutManager layoutManager;\n\n    private View headerView, footerView;\n\n    public SmartRecyclerAdapter(@NonNull RecyclerView.Adapter targetAdapter) {\n        super(targetAdapter);\n    }\n\n    public void setHeaderView(View view) {\n        headerView = view;\n        getWrappedAdapter().notifyDataSetChanged();\n    }\n\n    public void removeHeaderView() {\n        headerView = null;\n        getWrappedAdapter().notifyDataSetChanged();\n    }\n\n    public void setFooterView(View view) {\n        footerView = view;\n        getWrappedAdapter().notifyDataSetChanged();\n    }\n\n    public void removeFooterView() {\n        footerView = null;\n        getWrappedAdapter().notifyDataSetChanged();\n    }\n\n    private void setGridHeaderFooter(RecyclerView.LayoutManager layoutManager) {\n        if (layoutManager instanceof GridLayoutManager) {\n            final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;\n            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {\n                @Override\n                public int getSpanSize(int position) {\n                    boolean isShowHeader = (position == 0 && hasHeader());\n                    boolean isShowFooter = (position == getItemCount() - 1 && hasFooter());\n                    if (isShowFooter || isShowHeader) {\n                        return gridLayoutManager.getSpanCount();\n                    }\n                    return 1;\n                }\n            });\n        }\n    }\n\n    private boolean hasHeader() {\n        return headerView != null;\n    }\n\n    private boolean hasFooter() {\n        return footerView != null;\n    }\n\n    @Override\n    public void onAttachedToRecyclerView(RecyclerView recyclerView) {\n        super.onAttachedToRecyclerView(recyclerView);\n        layoutManager = recyclerView.getLayoutManager();\n        setGridHeaderFooter(layoutManager);\n    }\n\n    @Override\n    public int getItemCount() {\n        return super.getItemCount() + (hasHeader() ? 1 : 0) + (hasFooter() ? 1 : 0);\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        if (hasHeader() && position == 0) {\n            return TYPE_HEADER;\n        }\n\n        if (hasFooter() && position == getItemCount() - 1) {\n            return TYPE_FOOTER;\n        }\n        return super.getItemViewType(hasHeader() ? position - 1 : position);\n    }\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        View itemView = null;\n        if (viewType == TYPE_HEADER) {\n            itemView = headerView;\n        } else if (viewType == TYPE_FOOTER) {\n            itemView = footerView;\n        }\n        if (itemView != null) {\n            //set StaggeredGridLayoutManager header & footer view\n            if (layoutManager instanceof StaggeredGridLayoutManager) {\n                ViewGroup.LayoutParams targetParams = itemView.getLayoutParams();\n                StaggeredGridLayoutManager.LayoutParams StaggerLayoutParams;\n                if (targetParams != null) {\n                    StaggerLayoutParams = new StaggeredGridLayoutManager.LayoutParams(targetParams.width, targetParams.height);\n                } else {\n                    StaggerLayoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n                }\n                StaggerLayoutParams.setFullSpan(true);\n                itemView.setLayoutParams(StaggerLayoutParams);\n            }\n            return new RecyclerView.ViewHolder(itemView) {\n            };\n        }\n        return super.onCreateViewHolder(parent, viewType);\n    }\n\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n        if (getItemViewType(position) == TYPE_HEADER || getItemViewType(position) == TYPE_FOOTER) {\n            //if you need get header & footer state , do here\n            return;\n        }\n        super.onBindViewHolder(holder, hasHeader() ? position - 1 : position);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/percent/PercentLinearLayout.java",
    "content": "package io.virtualapp.abs.percent;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.support.percent.PercentLayoutHelper;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\n\n/**\n * @author Lody\n */\npublic class PercentLinearLayout extends LinearLayout {\n\n\tprivate PercentLayoutHelper mPercentLayoutHelper;\n\n\tpublic PercentLinearLayout(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\n\t\tmPercentLayoutHelper = new PercentLayoutHelper(this);\n\t}\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\tmPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);\n\t\tsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\t\tif (mPercentLayoutHelper.handleMeasuredStateTooSmall()) {\n\t\t\tsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onLayout(boolean changed, int l, int t, int r, int b) {\n\t\tsuper.onLayout(changed, l, t, r, b);\n\t\tmPercentLayoutHelper.restoreOriginalParams();\n\t}\n\n\t@Override\n\tpublic LayoutParams generateLayoutParams(AttributeSet attrs) {\n\t\treturn new LayoutParams(getContext(), attrs);\n\t}\n\n\tpublic static class LayoutParams extends LinearLayout.LayoutParams\n\t\t\timplements\n\t\t\t\tPercentLayoutHelper.PercentLayoutParams {\n\t\tprivate PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;\n\n\t\tpublic LayoutParams(Context c, AttributeSet attrs) {\n\t\t\tsuper(c, attrs);\n\t\t\tmPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);\n\t\t}\n\n\t\tpublic LayoutParams(int width, int height) {\n\t\t\tsuper(width, height);\n\t\t}\n\n\t\tpublic LayoutParams(ViewGroup.LayoutParams source) {\n\t\t\tsuper(source);\n\t\t}\n\n\t\tpublic LayoutParams(MarginLayoutParams source) {\n\t\t\tsuper(source);\n\t\t}\n\n\t\t@Override\n\t\tpublic PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {\n\t\t\treturn mPercentLayoutInfo;\n\t\t}\n\n\t\t@Override\n\t\tprotected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {\n\t\t\tPercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);\n\t\t}\n\n\t}\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/reflect/ReflectException.java",
    "content": "package io.virtualapp.abs.reflect;\n\n/**\n * @author Lody\n */\npublic class ReflectException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = 663038727503637969L;\n\n\tpublic ReflectException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/ui/VActivity.java",
    "content": "package io.virtualapp.abs.ui;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.support.annotation.IdRes;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.AppCompatActivity;\n\nimport com.flurry.android.FlurryAgent;\n\nimport org.jdeferred.android.AndroidDeferredManager;\n\nimport io.virtualapp.abs.BaseView;\n\n/**\n * @author Lody\n */\npublic class VActivity extends AppCompatActivity {\n\n    /**\n     * Implement of {@link BaseView#getActivity()}\n     */\n    public Activity getActivity() {\n        return this;\n    }\n\n    /**\n     * Implement of {@link BaseView#getContext()} ()}\n     */\n    public Context getContext() {\n        return this;\n    }\n\n    protected AndroidDeferredManager defer() {\n        return VUiKit.defer();\n    }\n\n    public Fragment findFragmentById(@IdRes int id) {\n        return getSupportFragmentManager().findFragmentById(id);\n    }\n\n    public void replaceFragment(@IdRes int id, Fragment fragment) {\n        getSupportFragmentManager().beginTransaction().replace(id, fragment).commit();\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        FlurryAgent.onStartSession(this);\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        FlurryAgent.onEndSession(this);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/ui/VFragment.java",
    "content": "package io.virtualapp.abs.ui;\n\nimport org.jdeferred.android.AndroidDeferredManager;\n\nimport android.app.Activity;\nimport android.support.v4.app.Fragment;\n\nimport io.virtualapp.abs.BasePresenter;\n\n/**\n * @author Lody\n */\npublic class VFragment<T extends BasePresenter> extends Fragment {\n\n\tprotected T mPresenter;\n\n\tpublic T getPresenter() {\n\t\treturn mPresenter;\n\t}\n\n\tpublic void setPresenter(T presenter) {\n\t\tthis.mPresenter = presenter;\n\t}\n\n\tprotected AndroidDeferredManager defer() {\n\t\treturn VUiKit.defer();\n\t}\n\n\tpublic void finishActivity() {\n\t\tActivity activity = getActivity();\n\t\tif (activity != null) {\n\t\t\tactivity.finish();\n\t\t}\n\t}\n\n\tpublic void destroy() {\n\t\tfinishActivity();\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/abs/ui/VUiKit.java",
    "content": "package io.virtualapp.abs.ui;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.TypedValue;\n\nimport org.jdeferred.android.AndroidDeferredManager;\n\n/**\n * @author Lody\n *         <p>\n *         A set of tools for UI.\n */\npublic class VUiKit {\n\tprivate static final AndroidDeferredManager gDM = new AndroidDeferredManager();\n\tprivate static final Handler gUiHandler = new Handler(Looper.getMainLooper());\n\n\tpublic static AndroidDeferredManager defer() {\n\t\treturn gDM;\n\t}\n\n\tpublic static int dpToPx(Context context, int dp) {\n\t\treturn (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,\n\t\t\t\tcontext.getResources().getDisplayMetrics());\n\t}\n\n\tpublic static void post(Runnable r) {\n\t\tgUiHandler.post(r);\n\t}\n\n\tpublic static void postDelayed(long delay, Runnable r) {\n\t\tgUiHandler.postDelayed(r, delay);\n\t}\n\n\tpublic static void sleep(long time) {\n\t\ttry {\n\t\t\tThread.sleep(time);\n\t\t} catch (InterruptedException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/delegate/MyAppRequestListener.java",
    "content": "package io.virtualapp.delegate;\n\nimport android.content.Context;\nimport android.widget.Toast;\n\nimport com.lody.virtual.client.core.InstallStrategy;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.remote.InstallResult;\n\nimport java.io.IOException;\n\n/**\n * @author Lody\n */\n\npublic class MyAppRequestListener implements VirtualCore.AppRequestListener {\n\n    private final Context context;\n\n    public MyAppRequestListener(Context context) {\n        this.context = context;\n    }\n\n    @Override\n    public void onRequestInstall(String path) {\n        Toast.makeText(context, \"Installing: \" + path, Toast.LENGTH_SHORT).show();\n        InstallResult res = VirtualCore.get().installPackage(path, InstallStrategy.UPDATE_IF_EXIST);\n        if (res.isSuccess) {\n            try {\n                VirtualCore.get().preOpt(res.packageName);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            if (res.isUpdate) {\n                Toast.makeText(context, \"Update: \" + res.packageName + \" success!\", Toast.LENGTH_SHORT).show();\n            } else {\n                Toast.makeText(context, \"Install: \" + res.packageName + \" success!\", Toast.LENGTH_SHORT).show();\n            }\n        } else {\n            Toast.makeText(context, \"Install failed: \" + res.error, Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    @Override\n    public void onRequestUninstall(String pkg) {\n        Toast.makeText(context, \"Uninstall: \" + pkg, Toast.LENGTH_SHORT).show();\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/delegate/MyComponentDelegate.java",
    "content": "package io.virtualapp.delegate;\n\nimport android.app.Activity;\nimport android.content.Intent;\n\nimport com.lody.virtual.client.hook.delegate.ComponentDelegate;\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport java.io.File;\n\n\npublic class MyComponentDelegate implements ComponentDelegate {\n    @Override\n    public void beforeActivityCreate(Activity activity) {\n\n    }\n\n    @Override\n    public void beforeActivityResume(Activity activity) {\n\n    }\n\n    @Override\n    public void beforeActivityPause(Activity activity) {\n\n    }\n\n    @Override\n    public void beforeActivityDestroy(Activity activity) {\n\n    }\n\n    @Override\n    public void afterActivityCreate(Activity activity) {\n\n    }\n\n    @Override\n    public void afterActivityResume(Activity activity) {\n\n    }\n\n    @Override\n    public void afterActivityPause(Activity activity) {\n\n    }\n\n    @Override\n    public void afterActivityDestroy(Activity activity) {\n\n    }\n\n    @Override\n    public void onSendBroadcast(Intent intent) {\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/delegate/MyPhoneInfoDelegate.java",
    "content": "package io.virtualapp.delegate;\n\nimport com.lody.virtual.client.hook.delegate.PhoneInfoDelegate;\n\n\n/**\n * Fake the Device ID.\n */\npublic class MyPhoneInfoDelegate implements PhoneInfoDelegate {\n\n    @Override\n    public String getDeviceId(String oldDeviceId, int userId) {\n        return oldDeviceId;\n    }\n\n    @Override\n    public String getBluetoothAddress(String oldAddress, int userId) {\n        return oldAddress;\n    }\n\n    @Override\n    public String getMacAddress(String oldAddress, int userId) {\n        return oldAddress;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/delegate/MyTaskDescriptionDelegate.java",
    "content": "package io.virtualapp.delegate;\n\nimport android.annotation.TargetApi;\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;\nimport com.lody.virtual.os.VUserManager;\n\n\n/**\n * Patch the task description with the (Virtual) user name\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class MyTaskDescriptionDelegate implements TaskDescriptionDelegate {\n    @Override\n    public ActivityManager.TaskDescription getTaskDescription(ActivityManager.TaskDescription oldTaskDescription) {\n\n        String labelPrefix = \"[\" + VUserManager.get().getUserName() + \"] \";\n\n        if (!oldTaskDescription.getLabel().startsWith(labelPrefix)) {\n            // Is it really necessary?\n            return new ActivityManager.TaskDescription(labelPrefix + oldTaskDescription.getLabel(), oldTaskDescription.getIcon(), oldTaskDescription.getPrimaryColor());\n        } else {\n            return oldTaskDescription;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/effects/ExplosionAnimator.java",
    "content": "package io.virtualapp.effects;\n\nimport java.util.Random;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.view.View;\nimport android.view.animation.AccelerateInterpolator;\nimport android.view.animation.Interpolator;\n\nimport io.virtualapp.VApp;\nimport io.virtualapp.abs.ui.VUiKit;\n\npublic class ExplosionAnimator extends ValueAnimator {\n\n\tprivate static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f);\n\tprivate static final float END_VALUE = 1.4f;\n\tprivate static final float X = VUiKit.dpToPx(VApp.getApp(), 5);\n\tprivate static final float Y = VUiKit.dpToPx(VApp.getApp(), 20);\n\tprivate static final float V = VUiKit.dpToPx(VApp.getApp(), 2);\n\tprivate static final float W = VUiKit.dpToPx(VApp.getApp(), 1);\n\tstatic long DEFAULT_DURATION = 0x450;\n\tprivate Paint mPaint;\n\tprivate Particle[] mParticles;\n\tprivate Rect mBound;\n\tprivate View mContainer;\n\n\tpublic ExplosionAnimator(View container, Bitmap bitmap, Rect bound) {\n\t\tmPaint = new Paint();\n\t\tmBound = new Rect(bound);\n\t\tint partLen = 15;\n\t\tmParticles = new Particle[partLen * partLen];\n\t\tRandom random = new Random(System.currentTimeMillis());\n\t\tint w = bitmap.getWidth() / (partLen + 2);\n\t\tint h = bitmap.getHeight() / (partLen + 2);\n\t\tfor (int i = 0; i < partLen; i++) {\n\t\t\tfor (int j = 0; j < partLen; j++) {\n\t\t\t\tmParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random);\n\t\t\t}\n\t\t}\n\t\tmContainer = container;\n\t\tsetFloatValues(0f, END_VALUE);\n\t\tsetInterpolator(DEFAULT_INTERPOLATOR);\n\t\tsetDuration(DEFAULT_DURATION);\n\t}\n\n\tprivate Particle generateParticle(int color, Random random) {\n\t\tParticle particle = new Particle();\n\t\tparticle.color = color;\n\t\tparticle.radius = V;\n\t\tif (random.nextFloat() < 0.2f) {\n\t\t\tparticle.baseRadius = V + ((X - V) * random.nextFloat());\n\t\t} else {\n\t\t\tparticle.baseRadius = W + ((V - W) * random.nextFloat());\n\t\t}\n\t\tfloat nextFloat = random.nextFloat();\n\t\tparticle.top = mBound.height() * ((0.18f * random.nextFloat()) + 0.2f);\n\t\tparticle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat());\n\t\tparticle.bottom = (mBound.height() * (random.nextFloat() - 0.5f)) * 1.8f;\n\t\tfloat f = nextFloat < 0.2f\n\t\t\t\t? particle.bottom\n\t\t\t\t: nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;\n\t\tparticle.bottom = f;\n\t\tparticle.mag = 4.0f * particle.top / particle.bottom;\n\t\tparticle.neg = (-particle.mag) / particle.bottom;\n\t\tf = mBound.centerX() + (Y * (random.nextFloat() - 0.5f));\n\t\tparticle.baseCx = f;\n\t\tparticle.cx = f;\n\t\tf = mBound.centerY() + (Y * (random.nextFloat() - 0.5f));\n\t\tparticle.baseCy = f;\n\t\tparticle.cy = f;\n\t\tparticle.life = END_VALUE / 10 * random.nextFloat();\n\t\tparticle.overflow = 0.4f * random.nextFloat();\n\t\tparticle.alpha = 1f;\n\t\treturn particle;\n\t}\n\n\tpublic boolean draw(Canvas canvas) {\n\t\tif (!isStarted()) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (Particle particle : mParticles) {\n\t\t\tparticle.advance((float) getAnimatedValue());\n\t\t\tif (particle.alpha > 0f) {\n\t\t\t\tmPaint.setColor(particle.color);\n\t\t\t\tmPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));\n\t\t\t\tcanvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);\n\t\t\t}\n\t\t}\n\t\tmContainer.invalidate();\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\t\tmContainer.invalidate(mBound);\n\t}\n\n\tprivate class Particle {\n\t\tfloat alpha;\n\t\tint color;\n\t\tfloat cx;\n\t\tfloat cy;\n\t\tfloat radius;\n\t\tfloat baseCx;\n\t\tfloat baseCy;\n\t\tfloat baseRadius;\n\t\tfloat top;\n\t\tfloat bottom;\n\t\tfloat mag;\n\t\tfloat neg;\n\t\tfloat life;\n\t\tfloat overflow;\n\n\t\tpublic void advance(float factor) {\n\t\t\tfloat f = 0f;\n\t\t\tfloat normalization = factor / END_VALUE;\n\t\t\tif (normalization < life || normalization > 1f - overflow) {\n\t\t\t\talpha = 0f;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tnormalization = (normalization - life) / (1f - life - overflow);\n\t\t\tfloat f2 = normalization * END_VALUE;\n\t\t\tif (normalization >= 0.7f) {\n\t\t\t\tf = (normalization - 0.7f) / 0.3f;\n\t\t\t}\n\t\t\talpha = 1f - f;\n\t\t\tf = bottom * f2;\n\t\t\tcx = baseCx + f;\n\t\t\tcy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;\n\t\t\tradius = V + (baseRadius - V) * f2;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/effects/ExplosionField.java",
    "content": "package io.virtualapp.effects;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Random;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ValueAnimator;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.ImageView;\n\nimport io.virtualapp.VApp;\nimport io.virtualapp.abs.ui.VUiKit;\n\npublic class ExplosionField extends View {\n\n\tprivate static final Canvas sCanvas = new Canvas();\n\tprivate List<ExplosionAnimator> mExplosions = new ArrayList<>();\n\tprivate int[] mExpandInset = new int[2];\n\n\tpublic ExplosionField(Context context) {\n\t\tsuper(context);\n\t\tinit();\n\t}\n\n\tpublic ExplosionField(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tinit();\n\t}\n\n\tpublic ExplosionField(Context context, AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t\tinit();\n\t}\n\n\tpublic static Bitmap createBitmapFromView(View view) {\n\t\tif (view instanceof ImageView) {\n\t\t\tDrawable drawable = ((ImageView) view).getDrawable();\n\t\t\tif (drawable != null && drawable instanceof BitmapDrawable) {\n\t\t\t\treturn ((BitmapDrawable) drawable).getBitmap();\n\t\t\t}\n\t\t}\n\t\tview.clearFocus();\n\t\tBitmap bitmap = createBitmapSafely(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888, 1);\n\t\tif (bitmap != null) {\n\t\t\tsynchronized (sCanvas) {\n\t\t\t\tCanvas canvas = sCanvas;\n\t\t\t\tcanvas.setBitmap(bitmap);\n\t\t\t\tview.draw(canvas);\n\t\t\t\tcanvas.setBitmap(null);\n\t\t\t}\n\t\t}\n\t\treturn bitmap;\n\t}\n\n\tpublic static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {\n\t\ttry {\n\t\t\treturn Bitmap.createBitmap(width, height, config);\n\t\t} catch (OutOfMemoryError e) {\n\t\t\te.printStackTrace();\n\t\t\tif (retryCount > 0) {\n\t\t\t\tSystem.gc();\n\t\t\t\treturn createBitmapSafely(width, height, config, retryCount - 1);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static ExplosionField attachToWindow(Activity activity) {\n\t\tViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);\n\t\tExplosionField explosionField = new ExplosionField(activity);\n\t\trootView.addView(explosionField,\n\t\t\t\tnew ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n\t\treturn explosionField;\n\t}\n\n\tpublic static ExplosionField attachToWindow(ViewGroup rootView, Activity activity) {\n\t\tExplosionField explosionField = new ExplosionField(activity);\n\t\trootView.addView(explosionField,\n\t\t\t\tnew ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n\t\treturn explosionField;\n\t}\n\n\tprivate void init() {\n\t\tArrays.fill(mExpandInset, VUiKit.dpToPx(VApp.getApp(), 32));\n\t}\n\n\t@Override\n\tprotected void onDraw(Canvas canvas) {\n\t\tsuper.onDraw(canvas);\n\t\tfor (ExplosionAnimator explosion : mExplosions) {\n\t\t\texplosion.draw(canvas);\n\t\t}\n\t}\n\n\tpublic void expandExplosionBound(int dx, int dy) {\n\t\tmExpandInset[0] = dx;\n\t\tmExpandInset[1] = dy;\n\t}\n\n\tpublic void explode(Bitmap bitmap, Rect bound, long startDelay, long duration) {\n\t\tfinal ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap, bound);\n\t\texplosion.addListener(new AnimatorListenerAdapter() {\n\t\t\t@Override\n\t\t\tpublic void onAnimationEnd(Animator animation) {\n\t\t\t\tmExplosions.remove(animation);\n\t\t\t}\n\t\t});\n\t\texplosion.setStartDelay(startDelay);\n\t\texplosion.setDuration(duration);\n\t\tmExplosions.add(explosion);\n\t\texplosion.start();\n\t}\n\n\tpublic void explode(final View view) {\n\t\texplode(view, null);\n\t}\n\n\tpublic void explode(final View view, OnExplodeFinishListener listener) {\n\t\tRect r = new Rect();\n\t\tview.getGlobalVisibleRect(r);\n\t\tint[] location = new int[2];\n\t\tgetLocationOnScreen(location);\n\t\tr.offset(-location[0], -location[1]);\n\t\tr.inset(-mExpandInset[0], -mExpandInset[1]);\n\t\tint startDelay = 100;\n\t\tValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);\n\t\tanimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n\t\t\tRandom random = new Random();\n\n\t\t\t@Override\n\t\t\tpublic void onAnimationUpdate(ValueAnimator animation) {\n\t\t\t\tview.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f);\n\t\t\t\tview.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f);\n\t\t\t}\n\n\t\t});\n\t\tanimator.addListener(new AnimatorListenerAdapter() {\n\t\t\t@Override\n\t\t\tpublic void onAnimationEnd(Animator animation) {\n\t\t\t\tif (listener != null) {\n\t\t\t\t\tlistener.onExplodeFinish(view);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tanimator.start();\n\t\tview.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f).scaleY(0f).alpha(0f).start();\n\t\texplode(createBitmapFromView(view), r, startDelay, ExplosionAnimator.DEFAULT_DURATION);\n\t}\n\n\tpublic void clear() {\n\t\tmExplosions.clear();\n\t\tinvalidate();\n\t}\n\n\tpublic interface OnExplodeFinishListener {\n\t\tvoid onExplodeFinish(View v);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/FlurryROMCollector.java",
    "content": "package io.virtualapp.home;\n\nimport android.hardware.Camera;\nimport android.os.Build;\nimport android.util.Log;\n\nimport com.flurry.android.FlurryAgent;\nimport com.flurry.android.FlurryEventRecordStatus;\nimport com.lody.virtual.client.natives.NativeMethods;\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\npublic class FlurryROMCollector {\n\n    private static final String TAG = FlurryROMCollector.class.getSimpleName();\n\n    public static void startCollect() {\n        Log.d(TAG, \"start collect...\");\n        NativeMethods.init();\n        if (NativeMethods.gCameraNativeSetup == null) {\n            reportCameraNativeSetup();\n        }\n        Log.d(TAG, \"end collect...\");\n    }\n\n\n    private static void reportCameraNativeSetup() {\n        for (Method method : Camera.class.getDeclaredMethods()) {\n            if (\"native_setup\".equals(method.getName())) {\n                FlurryEventRecordStatus status =\n                        FlurryAgent.logEvent(\"camera::native_setup\", createLogContent(\"method_details\", Reflect.getMethodDetails(method)));\n                Log.d(TAG, \"report CNS: \" + status);\n                break;\n            }\n        }\n    }\n\n    private static Map<String, String> createLogContent(String tag, String value) {\n        Map<String, String> content = new HashMap<>(3);\n        addRomInfo(content);\n        content.put(tag, value);\n        return content;\n    }\n\n\n    private static void addRomInfo(Map<String, String> content) {\n        content.put(\"device\", Build.DEVICE);\n        content.put(\"brand\", Build.BRAND);\n        content.put(\"manufacturer\", Build.MANUFACTURER);\n        content.put(\"display\", Build.DISPLAY);\n        content.put(\"model\", Build.MODEL);\n        content.put(\"protect\", Build.PRODUCT);\n        content.put(\"sdk_version\", \"API-\" + Build.VERSION.SDK_INT);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/HomeActivity.java",
    "content": "package io.virtualapp.home;\n\nimport android.animation.Animator;\nimport android.animation.ObjectAnimator;\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AlertDialog;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.PopupMenu;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.StaggeredGridLayoutManager;\nimport android.support.v7.widget.helper.ItemTouchHelper;\nimport android.view.ContextThemeWrapper;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.lody.virtual.GmsSupport;\nimport com.lody.virtual.client.stub.ChooseTypeAndAccountActivity;\nimport com.lody.virtual.os.VUserInfo;\nimport com.lody.virtual.os.VUserManager;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.virtualapp.R;\nimport io.virtualapp.VCommends;\nimport io.virtualapp.abs.nestedadapter.SmartRecyclerAdapter;\nimport io.virtualapp.abs.ui.VActivity;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.adapters.LaunchpadAdapter;\nimport io.virtualapp.home.adapters.decorations.ItemOffsetDecoration;\nimport io.virtualapp.home.models.AddAppButton;\nimport io.virtualapp.home.models.AppData;\nimport io.virtualapp.home.models.AppInfoLite;\nimport io.virtualapp.home.models.EmptyAppData;\nimport io.virtualapp.home.models.PackageAppData;\nimport io.virtualapp.widgets.TwoGearsView;\n\nimport static android.support.v7.widget.helper.ItemTouchHelper.ACTION_STATE_DRAG;\nimport static android.support.v7.widget.helper.ItemTouchHelper.DOWN;\nimport static android.support.v7.widget.helper.ItemTouchHelper.END;\nimport static android.support.v7.widget.helper.ItemTouchHelper.LEFT;\nimport static android.support.v7.widget.helper.ItemTouchHelper.RIGHT;\nimport static android.support.v7.widget.helper.ItemTouchHelper.START;\nimport static android.support.v7.widget.helper.ItemTouchHelper.UP;\n\n/**\n * @author Lody\n */\npublic class HomeActivity extends VActivity implements HomeContract.HomeView {\n\n    private static final String TAG = HomeActivity.class.getSimpleName();\n\n    private HomeContract.HomePresenter mPresenter;\n    private TwoGearsView mLoadingView;\n    private RecyclerView mLauncherView;\n    private View mMenuView;\n    private PopupMenu mPopupMenu;\n    private View mBottomArea;\n    private View mCreateShortcutBox;\n    private TextView mCreateShortcutTextView;\n    private View mDeleteAppBox;\n    private TextView mDeleteAppTextView;\n    private LaunchpadAdapter mLaunchpadAdapter;\n    private Handler mUiHandler;\n\n\n    public static void goHome(Context context) {\n        Intent intent = new Intent(context, HomeActivity.class);\n        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        context.startActivity(intent);\n    }\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        overridePendingTransition(0, 0);\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_home);\n        mUiHandler = new Handler(Looper.getMainLooper());\n        bindViews();\n        initLaunchpad();\n        initMenu();\n        new HomePresenterImpl(this).start();\n    }\n\n    private void initMenu() {\n        mPopupMenu = new PopupMenu(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Light), mMenuView);\n        Menu menu = mPopupMenu.getMenu();\n        setIconEnable(menu, true);\n        menu.add(\"Accounts\").setIcon(R.drawable.ic_account).setOnMenuItemClickListener(item -> {\n            List<VUserInfo> users = VUserManager.get().getUsers();\n            List<String> names = new ArrayList<>(users.size());\n            for (VUserInfo info : users) {\n                names.add(info.name);\n            }\n            CharSequence[] items = new CharSequence[names.size()];\n            for (int i = 0; i < names.size(); i++) {\n                items[i] = names.get(i);\n            }\n            new AlertDialog.Builder(this)\n                    .setTitle(\"Please select an user\")\n                    .setItems(items, (dialog, which) -> {\n                        VUserInfo info = users.get(which);\n                        Intent intent = new Intent(this, ChooseTypeAndAccountActivity.class);\n                        intent.putExtra(ChooseTypeAndAccountActivity.KEY_USER_ID, info.id);\n                        startActivity(intent);\n                    }).show();\n            return false;\n        });\n        menu.add(\"Virtual Storage\").setIcon(R.drawable.ic_vs).setOnMenuItemClickListener(item -> {\n            Toast.makeText(this, \"The coming\", Toast.LENGTH_SHORT).show();\n            return false;\n        });\n        menu.add(\"Notification\").setIcon(R.drawable.ic_notification).setOnMenuItemClickListener(item -> {\n            Toast.makeText(this, \"The coming\", Toast.LENGTH_SHORT).show();\n            return false;\n        });\n        menu.add(\"Settings\").setIcon(R.drawable.ic_settings).setOnMenuItemClickListener(item -> {\n            Toast.makeText(this, \"The coming\", Toast.LENGTH_SHORT).show();\n            return false;\n        });\n        mMenuView.setOnClickListener(v -> mPopupMenu.show());\n    }\n\n    private static void setIconEnable(Menu menu, boolean enable) {\n        try {\n            @SuppressLint(\"PrivateApi\")\n            Method m = menu.getClass().getDeclaredMethod(\"setOptionalIconsVisible\", boolean.class);\n            m.setAccessible(true);\n            m.invoke(menu, enable);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void bindViews() {\n        mLoadingView = (TwoGearsView) findViewById(R.id.pb_loading_app);\n        mLauncherView = (RecyclerView) findViewById(R.id.home_launcher);\n        mMenuView = findViewById(R.id.home_menu);\n        mBottomArea = findViewById(R.id.bottom_area);\n        mCreateShortcutBox = findViewById(R.id.create_shortcut_area);\n        mCreateShortcutTextView = (TextView) findViewById(R.id.create_shortcut_text);\n        mDeleteAppBox = findViewById(R.id.delete_app_area);\n        mDeleteAppTextView = (TextView) findViewById(R.id.delete_app_text);\n    }\n\n    private void initLaunchpad() {\n        mLauncherView.setHasFixedSize(true);\n        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL);\n        mLauncherView.setLayoutManager(layoutManager);\n        mLaunchpadAdapter = new LaunchpadAdapter(this);\n        SmartRecyclerAdapter wrap = new SmartRecyclerAdapter(mLaunchpadAdapter);\n        View footer = new View(this);\n        footer.setLayoutParams(new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, VUiKit.dpToPx(this, 60)));\n        wrap.setFooterView(footer);\n        mLauncherView.setAdapter(wrap);\n        mLauncherView.addItemDecoration(new ItemOffsetDecoration(this, R.dimen.desktop_divider));\n        ItemTouchHelper touchHelper = new ItemTouchHelper(new LauncherTouchCallback());\n        touchHelper.attachToRecyclerView(mLauncherView);\n        mLaunchpadAdapter.setAppClickListener((pos, data) -> {\n            if (!data.isLoading()) {\n                if (data instanceof AddAppButton) {\n                    onAddAppButtonClick();\n                }\n                mLaunchpadAdapter.notifyItemChanged(pos);\n                mPresenter.launchApp(data);\n            }\n        });\n    }\n\n    private void onAddAppButtonClick() {\n        ListAppActivity.gotoListApp(this);\n    }\n\n    private void deleteApp(int position) {\n        AppData data = mLaunchpadAdapter.getList().get(position);\n        new AlertDialog.Builder(this)\n                .setTitle(\"Delete app\")\n                .setMessage(\"Do you want to delete \" + data.getName() + \"?\")\n                .setPositiveButton(android.R.string.yes, (dialog, which) -> {\n                    mPresenter.deleteApp(data);\n                })\n                .setNegativeButton(android.R.string.no, null)\n                .show();\n    }\n\n    private void createShortcut(int position) {\n        AppData model = mLaunchpadAdapter.getList().get(position);\n        if (model instanceof PackageAppData) {\n            mPresenter.createShortcut(model);\n        }\n    }\n\n    @Override\n    public void setPresenter(HomeContract.HomePresenter presenter) {\n        mPresenter = presenter;\n    }\n\n    @Override\n    public void showBottomAction() {\n        mBottomArea.setTranslationY(mBottomArea.getHeight());\n        mBottomArea.setVisibility(View.VISIBLE);\n        mBottomArea.animate().translationY(0).setDuration(500L).start();\n    }\n\n    @Override\n    public void hideBottomAction() {\n        mBottomArea.setTranslationY(0);\n        ObjectAnimator transAnim = ObjectAnimator.ofFloat(mBottomArea, \"translationY\", 0, mBottomArea.getHeight());\n        transAnim.addListener(new Animator.AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animator) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animator) {\n                mBottomArea.setVisibility(View.GONE);\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animator) {\n                mBottomArea.setVisibility(View.GONE);\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animator) {\n\n            }\n        });\n        transAnim.setDuration(500L);\n        transAnim.start();\n    }\n\n    @Override\n    public void showLoading() {\n        mLoadingView.setVisibility(View.VISIBLE);\n        mLoadingView.startAnim();\n    }\n\n    @Override\n    public void hideLoading() {\n        mLoadingView.setVisibility(View.GONE);\n        mLoadingView.stopAnim();\n    }\n\n    @Override\n    public void loadFinish(List<AppData> list) {\n        list.add(new AddAppButton(this));\n        mLaunchpadAdapter.setList(list);\n        hideLoading();\n    }\n\n    @Override\n    public void loadError(Throwable err) {\n        err.printStackTrace();\n        hideLoading();\n    }\n\n    @Override\n    public void showGuide() {\n\n    }\n\n    @Override\n    public void addAppToLauncher(AppData model) {\n        List<AppData> dataList = mLaunchpadAdapter.getList();\n        boolean replaced = false;\n        for (int i = 0; i < dataList.size(); i++) {\n            AppData data = dataList.get(i);\n            if (data instanceof EmptyAppData) {\n                mLaunchpadAdapter.replace(i, model);\n                replaced = true;\n                break;\n            }\n        }\n        if (!replaced) {\n            mLaunchpadAdapter.add(model);\n            mLauncherView.smoothScrollToPosition(mLaunchpadAdapter.getItemCount() - 1);\n        }\n    }\n\n\n    @Override\n    public void removeAppToLauncher(AppData model) {\n        mLaunchpadAdapter.remove(model);\n    }\n\n    @Override\n    public void refreshLauncherItem(AppData model) {\n        mLaunchpadAdapter.refresh(model);\n    }\n\n    @Override\n    public void askInstallGms() {\n        new AlertDialog.Builder(this)\n                .setTitle(\"Hi\")\n                .setMessage(\"We found that your device has been installed the Google service, whether you need to install them?\")\n                .setPositiveButton(android.R.string.ok, (dialog, which) -> {\n                    defer().when(() -> {\n                        GmsSupport.installGApps(0);\n                    }).done((res) -> {\n                        mPresenter.dataChanged();\n                    });\n                })\n                .setNegativeButton(android.R.string.cancel, (dialog, which) ->\n                        Toast.makeText(HomeActivity.this, \"You can also find it in the Settings~\", Toast.LENGTH_LONG).show())\n                .setCancelable(false)\n                .show();\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (resultCode == RESULT_OK && data != null) {\n            List<AppInfoLite> appList = data.getParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST);\n            if (appList != null) {\n                for (AppInfoLite info : appList) {\n                    mPresenter.addApp(info);\n                }\n            }\n        }\n    }\n\n    private class LauncherTouchCallback extends ItemTouchHelper.SimpleCallback {\n\n        int[] location = new int[2];\n        boolean upAtDeleteAppArea;\n        boolean upAtCreateShortcutArea;\n        RecyclerView.ViewHolder dragHolder;\n\n        LauncherTouchCallback() {\n            super(UP | DOWN | LEFT | RIGHT | START | END, 0);\n        }\n\n        @Override\n        public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, int totalSize, long msSinceStartScroll) {\n            return 0;\n        }\n\n        @Override\n        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {\n            try {\n                AppData data = mLaunchpadAdapter.getList().get(viewHolder.getAdapterPosition());\n                if (!data.canReorder()) {\n                    return makeMovementFlags(0, 0);\n                }\n            } catch (IndexOutOfBoundsException e) {\n                e.printStackTrace();\n            }\n            return super.getMovementFlags(recyclerView, viewHolder);\n        }\n\n        @Override\n        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {\n            int pos = viewHolder.getAdapterPosition();\n            int targetPos = target.getAdapterPosition();\n            mLaunchpadAdapter.moveItem(pos, targetPos);\n            return true;\n        }\n\n        @Override\n        public boolean isLongPressDragEnabled() {\n            return true;\n        }\n\n        @Override\n        public boolean isItemViewSwipeEnabled() {\n            return false;\n        }\n\n        @Override\n        public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {\n            if (viewHolder instanceof LaunchpadAdapter.ViewHolder) {\n                if (actionState == ACTION_STATE_DRAG) {\n                    if (dragHolder != viewHolder) {\n                        dragHolder = viewHolder;\n                        viewHolder.itemView.setScaleX(1.2f);\n                        viewHolder.itemView.setScaleY(1.2f);\n                        if (mBottomArea.getVisibility() == View.GONE) {\n                            showBottomAction();\n                        }\n                    }\n                }\n            }\n            super.onSelectedChanged(viewHolder, actionState);\n        }\n\n        @Override\n        public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {\n            if (upAtCreateShortcutArea || upAtDeleteAppArea) {\n                return false;\n            }\n            try {\n                AppData data = mLaunchpadAdapter.getList().get(target.getAdapterPosition());\n                return data.canReorder();\n            } catch (IndexOutOfBoundsException e) {\n                e.printStackTrace();\n            }\n            return false;\n        }\n\n        @Override\n        public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {\n            if (viewHolder instanceof LaunchpadAdapter.ViewHolder) {\n                LaunchpadAdapter.ViewHolder holder = (LaunchpadAdapter.ViewHolder) viewHolder;\n                viewHolder.itemView.setScaleX(1f);\n                viewHolder.itemView.setScaleY(1f);\n                viewHolder.itemView.setBackgroundColor(holder.color);\n            }\n            super.clearView(recyclerView, viewHolder);\n            if (dragHolder == viewHolder) {\n                if (mBottomArea.getVisibility() == View.VISIBLE) {\n                    mUiHandler.postDelayed(HomeActivity.this::hideBottomAction, 200L);\n                    if (upAtCreateShortcutArea) {\n                        createShortcut(viewHolder.getAdapterPosition());\n                    } else if (upAtDeleteAppArea) {\n                        deleteApp(viewHolder.getAdapterPosition());\n                    }\n                }\n                dragHolder = null;\n            }\n        }\n\n\n        @Override\n        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {\n        }\n\n        @Override\n        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {\n            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);\n            if (actionState != ACTION_STATE_DRAG || !isCurrentlyActive) {\n                return;\n            }\n            View itemView = viewHolder.itemView;\n            itemView.getLocationInWindow(location);\n            int x = (int) (location[0] + dX);\n            int y = (int) (location[1] + dY);\n\n            mBottomArea.getLocationInWindow(location);\n            int baseLine = location[1] - mBottomArea.getHeight();\n            if (y >= baseLine) {\n                mDeleteAppBox.getLocationInWindow(location);\n                int deleteAppAreaStartX = location[0];\n                if (x < deleteAppAreaStartX) {\n                    upAtCreateShortcutArea = true;\n                    upAtDeleteAppArea = false;\n                    mCreateShortcutTextView.setTextColor(Color.parseColor(\"#0099cc\"));\n                    mDeleteAppTextView.setTextColor(Color.WHITE);\n                } else {\n                    upAtDeleteAppArea = true;\n                    upAtCreateShortcutArea = false;\n                    mDeleteAppTextView.setTextColor(Color.parseColor(\"#0099cc\"));\n                    mCreateShortcutTextView.setTextColor(Color.WHITE);\n                }\n            } else {\n                upAtCreateShortcutArea = false;\n                upAtDeleteAppArea = false;\n                mDeleteAppTextView.setTextColor(Color.WHITE);\n                mCreateShortcutTextView.setTextColor(Color.WHITE);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/HomeContract.java",
    "content": "package io.virtualapp.home;\n\n\nimport java.util.List;\n\nimport io.virtualapp.abs.BasePresenter;\nimport io.virtualapp.abs.BaseView;\nimport io.virtualapp.home.models.AppData;\nimport io.virtualapp.home.models.AppInfoLite;\n\n/**\n * @author Lody\n */\n/* package */ class HomeContract {\n\n\t/* package */ interface HomeView extends BaseView<HomePresenter> {\n\n        void showBottomAction();\n\n        void hideBottomAction();\n\n\t\tvoid showLoading();\n\n\t\tvoid hideLoading();\n\n\t\tvoid loadFinish(List<AppData> appModels);\n\n\t\tvoid loadError(Throwable err);\n\n\t\tvoid showGuide();\n\n\t\tvoid addAppToLauncher(AppData model);\n\n        void removeAppToLauncher(AppData model);\n\n\t\tvoid refreshLauncherItem(AppData model);\n\n\t\tvoid askInstallGms();\n\t}\n\n\t/* package */ interface HomePresenter extends BasePresenter {\n\n\t\tvoid launchApp(AppData data);\n\n\t\tvoid dataChanged();\n\n\t\tvoid addApp(AppInfoLite info);\n\n\t\tvoid deleteApp(AppData data);\n\n        void createShortcut(AppData data);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/HomePresenterImpl.java",
    "content": "package io.virtualapp.home;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\n\nimport com.google.android.gms.ads.AdListener;\nimport com.google.android.gms.ads.AdRequest;\nimport com.google.android.gms.ads.InterstitialAd;\nimport com.lody.virtual.GmsSupport;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.os.VUserInfo;\nimport com.lody.virtual.os.VUserManager;\nimport com.lody.virtual.remote.InstallResult;\nimport com.lody.virtual.remote.InstalledAppInfo;\n\nimport java.io.IOException;\n\nimport io.virtualapp.VCommends;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.ads.AdScheduler;\nimport io.virtualapp.home.models.AppData;\nimport io.virtualapp.home.models.AppInfoLite;\nimport io.virtualapp.home.models.MultiplePackageAppData;\nimport io.virtualapp.home.models.PackageAppData;\nimport io.virtualapp.home.repo.AppRepository;\nimport io.virtualapp.home.repo.PackageAppDataStorage;\nimport jonathanfinerty.once.Once;\n\n/**\n * @author Lody\n */\nclass HomePresenterImpl implements HomeContract.HomePresenter {\n\n    private HomeContract.HomeView mView;\n    private Activity mActivity;\n    private AppRepository mRepo;\n    private InterstitialAd mInterstitialAd;\n    private AdScheduler mScheduler = new AdScheduler(10000L);\n\n    private AppData mTempAppData;\n\n\n    HomePresenterImpl(HomeContract.HomeView view) {\n        mView = view;\n        mActivity = view.getActivity();\n        mRepo = new AppRepository(mActivity);\n        mView.setPresenter(this);\n        mInterstitialAd = new InterstitialAd(mActivity);\n        mInterstitialAd.setAdUnitId(\"ca-app-pub-1609791120068944/6903216910\");\n        mInterstitialAd.loadAd(new AdRequest.Builder().build());\n        mInterstitialAd.setAdListener(new AdListener() {\n            @Override\n            public void onAdClosed() {\n                if (mTempAppData != null) {\n                    launchApp(mTempAppData);\n                    mTempAppData = null;\n                }\n                mInterstitialAd.loadAd(new AdRequest.Builder().build());\n            }\n        });\n    }\n\n    @Override\n    public void start() {\n        dataChanged();\n        if (!Once.beenDone(VCommends.TAG_SHOW_ADD_APP_GUIDE)) {\n            mView.showGuide();\n            Once.markDone(VCommends.TAG_SHOW_ADD_APP_GUIDE);\n        }\n        if (!Once.beenDone(VCommends.TAG_ASK_INSTALL_GMS) && GmsSupport.isOutsideGoogleFrameworkExist()) {\n            mView.askInstallGms();\n            Once.markDone(VCommends.TAG_ASK_INSTALL_GMS);\n        }\n    }\n\n    @Override\n    public void launchApp(AppData data) {\n        if (mInterstitialAd.isLoaded() && mScheduler.shouldShowAd()) {\n            mTempAppData = data;\n            mInterstitialAd.show();\n            mScheduler.adShowed();\n        } else {\n            launchAppNoAd(data);\n        }\n    }\n\n    public void launchAppNoAd(AppData data) {\n        try {\n            if (data instanceof PackageAppData) {\n                PackageAppData appData = (PackageAppData) data;\n                appData.isFirstOpen = false;\n                LoadingActivity.launch(mActivity, appData.packageName, 0);\n            } else if (data instanceof MultiplePackageAppData) {\n                MultiplePackageAppData multipleData = (MultiplePackageAppData) data;\n                multipleData.isFirstOpen = false;\n                LoadingActivity.launch(mActivity, multipleData.appInfo.packageName, ((MultiplePackageAppData) data).userId);\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void dataChanged() {\n        mView.showLoading();\n        mRepo.getVirtualApps().done(mView::loadFinish).fail(mView::loadError);\n    }\n\n\n    @Override\n    public void addApp(AppInfoLite info) {\n        class AddResult {\n            private PackageAppData appData;\n            private int userId;\n            private boolean justEnableHidden;\n        }\n        AddResult addResult = new AddResult();\n        VUiKit.defer().when(() -> {\n            InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);\n            addResult.justEnableHidden = installedAppInfo != null;\n            if (addResult.justEnableHidden) {\n                int[] userIds = installedAppInfo.getInstalledUsers();\n                int nextUserId = userIds.length;\n                /*\n                  Input : userIds = {0, 1, 3}\n                  Output: nextUserId = 2\n                 */\n                for (int i = 0; i < userIds.length; i++) {\n                    if (userIds[i] != i) {\n                        nextUserId = i;\n                        break;\n                    }\n                }\n                addResult.userId = nextUserId;\n\n                if (VUserManager.get().getUserInfo(nextUserId) == null) {\n                    // user not exist, create it automatically.\n                    String nextUserName = \"Space \" + (nextUserId + 1);\n                    VUserInfo newUserInfo = VUserManager.get().createUser(nextUserName, VUserInfo.FLAG_ADMIN);\n                    if (newUserInfo == null) {\n                        throw new IllegalStateException();\n                    }\n                }\n                boolean success = VirtualCore.get().installPackageAsUser(nextUserId, info.packageName);\n                if (!success) {\n                    throw new IllegalStateException();\n                }\n            } else {\n                InstallResult res = mRepo.addVirtualApp(info);\n                if (!res.isSuccess) {\n                    throw new IllegalStateException();\n                }\n            }\n        }).then((res) -> {\n            addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);\n        }).done(res -> {\n            boolean multipleVersion = addResult.justEnableHidden && addResult.userId != 0;\n            if (!multipleVersion) {\n                PackageAppData data = addResult.appData;\n                data.isLoading = true;\n                mView.addAppToLauncher(data);\n                handleOptApp(data, info.packageName, true);\n            } else {\n                MultiplePackageAppData data = new MultiplePackageAppData(addResult.appData, addResult.userId);\n                data.isLoading = true;\n                mView.addAppToLauncher(data);\n                handleOptApp(data, info.packageName, false);\n            }\n        });\n    }\n\n\n    private void handleOptApp(AppData data, String packageName, boolean needOpt) {\n        VUiKit.defer().when(() -> {\n            long time = System.currentTimeMillis();\n            if (needOpt) {\n                try {\n                    VirtualCore.get().preOpt(packageName);\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n            time = System.currentTimeMillis() - time;\n            if (time < 1500L) {\n                try {\n                    Thread.sleep(1500L - time);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }).done((res) -> {\n            if (data instanceof PackageAppData) {\n                ((PackageAppData) data).isLoading = false;\n                ((PackageAppData) data).isFirstOpen = true;\n            } else if (data instanceof MultiplePackageAppData) {\n                ((MultiplePackageAppData) data).isLoading = false;\n                ((MultiplePackageAppData) data).isFirstOpen = true;\n            }\n            mView.refreshLauncherItem(data);\n        });\n    }\n\n    @Override\n    public void deleteApp(AppData data) {\n        try {\n            mView.removeAppToLauncher(data);\n            if (data instanceof PackageAppData) {\n                mRepo.removeVirtualApp(((PackageAppData) data).packageName, 0);\n            } else {\n                MultiplePackageAppData appData = (MultiplePackageAppData) data;\n                mRepo.removeVirtualApp(appData.appInfo.packageName, appData.userId);\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void createShortcut(AppData data) {\n        VirtualCore.OnEmitShortcutListener listener = new VirtualCore.OnEmitShortcutListener() {\n            @Override\n            public Bitmap getIcon(Bitmap originIcon) {\n                return originIcon;\n            }\n\n            @Override\n            public String getName(String originName) {\n                return originName + \"(VA)\";\n            }\n        };\n        if (data instanceof PackageAppData) {\n            VirtualCore.get().createShortcut(0, ((PackageAppData) data).packageName, listener);\n        } else if (data instanceof MultiplePackageAppData) {\n            MultiplePackageAppData appData = (MultiplePackageAppData) data;\n            VirtualCore.get().createShortcut(appData.userId, appData.appInfo.packageName, listener);\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/ListAppActivity.java",
    "content": "package io.virtualapp.home;\n\nimport android.Manifest;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.graphics.drawable.ColorDrawable;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.design.widget.TabLayout;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.widget.Toolbar;\nimport android.view.MenuItem;\n\nimport io.virtualapp.R;\nimport io.virtualapp.VCommends;\nimport io.virtualapp.abs.ui.VActivity;\nimport io.virtualapp.home.adapters.AppPagerAdapter;\n\n/**\n * @author Lody\n */\npublic class ListAppActivity extends VActivity {\n\n    private Toolbar mToolBar;\n    private TabLayout mTabLayout;\n    private ViewPager mViewPager;\n\n    public static void gotoListApp(Activity activity) {\n        Intent intent = new Intent(activity, ListAppActivity.class);\n        activity.startActivityForResult(intent, VCommends.REQUEST_SELECT_APP);\n    }\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getWindow().setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.colorPrimaryDark)));\n        setContentView(R.layout.activity_clone_app);\n        mToolBar = (Toolbar) findViewById(R.id.clone_app_tool_bar);\n        mTabLayout = (TabLayout) mToolBar.findViewById(R.id.clone_app_tab_layout);\n        mViewPager = (ViewPager) findViewById(R.id.clone_app_view_pager);\n        setupToolBar();\n        mViewPager.setAdapter(new AppPagerAdapter(getSupportFragmentManager()));\n        mTabLayout.setupWithViewPager(mViewPager);\n        // Request permission to access external storage\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {\n                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0);\n            }\n        }\n    }\n\n    private void setupToolBar() {\n        setSupportActionBar(mToolBar);\n        ActionBar actionBar = getSupportActionBar();\n        if (actionBar != null) {\n            actionBar.setDisplayHomeAsUpEnabled(true);\n        }\n    }\n\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        if (item.getItemId() == android.R.id.home) {\n            finish();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        for (int result : grantResults) {\n            if (result == PackageManager.PERMISSION_GRANTED) {\n                mViewPager.setAdapter(new AppPagerAdapter(getSupportFragmentManager()));\n                break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/ListAppContract.java",
    "content": "package io.virtualapp.home;\n\nimport java.util.List;\n\nimport io.virtualapp.abs.BasePresenter;\nimport io.virtualapp.abs.BaseView;\nimport io.virtualapp.home.models.AppInfo;\n\n/**\n * @author Lody\n * @version 1.0\n */\n/*package*/ class ListAppContract {\n    interface ListAppView extends BaseView<ListAppPresenter> {\n\n        void startLoading();\n\n        void loadFinish(List<AppInfo> infoList);\n    }\n\n    interface ListAppPresenter extends BasePresenter {\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/ListAppFragment.java",
    "content": "package io.virtualapp.home;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.StaggeredGridLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.ProgressBar;\nimport android.widget.Toast;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport io.virtualapp.R;\nimport io.virtualapp.VCommends;\nimport io.virtualapp.abs.ui.VFragment;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.adapters.CloneAppListAdapter;\nimport io.virtualapp.home.adapters.decorations.ItemOffsetDecoration;\nimport io.virtualapp.home.models.AppInfo;\nimport io.virtualapp.home.models.AppInfoLite;\nimport io.virtualapp.widgets.DragSelectRecyclerView;\n\n/**\n * @author Lody\n */\npublic class ListAppFragment extends VFragment<ListAppContract.ListAppPresenter> implements ListAppContract.ListAppView {\n    private static final String KEY_SELECT_FROM = \"key_select_from\";\n    private DragSelectRecyclerView mRecyclerView;\n    private ProgressBar mProgressBar;\n    private Button mInstallButton;\n    private CloneAppListAdapter mAdapter;\n\n    public static ListAppFragment newInstance(File selectFrom) {\n        Bundle args = new Bundle();\n        if (selectFrom != null)\n            args.putString(KEY_SELECT_FROM, selectFrom.getPath());\n        ListAppFragment fragment = new ListAppFragment();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    private File getSelectFrom() {\n        Bundle bundle = getArguments();\n        if (bundle != null) {\n            String selectFrom = bundle.getString(KEY_SELECT_FROM);\n            if (selectFrom != null) {\n                return new File(selectFrom);\n            }\n        }\n        return null;\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        return inflater.inflate(R.layout.fragment_list_app, null);\n    }\n\n    @Override\n    public void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        mAdapter.saveInstanceState(outState);\n    }\n\n    @Override\n    public void onViewCreated(View view, Bundle savedInstanceState) {\n        mRecyclerView = (DragSelectRecyclerView) view.findViewById(R.id.select_app_recycler_view);\n        mProgressBar = (ProgressBar) view.findViewById(R.id.select_app_progress_bar);\n        mInstallButton = (Button) view.findViewById(R.id.select_app_install_btn);\n        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL));\n        mRecyclerView.addItemDecoration(new ItemOffsetDecoration(VUiKit.dpToPx(getContext(), 2)));\n        mAdapter = new CloneAppListAdapter(getActivity());\n        mRecyclerView.setAdapter(mAdapter);\n        mAdapter.setOnItemClickListener(new CloneAppListAdapter.ItemEventListener() {\n            @Override\n            public void onItemClick(AppInfo info, int position) {\n                int count = mAdapter.getSelectedCount();\n                if (!mAdapter.isIndexSelected(position)) {\n                    if (count >= 9) {\n                        Toast.makeText(getContext(), R.string.install_too_much_once_time, Toast.LENGTH_SHORT).show();\n                        return;\n                    }\n                }\n                mAdapter.toggleSelected(position);\n            }\n\n            @Override\n            public boolean isSelectable(int position) {\n                return mAdapter.isIndexSelected(position) || mAdapter.getSelectedCount() < 9;\n            }\n        });\n        mAdapter.setSelectionListener(count -> {\n            mInstallButton.setEnabled(count > 0);\n            mInstallButton.setText(String.format(Locale.ENGLISH, getResources().getString(R.string.install_d), count));\n        });\n        mInstallButton.setOnClickListener(v -> {\n            Integer[] selectedIndices = mAdapter.getSelectedIndices();\n            ArrayList<AppInfoLite> dataList = new ArrayList<AppInfoLite>(selectedIndices.length);\n            for (int index : selectedIndices) {\n                AppInfo info = mAdapter.getItem(index);\n                dataList.add(new AppInfoLite(info.packageName, info.path, info.fastOpen));\n            }\n            Intent data = new Intent();\n            data.putParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST, dataList);\n            getActivity().setResult(Activity.RESULT_OK, data);\n            getActivity().finish();\n        });\n        new ListAppPresenterImpl(getActivity(), this, getSelectFrom()).start();\n    }\n\n    @Override\n    public void startLoading() {\n        mProgressBar.setVisibility(View.VISIBLE);\n        mRecyclerView.setVisibility(View.GONE);\n    }\n\n    @Override\n    public void loadFinish(List<AppInfo> infoList) {\n        mAdapter.setList(infoList);\n        mRecyclerView.setDragSelectActive(true, 0);\n        mAdapter.setSelected(0, false);\n        mProgressBar.setVisibility(View.GONE);\n        mRecyclerView.setVisibility(View.VISIBLE);\n    }\n\n    @Override\n    public void setPresenter(ListAppContract.ListAppPresenter presenter) {\n        this.mPresenter = presenter;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/ListAppPresenterImpl.java",
    "content": "package io.virtualapp.home;\n\nimport android.app.Activity;\nimport android.content.Intent;\n\nimport java.io.File;\n\nimport io.virtualapp.VCommends;\nimport io.virtualapp.home.repo.AppDataSource;\nimport io.virtualapp.home.models.PackageAppData;\nimport io.virtualapp.home.repo.AppRepository;\n\n/**\n * @author Lody\n */\nclass ListAppPresenterImpl implements ListAppContract.ListAppPresenter {\n\n\tprivate Activity mActivity;\n\tprivate ListAppContract.ListAppView mView;\n\tprivate AppDataSource mRepository;\n\n\tprivate File from;\n\n\tListAppPresenterImpl(Activity activity, ListAppContract.ListAppView view, File fromWhere) {\n\t\tmActivity = activity;\n\t\tmView = view;\n\t\tmRepository = new AppRepository(activity);\n\t\tmView.setPresenter(this);\n\t\tthis.from = fromWhere;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tmView.setPresenter(this);\n\t\tmView.startLoading();\n\t\tif (from == null)\n\t\t\tmRepository.getInstalledApps(mActivity).done(mView::loadFinish);\n\t\telse\n\t\t\tmRepository.getStorageApps(mActivity, from).done(mView::loadFinish);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/LoadingActivity.java",
    "content": "package io.virtualapp.home;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.RemoteException;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.VActivityManager;\n\nimport java.util.Locale;\n\nimport io.virtualapp.R;\nimport io.virtualapp.abs.ui.VActivity;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.models.PackageAppData;\nimport io.virtualapp.home.repo.PackageAppDataStorage;\nimport io.virtualapp.widgets.EatBeansView;\n\n/**\n * @author Lody\n */\n\npublic class LoadingActivity extends VActivity {\n\n    private static final String PKG_NAME_ARGUMENT = \"MODEL_ARGUMENT\";\n    private static final String KEY_INTENT = \"KEY_INTENT\";\n    private static final String KEY_USER = \"KEY_USER\";\n    private PackageAppData appModel;\n    private EatBeansView loadingView;\n\n    public static void launch(Context context, String packageName, int userId) {\n        Intent intent = VirtualCore.get().getLaunchIntent(packageName, userId);\n        if (intent != null) {\n            Intent loadingPageIntent = new Intent(context, LoadingActivity.class);\n            loadingPageIntent.putExtra(PKG_NAME_ARGUMENT, packageName);\n            loadingPageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            loadingPageIntent.putExtra(KEY_INTENT, intent);\n            loadingPageIntent.putExtra(KEY_USER, userId);\n            context.startActivity(loadingPageIntent);\n        }\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_loading);\n        loadingView = (EatBeansView) findViewById(R.id.loading_anim);\n        int userId = getIntent().getIntExtra(KEY_USER, -1);\n        String pkg = getIntent().getStringExtra(PKG_NAME_ARGUMENT);\n        appModel = PackageAppDataStorage.get().acquire(pkg);\n        ImageView iconView = (ImageView) findViewById(R.id.app_icon);\n        iconView.setImageDrawable(appModel.icon);\n        TextView nameView = (TextView) findViewById(R.id.app_name);\n        nameView.setText(String.format(Locale.ENGLISH, \"Opening %s...\", appModel.name));\n        Intent intent = getIntent().getParcelableExtra(KEY_INTENT);\n        if (intent == null) {\n            return;\n        }\n        VirtualCore.get().setUiCallback(intent, mUiCallback);\n        VUiKit.defer().when(() -> {\n            long startTime = System.currentTimeMillis();\n            if (!appModel.fastOpen) {\n                try {\n                    VirtualCore.get().preOpt(appModel.packageName);\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n            long spend = System.currentTimeMillis() - startTime;\n            if (spend < 500) {\n                try {\n                    Thread.sleep(500 - spend);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n            VActivityManager.get().startActivity(intent, userId);\n        });\n\n    }\n\n    private final VirtualCore.UiCallback mUiCallback = new VirtualCore.UiCallback() {\n\n        @Override\n        public void onAppOpened(String packageName, int userId) throws RemoteException {\n            finish();\n        }\n    };\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        loadingView.startAnim();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        loadingView.stopAnim();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/adapters/AppPagerAdapter.java",
    "content": "package io.virtualapp.home.adapters;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.storage.StorageManager;\nimport android.os.storage.StorageVolume;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentPagerAdapter;\n\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.virtualapp.R;\nimport io.virtualapp.VApp;\nimport io.virtualapp.home.ListAppFragment;\n\n/**\n * @author Lody\n */\npublic class AppPagerAdapter extends FragmentPagerAdapter {\n    private List<String> titles = new ArrayList<>();\n    private List<File> dirs = new ArrayList<>();\n\n    public AppPagerAdapter(FragmentManager fm) {\n        super(fm);\n        titles.add(VApp.getApp().getResources().getString(R.string.clone_apps));\n        dirs.add(null);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            Context ctx = VApp.getApp();\n            StorageManager storage = (StorageManager) ctx.getSystemService(Context.STORAGE_SERVICE);\n            for (StorageVolume volume : storage.getStorageVolumes()) {\n                //Why the fuck are getPathFile and getUserLabel hidden?!\n                //StorageVolume is kinda useless without those...\n                File dir = Reflect.on(volume).call(\"getPathFile\").get();\n                String label = Reflect.on(volume).call(\"getUserLabel\").get();\n                if (dir.listFiles() != null) {\n                    titles.add(label);\n                    dirs.add(dir);\n                }\n            }\n        } else {\n            // Fallback: only support the default storage sources\n            File storageFir = Environment.getExternalStorageDirectory();\n            if (storageFir != null && storageFir.isDirectory()) {\n                titles.add(VApp.getApp().getResources().getString(R.string.external_storage));\n                dirs.add(storageFir);\n            }\n        }\n    }\n\n    @Override\n    public Fragment getItem(int position) {\n        return ListAppFragment.newInstance(dirs.get(position));\n    }\n\n    @Override\n    public int getCount() {\n        return titles.size();\n    }\n\n    @Override\n    public CharSequence getPageTitle(int position) {\n        return titles.get(position);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java",
    "content": "package io.virtualapp.home.adapters;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.StaggeredGridLayoutManager;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport java.util.List;\n\nimport io.virtualapp.R;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.models.AppInfo;\nimport io.virtualapp.widgets.DragSelectRecyclerViewAdapter;\nimport io.virtualapp.widgets.LabelView;\n\n/**\n * @author Lody\n */\npublic class CloneAppListAdapter extends DragSelectRecyclerViewAdapter<CloneAppListAdapter.ViewHolder> {\n\n    private static final int TYPE_FOOTER = -2;\n    private final View mFooterView;\n    private LayoutInflater mInflater;\n    private List<AppInfo> mAppList;\n    private ItemEventListener mItemEventListener;\n\n    public CloneAppListAdapter(Context context) {\n        this.mInflater = LayoutInflater.from(context);\n        mFooterView = new View(context);\n        StaggeredGridLayoutManager.LayoutParams params = new StaggeredGridLayoutManager.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, VUiKit.dpToPx(context, 60)\n        );\n        params.setFullSpan(true);\n        mFooterView.setLayoutParams(params);\n\n    }\n\n    public void setOnItemClickListener(ItemEventListener mItemEventListener) {\n        this.mItemEventListener = mItemEventListener;\n    }\n\n    public List<AppInfo> getList() {\n        return mAppList;\n    }\n\n    public void setList(List<AppInfo> models) {\n        this.mAppList = models;\n        notifyDataSetChanged();\n    }\n\n    @Override\n    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        if (viewType == TYPE_FOOTER) {\n            return new ViewHolder(mFooterView);\n        }\n        return new ViewHolder(mInflater.inflate(R.layout.item_clone_app, null));\n    }\n\n    @Override\n    public void onBindViewHolder(ViewHolder holder, int position) {\n        if (getItemViewType(position) == TYPE_FOOTER) {\n            return;\n        }\n        super.onBindViewHolder(holder, position);\n        AppInfo info = mAppList.get(position);\n        holder.iconView.setImageDrawable(info.icon);\n        holder.nameView.setText(info.name);\n        if (isIndexSelected(position)) {\n            holder.iconView.setAlpha(1f);\n            holder.appCheckView.setImageResource(R.drawable.ic_check);\n        } else {\n            holder.iconView.setAlpha(0.65f);\n            holder.appCheckView.setImageResource(R.drawable.ic_no_check);\n        }\n        if (info.cloneCount > 0) {\n            holder.labelView.setVisibility(View.VISIBLE);\n            holder.labelView.setText(info.cloneCount + 1 + \"\");\n        } else {\n            holder.labelView.setVisibility(View.INVISIBLE);\n        }\n\n        holder.itemView.setOnClickListener(v -> {\n            mItemEventListener.onItemClick(info, position);\n        });\n    }\n\n    @Override\n    public void onAttachedToRecyclerView(RecyclerView recyclerView) {\n        super.onAttachedToRecyclerView(recyclerView);\n    }\n\n    @Override\n    protected boolean isIndexSelectable(int index) {\n        return mItemEventListener.isSelectable(index);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mAppList == null ? 1 : mAppList.size() + 1;\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        if (position == getItemCount() - 1) {\n            return TYPE_FOOTER;\n        }\n        return super.getItemViewType(position);\n    }\n\n    public AppInfo getItem(int index) {\n        return mAppList.get(index);\n    }\n\n    public interface ItemEventListener {\n\n        void onItemClick(AppInfo appData, int position);\n\n        boolean isSelectable(int position);\n    }\n\n    class ViewHolder extends RecyclerView.ViewHolder {\n        private ImageView iconView;\n        private TextView nameView;\n        private ImageView appCheckView;\n        private LabelView labelView;\n\n        ViewHolder(View itemView) {\n            super(itemView);\n            if (itemView != mFooterView) {\n                iconView = (ImageView) itemView.findViewById(R.id.item_app_icon);\n                nameView = (TextView) itemView.findViewById(R.id.item_app_name);\n                appCheckView = (ImageView) itemView.findViewById(R.id.item_app_checked);\n                labelView = (LabelView) itemView.findViewById(R.id.item_app_clone_count);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/adapters/LaunchpadAdapter.java",
    "content": "package io.virtualapp.home.adapters;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport java.util.List;\n\nimport io.virtualapp.R;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.models.AppData;\nimport io.virtualapp.home.models.MultiplePackageAppData;\nimport io.virtualapp.widgets.LabelView;\nimport io.virtualapp.widgets.LauncherIconView;\n\n/**\n * @author Lody\n */\npublic class LaunchpadAdapter extends RecyclerView.Adapter<LaunchpadAdapter.ViewHolder> {\n\n    private LayoutInflater mInflater;\n    private List<AppData> mList;\n    private SparseIntArray mColorArray = new SparseIntArray();\n    private OnAppClickListener mAppClickListener;\n\n    public LaunchpadAdapter(Context context) {\n        mInflater = LayoutInflater.from(context);\n    }\n\n    public void add(AppData model) {\n        int insertPos = mList.size() - 1;\n        mList.add(insertPos, model);\n        notifyItemInserted(insertPos);\n    }\n\n    public void replace(int index, AppData data) {\n        mList.set(index, data);\n        notifyItemChanged(index);\n    }\n\n    public void remove(AppData data) {\n        if (mList.remove(data)) {\n            notifyDataSetChanged();\n        }\n    }\n\n    @Override\n    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        return new ViewHolder(mInflater.inflate(R.layout.item_launcher_app, null));\n    }\n\n    @Override\n    public void onBindViewHolder(ViewHolder holder, int position) {\n        AppData data = mList.get(position);\n        holder.color = getColor(position);\n        holder.iconView.setImageDrawable(data.getIcon());\n        holder.nameView.setText(data.getName());\n        if (data.isFirstOpen() && !data.isLoading()) {\n            holder.firstOpenDot.setVisibility(View.VISIBLE);\n        } else {\n            holder.firstOpenDot.setVisibility(View.INVISIBLE);\n        }\n        holder.itemView.setBackgroundColor(holder.color);\n        holder.itemView.setOnClickListener(v -> {\n            if (mAppClickListener != null) {\n                mAppClickListener.onAppClick(position, data);\n            }\n        });\n        if (data instanceof MultiplePackageAppData) {\n            MultiplePackageAppData multipleData = (MultiplePackageAppData) data;\n            holder.spaceLabelView.setVisibility(View.VISIBLE);\n            holder.spaceLabelView.setText(multipleData.userId + 1 + \"\");\n        } else {\n            holder.spaceLabelView.setVisibility(View.INVISIBLE);\n        }\n        if (data.isLoading()) {\n            startLoadingAnimation(holder.iconView);\n        } else {\n            holder.iconView.setProgress(100, false);\n        }\n    }\n\n    private void startLoadingAnimation(LauncherIconView iconView) {\n        iconView.setProgress(40, true);\n        VUiKit.defer().when(() -> {\n            try {\n                Thread.sleep(900L);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }).done((res) -> iconView.setProgress(80, true));\n    }\n\n    private int getColor(int position) {\n        int color = mColorArray.get(position);\n        if (color == 0) {\n            int type = position % 3;\n            int row = position / 3;\n            int rowType = row % 3;\n            if (rowType == 0) {\n                if (type == 0) {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorA);\n                } else if (type == 1) {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorB);\n                } else {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorC);\n                }\n            } else if (rowType == 1) {\n                if (type == 0) {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorB);\n                } else if (type == 1) {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorC);\n                } else {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorA);\n                }\n            } else {\n                if (type == 0) {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorC);\n                } else if (type == 1) {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorA);\n                } else {\n                    color = mInflater.getContext().getResources().getColor(R.color.desktopColorB);\n                }\n            }\n            mColorArray.put(position, color);\n        }\n        return color;\n    }\n\n    @Override\n    public int getItemCount() {\n        return mList == null ? 0 : mList.size();\n    }\n\n    public List<AppData> getList() {\n        return mList;\n    }\n\n    public void setList(List<AppData> list) {\n        this.mList = list;\n        notifyDataSetChanged();\n    }\n\n    public void setAppClickListener(OnAppClickListener mAppClickListener) {\n        this.mAppClickListener = mAppClickListener;\n    }\n\n    public void moveItem(int pos, int targetPos) {\n        AppData model = mList.remove(pos);\n        mList.add(targetPos, model);\n        notifyItemMoved(pos, targetPos);\n    }\n\n    public void refresh(AppData model) {\n        int index = mList.indexOf(model);\n        if (index >= 0) {\n            notifyItemChanged(index);\n        }\n    }\n\n    public interface OnAppClickListener {\n        void onAppClick(int position, AppData model);\n    }\n\n    public class ViewHolder extends RecyclerView.ViewHolder {\n        public int color;\n        LauncherIconView iconView;\n        TextView nameView;\n        LabelView spaceLabelView;\n        View firstOpenDot;\n\n        ViewHolder(View itemView) {\n            super(itemView);\n            iconView = (LauncherIconView) itemView.findViewById(R.id.item_app_icon);\n            nameView = (TextView) itemView.findViewById(R.id.item_app_name);\n            spaceLabelView = (LabelView) itemView.findViewById(R.id.item_app_space_idx);\n            firstOpenDot = itemView.findViewById(R.id.item_first_open_dot);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/adapters/decorations/ItemOffsetDecoration.java",
    "content": "package io.virtualapp.home.adapters.decorations;\n\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.support.annotation.DimenRes;\nimport android.support.annotation.NonNull;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\n\npublic class ItemOffsetDecoration extends RecyclerView.ItemDecoration {\n\n    private int mItemOffset;\n\n    public ItemOffsetDecoration(int itemOffset) {\n        mItemOffset = itemOffset;\n    }\n\n    public ItemOffsetDecoration(@NonNull Context context, @DimenRes int itemOffsetId) {\n        this(context.getResources().getDimensionPixelSize(itemOffsetId));\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,\n                               RecyclerView.State state) {\n        outRect.set(mItemOffset, mItemOffset, mItemOffset, mItemOffset);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/ads/AdScheduler.java",
    "content": "package io.virtualapp.home.ads;\n\n/**\n * @author Lody\n */\n\npublic class AdScheduler {\n\n    private long adDeltaTime;\n    private long lastShowAdTime;\n\n    public AdScheduler(long adDeltaTime) {\n        this.adDeltaTime = adDeltaTime;\n    }\n\n    public void adShowed() {\n        lastShowAdTime = System.currentTimeMillis();\n    }\n\n    public boolean shouldShowAd() {\n        return System.currentTimeMillis() - lastShowAdTime >= adDeltaTime;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/AddAppButton.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\n\nimport io.virtualapp.R;\n\n/**\n * @author Lody\n */\n\npublic class AddAppButton implements AppData {\n\n    private String name;\n    private Drawable icon;\n\n    public AddAppButton(Context context) {\n        name = context.getResources().getString(R.string.add_app);\n        icon = context.getResources().getDrawable(R.drawable.ic_add_circle);\n    }\n\n    @Override\n    public boolean isLoading() {\n        return false;\n    }\n\n    @Override\n    public boolean isFirstOpen() {\n        return false;\n    }\n\n    @Override\n    public Drawable getIcon() {\n        return icon;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public boolean canReorder() {\n        return false;\n    }\n\n    @Override\n    public boolean canLaunch() {\n        return false;\n    }\n\n    @Override\n    public boolean canDelete() {\n        return false;\n    }\n\n    @Override\n    public boolean canCreateShortcut() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/AppData.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.graphics.drawable.Drawable;\n\n/**\n * @author Lody\n */\n\npublic interface AppData {\n\n    boolean isLoading();\n\n    boolean isFirstOpen();\n\n    Drawable getIcon();\n\n    String getName();\n\n    boolean canReorder();\n\n    boolean canLaunch();\n\n    boolean canDelete();\n\n    boolean canCreateShortcut();\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/AppInfo.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.graphics.drawable.Drawable;\n\n/**\n * @author Lody\n */\n\npublic class AppInfo {\n    public String packageName;\n    public String path;\n    public boolean fastOpen;\n    public Drawable icon;\n    public CharSequence name;\n    public int cloneCount;\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/AppInfoLite.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n */\n\npublic class AppInfoLite implements Parcelable {\n\n    public static final Creator<AppInfoLite> CREATOR = new Creator<AppInfoLite>() {\n        @Override\n        public AppInfoLite createFromParcel(Parcel source) {\n            return new AppInfoLite(source);\n        }\n\n        @Override\n        public AppInfoLite[] newArray(int size) {\n            return new AppInfoLite[size];\n        }\n    };\n    public String packageName;\n    public String path;\n    public boolean fastOpen;\n\n    public AppInfoLite(String packageName, String path, boolean fastOpen) {\n        this.packageName = packageName;\n        this.path = path;\n        this.fastOpen = fastOpen;\n    }\n\n    protected AppInfoLite(Parcel in) {\n        this.packageName = in.readString();\n        this.path = in.readString();\n        this.fastOpen = in.readByte() != 0;\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(this.packageName);\n        dest.writeString(this.path);\n        dest.writeByte(this.fastOpen ? (byte) 1 : (byte) 0);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/EmptyAppData.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.graphics.drawable.Drawable;\n\n/**\n * @author Lody\n */\n\npublic class EmptyAppData implements AppData {\n\n    @Override\n    public boolean isLoading() {\n        return false;\n    }\n\n    @Override\n    public boolean isFirstOpen() {\n        return false;\n    }\n\n    @Override\n    public Drawable getIcon() {\n        return null;\n    }\n\n    @Override\n    public String getName() {\n        return null;\n    }\n\n    @Override\n    public boolean canReorder() {\n        return false;\n    }\n\n    @Override\n    public boolean canLaunch() {\n        return false;\n    }\n\n    @Override\n    public boolean canDelete() {\n        return false;\n    }\n\n    @Override\n    public boolean canCreateShortcut() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/MultiplePackageAppData.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.graphics.drawable.Drawable;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.remote.InstalledAppInfo;\n\n/**\n * @author Lody\n */\n\npublic class MultiplePackageAppData implements AppData {\n\n    public InstalledAppInfo appInfo;\n    public int userId;\n    public boolean isFirstOpen;\n    public boolean isLoading;\n    public Drawable icon;\n    public String name;\n\n    public MultiplePackageAppData(PackageAppData target, int userId) {\n        this.userId = userId;\n        this.appInfo = VirtualCore.get().getInstalledAppInfo(target.packageName, 0);\n        this.isFirstOpen = !appInfo.isLaunched(userId);\n        if (target.icon != null) {\n            Drawable.ConstantState state = target.icon.getConstantState();\n            if (state != null) {\n                icon = state.newDrawable();\n            }\n        }\n        name = target.name;\n    }\n\n    @Override\n    public boolean isLoading() {\n        return isLoading;\n    }\n\n    @Override\n    public boolean isFirstOpen() {\n        return isFirstOpen;\n    }\n\n    @Override\n    public Drawable getIcon() {\n        return icon;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public boolean canReorder() {\n        return true;\n    }\n\n    @Override\n    public boolean canLaunch() {\n        return true;\n    }\n\n    @Override\n    public boolean canDelete() {\n        return true;\n    }\n\n    @Override\n    public boolean canCreateShortcut() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/models/PackageAppData.java",
    "content": "package io.virtualapp.home.models;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.graphics.drawable.Drawable;\n\nimport com.lody.virtual.remote.InstalledAppInfo;\n\n/**\n * @author Lody\n */\npublic class PackageAppData implements AppData {\n\n    public String packageName;\n    public String name;\n    public Drawable icon;\n    public boolean fastOpen;\n    public boolean isFirstOpen;\n    public boolean isLoading;\n\n    public PackageAppData(Context context, InstalledAppInfo installedAppInfo) {\n        this.packageName = installedAppInfo.packageName;\n        this.isFirstOpen = !installedAppInfo.isLaunched(0);\n        loadData(context, installedAppInfo.getApplicationInfo(installedAppInfo.getInstalledUsers()[0]));\n    }\n\n    private void loadData(Context context, ApplicationInfo appInfo) {\n        if (appInfo == null) {\n            return;\n        }\n        PackageManager pm = context.getPackageManager();\n        try {\n            CharSequence sequence = appInfo.loadLabel(pm);\n            if (sequence != null) {\n                name = sequence.toString();\n            }\n            icon = appInfo.loadIcon(pm);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public boolean isLoading() {\n        return isLoading;\n    }\n\n    @Override\n    public boolean isFirstOpen() {\n        return isFirstOpen;\n    }\n\n    @Override\n    public Drawable getIcon() {\n        return icon;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public boolean canReorder() {\n        return true;\n    }\n\n    @Override\n    public boolean canLaunch() {\n        return true;\n    }\n\n    @Override\n    public boolean canDelete() {\n        return true;\n    }\n\n    @Override\n    public boolean canCreateShortcut() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/platform/PlatformInfo.java",
    "content": "package io.virtualapp.home.platform;\n\nimport android.content.pm.PackageInfo;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author Lody\n */\npublic abstract class PlatformInfo {\n\n    private final Set<String> platformPkgs = new HashSet<>();\n\n    public PlatformInfo(String... pkgs) {\n        Collections.addAll(platformPkgs, pkgs);\n    }\n\n    public abstract boolean relyOnPlatform(PackageInfo info);\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/platform/WechatPlatformInfo.java",
    "content": "package io.virtualapp.home.platform;\n\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.PackageInfo;\n\n/**\n * @author Lody\n */\n\npublic class WechatPlatformInfo extends PlatformInfo {\n\n    public WechatPlatformInfo() {\n        super(\"com.tencent.mm\");\n    }\n\n    @Override\n    public boolean relyOnPlatform(PackageInfo info) {\n        if (info.activities == null) {\n            return false;\n        }\n        for (ActivityInfo activityInfo : info.activities) {\n            if (activityInfo.name.endsWith(\"WXEntryActivity\")) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/repo/AppDataSource.java",
    "content": "package io.virtualapp.home.repo;\n\nimport android.content.Context;\n\nimport com.lody.virtual.remote.InstallResult;\n\nimport org.jdeferred.Promise;\n\nimport java.io.File;\nimport java.util.List;\n\nimport io.virtualapp.home.models.AppData;\nimport io.virtualapp.home.models.AppInfo;\nimport io.virtualapp.home.models.AppInfoLite;\n\n/**\n * @author Lody\n * @version 1.0\n */\npublic interface AppDataSource {\n\n    /**\n     * @return All the Applications we Virtual.\n     */\n    Promise<List<AppData>, Throwable, Void> getVirtualApps();\n\n    /**\n     * @param context Context\n     * @return All the Applications we Installed.\n     */\n    Promise<List<AppInfo>, Throwable, Void> getInstalledApps(Context context);\n\n    Promise<List<AppInfo>, Throwable, Void> getStorageApps(Context context, File rootDir);\n\n    InstallResult addVirtualApp(AppInfoLite info);\n\n    boolean removeVirtualApp(String packageName, int userId);\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/repo/AppRepository.java",
    "content": "package io.virtualapp.home.repo;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\n\nimport com.lody.virtual.GmsSupport;\nimport com.lody.virtual.client.core.InstallStrategy;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.remote.InstallResult;\nimport com.lody.virtual.remote.InstalledAppInfo;\n\nimport org.jdeferred.Promise;\n\nimport java.io.File;\nimport java.text.Collator;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Locale;\n\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.models.AppData;\nimport io.virtualapp.home.models.AppInfo;\nimport io.virtualapp.home.models.AppInfoLite;\nimport io.virtualapp.home.models.MultiplePackageAppData;\nimport io.virtualapp.home.models.PackageAppData;\n\n/**\n * @author Lody\n */\npublic class AppRepository implements AppDataSource {\n\n    private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);\n    private static final List<String> SCAN_PATH_LIST = Arrays.asList(\n            \".\",\n            \"wandoujia/app\",\n            \"tencent/tassistant/apk\",\n            \"BaiduAsa9103056\",\n            \"360Download\",\n            \"pp/downloader\",\n            \"pp/downloader/apk\",\n            \"pp/downloader/silent/apk\");\n\n    private Context mContext;\n\n    public AppRepository(Context context) {\n        mContext = context;\n    }\n\n    private static boolean isSystemApplication(PackageInfo packageInfo) {\n        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0\n                && !GmsSupport.isGmsFamilyPackage(packageInfo.packageName);\n    }\n\n    @Override\n    public Promise<List<AppData>, Throwable, Void> getVirtualApps() {\n        return VUiKit.defer().when(() -> {\n            List<InstalledAppInfo> infos = VirtualCore.get().getInstalledApps(0);\n            List<AppData> models = new ArrayList<>();\n            for (InstalledAppInfo info : infos) {\n                if (!VirtualCore.get().isPackageLaunchable(info.packageName)) {\n                    continue;\n                }\n                PackageAppData data = new PackageAppData(mContext, info);\n                if (VirtualCore.get().isAppInstalledAsUser(0, info.packageName)) {\n                    models.add(data);\n                }\n                int[] userIds = info.getInstalledUsers();\n                for (int userId : userIds) {\n                    if (userId != 0) {\n                        models.add(new MultiplePackageAppData(data, userId));\n                    }\n                }\n            }\n            return models;\n        });\n    }\n\n    @Override\n    public Promise<List<AppInfo>, Throwable, Void> getInstalledApps(Context context) {\n        return VUiKit.defer().when(() -> convertPackageInfoToAppData(context, context.getPackageManager().getInstalledPackages(0), true));\n    }\n\n    @Override\n    public Promise<List<AppInfo>, Throwable, Void> getStorageApps(Context context, File rootDir) {\n        return VUiKit.defer().when(() -> convertPackageInfoToAppData(context, findAndParseAPKs(context, rootDir, SCAN_PATH_LIST), false));\n    }\n\n    private List<PackageInfo> findAndParseAPKs(Context context, File rootDir, List<String> paths) {\n        List<PackageInfo> packageList = new ArrayList<>();\n        if (paths == null)\n            return packageList;\n        for (String path : paths) {\n            File[] dirFiles = new File(rootDir, path).listFiles();\n            if (dirFiles == null)\n                continue;\n            for (File f : dirFiles) {\n                if (!f.getName().toLowerCase().endsWith(\".apk\"))\n                    continue;\n                PackageInfo pkgInfo = null;\n                try {\n                    pkgInfo = context.getPackageManager().getPackageArchiveInfo(f.getAbsolutePath(), 0);\n                    pkgInfo.applicationInfo.sourceDir = f.getAbsolutePath();\n                    pkgInfo.applicationInfo.publicSourceDir = f.getAbsolutePath();\n                } catch (Exception e) {\n                    // Ignore\n                }\n                if (pkgInfo != null)\n                    packageList.add(pkgInfo);\n            }\n        }\n        return packageList;\n    }\n\n    private List<AppInfo> convertPackageInfoToAppData(Context context, List<PackageInfo> pkgList, boolean fastOpen) {\n        PackageManager pm = context.getPackageManager();\n        List<AppInfo> list = new ArrayList<>(pkgList.size());\n        String hostPkg = VirtualCore.get().getHostPkg();\n        for (PackageInfo pkg : pkgList) {\n            // ignore the host package\n            if (hostPkg.equals(pkg.packageName)) {\n                continue;\n            }\n            // ignore the System package\n            if (isSystemApplication(pkg)) {\n                continue;\n            }\n            ApplicationInfo ai = pkg.applicationInfo;\n            String path = ai.publicSourceDir != null ? ai.publicSourceDir : ai.sourceDir;\n            if (path == null) {\n                continue;\n            }\n            AppInfo info = new AppInfo();\n            info.packageName = pkg.packageName;\n            info.fastOpen = fastOpen;\n            info.path = path;\n            info.icon = ai.loadIcon(pm);\n            info.name = ai.loadLabel(pm);\n            InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(pkg.packageName, 0);\n            if (installedAppInfo != null) {\n                info.cloneCount = installedAppInfo.getInstalledUsers().length;\n            }\n            list.add(info);\n        }\n        return list;\n    }\n\n    @Override\n    public InstallResult addVirtualApp(AppInfoLite info) {\n        int flags = InstallStrategy.COMPARE_VERSION | InstallStrategy.SKIP_DEX_OPT;\n        if (info.fastOpen) {\n            flags |= InstallStrategy.DEPEND_SYSTEM_IF_EXIST;\n        }\n        return VirtualCore.get().installPackage(info.path, flags);\n    }\n\n    @Override\n    public boolean removeVirtualApp(String packageName, int userId) {\n        return VirtualCore.get().uninstallPackageAsUser(packageName, userId);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/home/repo/PackageAppDataStorage.java",
    "content": "package io.virtualapp.home.repo;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.remote.InstalledAppInfo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport io.virtualapp.VApp;\nimport io.virtualapp.abs.Callback;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.models.PackageAppData;\n\n/**\n * @author Lody\n *         <p>\n *         Cache the loaded PackageAppData.\n */\npublic class PackageAppDataStorage {\n\n    private static final PackageAppDataStorage STORAGE = new PackageAppDataStorage();\n    private final Map<String, PackageAppData> packageDataMap = new HashMap<>();\n\n    public static PackageAppDataStorage get() {\n        return STORAGE;\n    }\n\n    public PackageAppData acquire(String packageName) {\n        PackageAppData data;\n        synchronized (packageDataMap) {\n            data = packageDataMap.get(packageName);\n            if (data == null) {\n                data = loadAppData(packageName);\n            }\n        }\n        return data;\n    }\n\n    public void acquire(String packageName, Callback<PackageAppData> callback) {\n        VUiKit.defer()\n                .when(() -> acquire(packageName))\n                .done(callback::callback);\n    }\n\n    private PackageAppData loadAppData(String packageName) {\n        InstalledAppInfo setting = VirtualCore.get().getInstalledAppInfo(packageName, 0);\n        if (setting != null) {\n            PackageAppData data = new PackageAppData(VApp.getApp(), setting);\n            synchronized (packageDataMap) {\n                packageDataMap.put(packageName, data);\n            }\n            return data;\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/splash/SplashActivity.java",
    "content": "package io.virtualapp.splash;\n\nimport android.os.Bundle;\nimport android.view.WindowManager;\n\nimport com.google.android.gms.ads.AdRequest;\nimport com.google.android.gms.ads.AdView;\nimport com.lody.virtual.client.core.VirtualCore;\n\nimport io.virtualapp.R;\nimport io.virtualapp.VCommends;\nimport io.virtualapp.abs.ui.VActivity;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.FlurryROMCollector;\nimport io.virtualapp.home.HomeActivity;\nimport jonathanfinerty.once.Once;\n\npublic class SplashActivity extends VActivity {\n\n    private AdView mAdView;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        @SuppressWarnings(\"unused\")\n        boolean enterGuide = !Once.beenDone(Once.THIS_APP_INSTALL, VCommends.TAG_NEW_VERSION);\n        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,\n                WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_splash);\n        showBanner();\n        VUiKit.defer().when(() -> {\n            if (!Once.beenDone(\"collect_flurry\")) {\n                FlurryROMCollector.startCollect();\n                Once.markDone(\"collect_flurry\");\n            }\n            long time = System.currentTimeMillis();\n            doActionInThread();\n            time = System.currentTimeMillis() - time;\n            long delta = 3000L - time;\n            if (delta > 0) {\n                VUiKit.sleep(delta);\n            }\n        }).done((res) -> {\n            HomeActivity.goHome(this);\n            finish();\n        });\n    }\n\n    private void showBanner() {\n        mAdView = (AdView) findViewById(R.id.splash_banner);\n        AdRequest adRequest = new AdRequest.Builder().build();\n        mAdView.loadAd(adRequest);\n    }\n\n    private void doActionInThread() {\n        if (!VirtualCore.get().isEngineLaunched()) {\n            VirtualCore.get().waitForEngine();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/vs/VSManagerActivity.java",
    "content": "package io.virtualapp.vs;\n\nimport io.virtualapp.abs.ui.VActivity;\n\n/**\n * @author Lody\n *\n *\n *\n */\npublic class VSManagerActivity extends VActivity {\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/BallGridBeatIndicator.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\n\nimport java.util.ArrayList;\n\npublic class BallGridBeatIndicator extends Indicator {\n\n    private static final int ALPHA = 255;\n\n    private static final int[] ALPHAS = new int[]{ALPHA,\n            ALPHA,\n            ALPHA,\n            ALPHA,\n            ALPHA,\n            ALPHA,\n            ALPHA,\n            ALPHA,\n            ALPHA};\n\n    @Override\n    public void draw(Canvas canvas, Paint paint) {\n        float circleSpacing = 4;\n        float radius = (getWidth() - circleSpacing * 4) / 6;\n        float x = getWidth() / 2 - (radius * 2 + circleSpacing);\n        float y = getWidth() / 2 - (radius * 2 + circleSpacing);\n\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                canvas.save();\n                float translateX = x + (radius * 2) * j + circleSpacing * j;\n                float translateY = y + (radius * 2) * i + circleSpacing * i;\n                canvas.translate(translateX, translateY);\n                paint.setAlpha(ALPHAS[3 * i + j]);\n                canvas.drawCircle(0, 0, radius, paint);\n                canvas.restore();\n            }\n        }\n    }\n\n    @Override\n    public ArrayList<ValueAnimator> onCreateAnimators() {\n        ArrayList<ValueAnimator> animators = new ArrayList<>();\n\n        int[] durations = {960, 930, 1190, 1130, 1340, 940, 1200, 820, 1190};\n        int[] delays = {360, 400, 680, 410, 710, -150, -120, 10, 320};\n\n        for (int i = 0; i < 9; i++) {\n            final int index = i;\n            ValueAnimator alphaAnim = ValueAnimator.ofInt(255, 168, 255);\n            alphaAnim.setDuration(durations[i]);\n            alphaAnim.setRepeatCount(-1);\n            alphaAnim.setStartDelay(delays[i]);\n            addUpdateListener(alphaAnim, animation -> {\n                ALPHAS[index] = (int) animation.getAnimatedValue();\n                postInvalidate();\n            });\n            animators.add(alphaAnim);\n        }\n        return animators;\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/BallPulseIndicator.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\n\nimport java.util.ArrayList;\n\npublic class BallPulseIndicator extends Indicator {\n\n    public static final float SCALE = 1.0f;\n\n    //scale x ,y\n    private float[] scaleFloats = new float[]{SCALE,\n            SCALE,\n            SCALE};\n\n\n    @Override\n    public void draw(Canvas canvas, Paint paint) {\n        float circleSpacing = 4;\n        float radius = (Math.min(getWidth(), getHeight()) - circleSpacing * 2) / 6;\n        float x = getWidth() / 2 - (radius * 2 + circleSpacing);\n        float y = getHeight() / 2;\n        for (int i = 0; i < 3; i++) {\n            canvas.save();\n            float translateX = x + (radius * 2) * i + circleSpacing * i;\n            canvas.translate(translateX, y);\n            canvas.scale(scaleFloats[i], scaleFloats[i]);\n            canvas.drawCircle(0, 0, radius, paint);\n            canvas.restore();\n        }\n    }\n\n    @Override\n    public ArrayList<ValueAnimator> onCreateAnimators() {\n        ArrayList<ValueAnimator> animators = new ArrayList<>();\n        int[] delays = new int[]{120, 240, 360};\n        for (int i = 0; i < 3; i++) {\n            final int index = i;\n\n            ValueAnimator scaleAnim = ValueAnimator.ofFloat(1, 0.3f, 1);\n\n            scaleAnim.setDuration(750);\n            scaleAnim.setRepeatCount(-1);\n            scaleAnim.setStartDelay(delays[i]);\n\n            addUpdateListener(scaleAnim, animation -> {\n                scaleFloats[index] = (float) animation.getAnimatedValue();\n                postInvalidate();\n            });\n            animators.add(scaleAnim);\n        }\n        return animators;\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/BaseView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.LinearInterpolator;\n\npublic abstract class BaseView extends View {\n\n\n    public ValueAnimator valueAnimator;\n\n    public BaseView(Context context) {\n        this(context, null);\n    }\n\n    public BaseView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public BaseView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        InitPaint();\n    }\n\n    public void startAnim() {\n        stopAnim();\n        startViewAnim(0f, 1f, 500);\n    }\n\n    public void startAnim(int time) {\n        stopAnim();\n        startViewAnim(0f, 1f, time);\n    }\n\n    public void stopAnim() {\n        if (valueAnimator != null) {\n            clearAnimation();\n\n            valueAnimator.setRepeatCount(0);\n            valueAnimator.cancel();\n            valueAnimator.end();\n            if (OnStopAnim() == 0) {\n                valueAnimator.setRepeatCount(0);\n                valueAnimator.cancel();\n                valueAnimator.end();\n            }\n\n        }\n    }\n\n    private ValueAnimator startViewAnim(float startF, final float endF, long time) {\n        valueAnimator = ValueAnimator.ofFloat(startF, endF);\n        valueAnimator.setDuration(time);\n        valueAnimator.setInterpolator(new LinearInterpolator());\n\n\n        valueAnimator.setRepeatCount(SetAnimRepeatCount());\n\n\n        if (ValueAnimator.RESTART == SetAnimRepeatMode()) {\n            valueAnimator.setRepeatMode(ValueAnimator.RESTART);\n\n        } else if (ValueAnimator.REVERSE == SetAnimRepeatMode()) {\n            valueAnimator.setRepeatMode(ValueAnimator.REVERSE);\n\n        }\n\n        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator valueAnimator) {\n                OnAnimationUpdate(valueAnimator);\n\n            }\n        });\n        valueAnimator.addListener(new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                super.onAnimationEnd(animation);\n\n            }\n\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n                super.onAnimationStart(animation);\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n                super.onAnimationRepeat(animation);\n                OnAnimationRepeat(animation);\n            }\n        });\n        if (!valueAnimator.isRunning()) {\n            AnimIsRunning();\n            valueAnimator.start();\n\n        }\n\n        return valueAnimator;\n    }\n\n\n    protected abstract void InitPaint();\n\n    protected abstract void OnAnimationUpdate(ValueAnimator valueAnimator);\n\n    protected abstract void OnAnimationRepeat(Animator animation);\n\n    protected abstract int OnStopAnim();\n\n    protected abstract int SetAnimRepeatMode();\n\n    protected abstract int SetAnimRepeatCount();\n\n    protected abstract void AnimIsRunning();\n\n\n    public float getFontlength(Paint paint, String str) {\n        Rect rect = new Rect();\n        paint.getTextBounds(str, 0, str.length(), rect);\n        return rect.width();\n    }\n\n    public float getFontHeight(Paint paint, String str) {\n        Rect rect = new Rect();\n        paint.getTextBounds(str, 0, str.length(), rect);\n        return rect.height();\n\n    }\n\n    public float getFontHeight(Paint paint) {\n        Paint.FontMetrics fm = paint.getFontMetrics();\n        return fm.descent - fm.ascent;\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/CardStackAdapter.java",
    "content": "package io.virtualapp.widgets;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.util.DisplayMetrics;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.DecelerateInterpolator;\nimport android.widget.FrameLayout;\n\nimport io.virtualapp.R;\n\n/**\n * This class acts as an adapter for the {@link CardStackLayout} view. This\n * adapter is intentionally made an abstract class with following abstract\n * methods -\n * <p>\n * <p>\n * {@link #getCount()} - Decides the number of views present in the view\n * <p>\n * {@link #createView(int, ViewGroup)} - Creates the view for all positions in\n * range [0, {@link #getCount()})\n * <p>\n * Contains the logic for touch events in {@link #onTouch(View, MotionEvent)}\n */\npublic abstract class CardStackAdapter implements View.OnTouchListener, View.OnClickListener {\n\n\tpublic static final int ANIM_DURATION = 600;\n\tpublic static final int DECELERATION_FACTOR = 2;\n\n\tpublic static final int INVALID_CARD_POSITION = -1;\n\tprivate final int mScreenHeight;\n\tprivate final int dp30;\n\t// Settings for the adapter from layout\n\tprivate float mCardGapBottom;\n\tprivate float mCardGap;\n\tprivate int mParallaxScale;\n\tprivate boolean mParallaxEnabled;\n\tprivate boolean mShowInitAnimation;\n\tprivate int fullCardHeight;\n\tprivate View[] mCardViews;\n\tprivate float dp8;\n\tprivate CardStackLayout mParent;\n\n\tprivate boolean mScreenTouchable = false;\n\tprivate float mTouchFirstY = -1;\n\tprivate float mTouchPrevY = -1;\n\tprivate float mTouchDistance = 0;\n\tprivate int mSelectedCardPosition = INVALID_CARD_POSITION;\n\tprivate float scaleFactorForElasticEffect;\n\tprivate int mParentPaddingTop = 0;\n\tprivate int mCardPaddingInternal = 0;\n\n\tpublic CardStackAdapter(Context context) {\n\t\tResources resources = context.getResources();\n\n\t\tDisplayMetrics dm = Resources.getSystem().getDisplayMetrics();\n\t\tmScreenHeight = dm.heightPixels;\n\t\tdp30 = (int) resources.getDimension(R.dimen.dp30);\n\t\tscaleFactorForElasticEffect = (int) resources.getDimension(R.dimen.dp8);\n\t\tdp8 = (int) resources.getDimension(R.dimen.dp8);\n\t}\n\n\tprotected float getCardGapBottom() {\n\t\treturn mCardGapBottom;\n\t}\n\n\t/**\n\t * Defines and initializes the view to be shown in the\n\t * {@link CardStackLayout} Provides two parameters to the sub-class namely -\n\t *\n\t * @param position\n\t * @param container\n\t * @return View corresponding to the position and parent container\n\t */\n\tpublic abstract View createView(int position, ViewGroup container);\n\n\t/**\n\t * Defines the number of cards that are present in the\n\t * {@link CardStackLayout}\n\t *\n\t * @return cardCount - Number of views in the related\n\t *         {@link CardStackLayout}\n\t */\n\tpublic abstract int getCount();\n\n\t/**\n\t * Returns true if no animation is in progress currently. Can be used to\n\t * disable any events if they are not allowed during an animation. Returns\n\t * false if an animation is in progress.\n\t *\n\t * @return - true if animation in progress, false otherwise\n\t */\n\tpublic boolean isScreenTouchable() {\n\t\treturn mScreenTouchable;\n\t}\n\n\tprivate void setScreenTouchable(boolean screenTouchable) {\n\t\tthis.mScreenTouchable = screenTouchable;\n\t}\n\n\tvoid addView(final int position) {\n\t\tView root = createView(position, mParent);\n\t\troot.setOnTouchListener(this);\n\t\troot.setTag(R.id.cardstack_internal_position_tag, position);\n\t\troot.setLayerType(View.LAYER_TYPE_HARDWARE, null);\n\n\t\tmCardPaddingInternal = root.getPaddingTop();\n\n\t\tFrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, fullCardHeight);\n\t\troot.setLayoutParams(lp);\n\t\tif (mShowInitAnimation) {\n\t\t\troot.setY(getCardFinalY(position));\n\t\t\tsetScreenTouchable(false);\n\t\t} else {\n\t\t\troot.setY(getCardOriginalY(position) - mParentPaddingTop);\n\t\t\tsetScreenTouchable(true);\n\t\t}\n\n\t\tmCardViews[position] = root;\n\n\t\tmParent.addView(root);\n\t}\n\n\tprotected float getCardFinalY(int position) {\n\t\treturn mScreenHeight - dp30 - ((getCount() - position) * mCardGapBottom) - mCardPaddingInternal;\n\t}\n\n\tprotected float getCardOriginalY(int position) {\n\t\treturn mParentPaddingTop + mCardGap * position;\n\t}\n\n\t/**\n\t * Resets all cards in {@link CardStackLayout} to their initial positions\n\t *\n\t * @param r\n\t *            Execute r.run() once the reset animation is done\n\t */\n\tpublic void resetCards(Runnable r) {\n\t\tList<Animator> animations = new ArrayList<>(getCount());\n\t\tfor (int i = 0; i < getCount(); i++) {\n\t\t\tfinal View child = mCardViews[i];\n\t\t\tanimations.add(ObjectAnimator.ofFloat(child, View.Y, (int) child.getY(), getCardOriginalY(i)));\n\t\t}\n\t\tstartAnimations(animations, r, true);\n\t}\n\n\t/**\n\t * Plays together all animations passed in as parameter. Once animation is\n\t * completed, r.run() is executed. If parameter isReset is set to true,\n\t * {@link #mSelectedCardPosition} is set to {@link #INVALID_CARD_POSITION}\n\t *\n\t * @param animations\n\t * @param r\n\t * @param isReset\n\t */\n\tprivate void startAnimations(List<Animator> animations, final Runnable r, final boolean isReset) {\n\t\tAnimatorSet animatorSet = new AnimatorSet();\n\t\tanimatorSet.playTogether(animations);\n\t\tanimatorSet.setDuration(ANIM_DURATION);\n\t\tanimatorSet.setInterpolator(new DecelerateInterpolator(DECELERATION_FACTOR));\n\t\tanimatorSet.addListener(new AnimatorListenerAdapter() {\n\t\t\t@Override\n\t\t\tpublic void onAnimationEnd(Animator animation) {\n\t\t\t\tif (r != null)\n\t\t\t\t\tr.run();\n\t\t\t\tsetScreenTouchable(true);\n\t\t\t\tif (isReset)\n\t\t\t\t\tmSelectedCardPosition = INVALID_CARD_POSITION;\n\t\t\t}\n\t\t});\n\t\tanimatorSet.start();\n\t}\n\n\t@Override\n\tpublic boolean onTouch(View v, MotionEvent event) {\n\t\tif (!isScreenTouchable()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfloat y = event.getRawY();\n\t\tint positionOfCardToMove = (int) v.getTag(R.id.cardstack_internal_position_tag);\n\n\t\tswitch (event.getAction()) {\n\t\t\tcase MotionEvent.ACTION_DOWN :\n\t\t\t\tif (mTouchFirstY != -1) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tmTouchPrevY = mTouchFirstY = y;\n\t\t\t\tmTouchDistance = 0;\n\t\t\t\tbreak;\n\t\t\tcase MotionEvent.ACTION_MOVE :\n\t\t\t\tif (mSelectedCardPosition == INVALID_CARD_POSITION)\n\t\t\t\t\tmoveCards(positionOfCardToMove, y - mTouchFirstY);\n\t\t\t\tmTouchDistance += Math.abs(y - mTouchPrevY);\n\t\t\t\tbreak;\n\t\t\tcase MotionEvent.ACTION_CANCEL :\n\t\t\tcase MotionEvent.ACTION_UP :\n\t\t\t\tif (mTouchDistance < dp8 && Math.abs(y - mTouchFirstY) < dp8\n\t\t\t\t\t\t&& mSelectedCardPosition == INVALID_CARD_POSITION) {\n\t\t\t\t\tonClick(v);\n\t\t\t\t} else {\n\t\t\t\t\tresetCards();\n\t\t\t\t}\n\t\t\t\tmTouchPrevY = mTouchFirstY = -1;\n\t\t\t\tmTouchDistance = 0;\n\t\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void onClick(final View v) {\n\n\t\tif (!isScreenTouchable()) {\n\t\t\treturn;\n\t\t}\n\t\tsetScreenTouchable(false);\n\t\tif (mSelectedCardPosition == INVALID_CARD_POSITION) {\n\t\t\tmSelectedCardPosition = (int) v.getTag(R.id.cardstack_internal_position_tag);\n\n\t\t\tList<Animator> animations = new ArrayList<>(getCount());\n\t\t\tfor (int i = 0; i < getCount(); i++) {\n\t\t\t\tView child = mCardViews[i];\n\t\t\t\tanimations.add(getAnimatorForView(child, i, mSelectedCardPosition));\n\t\t\t}\n\t\t\tstartAnimations(animations, () -> {\n\t\t\t\tsetScreenTouchable(true);\n\t\t\t\tif (mParent.getOnCardSelectedListener() != null) {\n\t\t\t\t\tmParent.getOnCardSelectedListener().onCardSelected(v, mSelectedCardPosition);\n\t\t\t\t}\n\t\t\t}, false);\n\n\t\t}\n\t}\n\n\t/**\n\t * This method can be overridden to have different animations for each card\n\t * when a click event happens on any card view. This method will be called\n\t * for every\n\t *\n\t * @param view\n\t *            The view for which this method needs to return an animator\n\t * @param selectedCardPosition\n\t *            Position of the card that was clicked\n\t * @param currentCardPosition\n\t *            Position of the current card\n\t * @return animator which has to be applied on the current card\n\t */\n\tprotected Animator getAnimatorForView(View view, int currentCardPosition, int selectedCardPosition) {\n\t\tif (currentCardPosition != selectedCardPosition) {\n\t\t\treturn ObjectAnimator.ofFloat(view, View.Y, (int) view.getY(), getCardFinalY(currentCardPosition));\n\t\t} else {\n\t\t\treturn ObjectAnimator.ofFloat(view, View.Y, (int) view.getY(),\n\t\t\t\t\tgetCardOriginalY(0) + (currentCardPosition * mCardGapBottom));\n\t\t}\n\t}\n\n\tprivate void moveCards(int positionOfCardToMove, float diff) {\n\t\tif (diff < 0 || positionOfCardToMove < 0 || positionOfCardToMove >= getCount())\n\t\t\treturn;\n\t\tfor (int i = positionOfCardToMove; i < getCount(); i++) {\n\t\t\tfinal View child = mCardViews[i];\n\t\t\tfloat diffCard = diff / scaleFactorForElasticEffect;\n\t\t\tif (mParallaxEnabled) {\n\t\t\t\tif (mParallaxScale > 0) {\n\t\t\t\t\tdiffCard = diffCard * (mParallaxScale / 3) * (getCount() + 1 - i);\n\t\t\t\t} else {\n\t\t\t\t\tint scale = mParallaxScale * -1;\n\t\t\t\t\tdiffCard = diffCard * (i * (scale / 3) + 1);\n\t\t\t\t}\n\t\t\t} else\n\t\t\t\tdiffCard = diffCard * (getCount() * 2 + 1);\n\t\t\tchild.setY(getCardOriginalY(i) + diffCard);\n\t\t}\n\t}\n\n\t/**\n\t * Provides an API to {@link CardStackLayout} to set the parameters provided\n\t * to it in its XML\n\t *\n\t * @param cardStackLayout\n\t *            Parent of all cards\n\t */\n\tvoid setAdapterParams(CardStackLayout cardStackLayout) {\n\t\tmParent = cardStackLayout;\n\t\tmCardViews = new View[getCount()];\n\t\tmCardGapBottom = cardStackLayout.getCardGapBottom();\n\t\tmCardGap = cardStackLayout.getCardGap();\n\t\tmParallaxScale = cardStackLayout.getParallaxScale();\n\t\tmParallaxEnabled = cardStackLayout.isParallaxEnabled();\n\t\tif (mParallaxEnabled && mParallaxScale == 0)\n\t\t\tmParallaxEnabled = false;\n\t\tmShowInitAnimation = cardStackLayout.isShowInitAnimation();\n\t\tmParentPaddingTop = cardStackLayout.getPaddingTop();\n\t\tfullCardHeight = (int) (mScreenHeight - dp30 - dp8 - getCount() * mCardGapBottom);\n\t}\n\n\t/**\n\t * Resets all cards in {@link CardStackLayout} to their initial positions\n\t */\n\tpublic void resetCards() {\n\t\tresetCards(null);\n\t}\n\n\t/**\n\t * Returns false if all the cards are in their initial position i.e. no card\n\t * is selected\n\t * <p>\n\t * Returns true if the {@link CardStackLayout} has a card selected and all\n\t * other cards are at the bottom of the screen.\n\t *\n\t * @return true if any card is selected, false otherwise\n\t */\n\tpublic boolean isCardSelected() {\n\t\treturn mSelectedCardPosition != INVALID_CARD_POSITION;\n\t}\n\n\t/**\n\t * Returns the position of selected card. If no card is selected, returns\n\t * {@link #INVALID_CARD_POSITION}\n\t */\n\tpublic int getSelectedCardPosition() {\n\t\treturn mSelectedCardPosition;\n\t}\n\n\t/**\n\t * Since there is no view recycling in {@link CardStackLayout}, we maintain\n\t * an instance of every view that is set for every position. This method\n\t * returns a view at the requested position.\n\t *\n\t * @param position\n\t *            Position of card in {@link CardStackLayout}\n\t * @return View at requested position\n\t */\n\tpublic View getCardView(int position) {\n\t\tif (mCardViews == null)\n\t\t\treturn null;\n\n\t\treturn mCardViews[position];\n\t}\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/CardStackLayout.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport io.virtualapp.R;\n\n/**\n * Displays a list of cards as a stack on the screen.\n * <p>\n * <b>XML attributes</b>\n * <p>\n * See {@link R.styleable#CardStackLayout CardStackLayout Attributes}\n * <p>\n * {@link R.styleable#CardStackLayout_showInitAnimation}\n * {@link R.styleable#CardStackLayout_card_gap}\n * {@link R.styleable#CardStackLayout_card_gap_bottom}\n * {@link R.styleable#CardStackLayout_parallax_enabled}\n * {@link R.styleable#CardStackLayout_parallax_scale}\n */\npublic class CardStackLayout extends FrameLayout {\n\tpublic static final boolean PARALLAX_ENABLED_DEFAULT = false;\n\tpublic static final boolean SHOW_INIT_ANIMATION_DEFAULT = true;\n\n\tprivate float mCardGapBottom;\n\tprivate float mCardGap;\n\tprivate boolean mShowInitAnimation;\n\tprivate boolean mParallaxEnabled;\n\tprivate int mParallaxScale;\n\tprivate OnCardSelected mOnCardSelectedListener = null;\n\n\tprivate CardStackAdapter mAdapter = null;\n\n\tpublic CardStackLayout(Context context) {\n\t\tsuper(context);\n\t\tresetDefaults();\n\t}\n\n\tpublic CardStackLayout(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic CardStackLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t\thandleArgs(context, attrs, defStyleAttr, 0);\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\tpublic CardStackLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n\t\tsuper(context, attrs, defStyleAttr, defStyleRes);\n\t\thandleArgs(context, attrs, defStyleAttr, defStyleRes);\n\t}\n\n\t/**\n\t * package restricted\n\t */\n\tOnCardSelected getOnCardSelectedListener() {\n\t\treturn mOnCardSelectedListener;\n\t}\n\n\t/**\n\t * Listen on card selection events for {@link CardStackLayout}. Sends\n\t * clicked view and it's corresponding position in the callback.\n\t *\n\t * @param onCardSelectedListener\n\t *            listener\n\t */\n\tpublic void setOnCardSelectedListener(OnCardSelected onCardSelectedListener) {\n\t\tthis.mOnCardSelectedListener = onCardSelectedListener;\n\t}\n\n\tprivate void resetDefaults() {\n\t\tmOnCardSelectedListener = null;\n\t}\n\n\tprivate void handleArgs(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n\t\tresetDefaults();\n\n\t\tfinal TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CardStackLayout, defStyleAttr,\n\t\t\t\tdefStyleRes);\n\t\tmParallaxEnabled = a.getBoolean(R.styleable.CardStackLayout_parallax_enabled, PARALLAX_ENABLED_DEFAULT);\n\t\tmShowInitAnimation = a.getBoolean(R.styleable.CardStackLayout_showInitAnimation, SHOW_INIT_ANIMATION_DEFAULT);\n\t\tmParallaxScale = a.getInteger(R.styleable.CardStackLayout_parallax_scale,\n\t\t\t\tgetResources().getInteger(R.integer.parallax_scale_default));\n\t\tmCardGap = a.getDimension(R.styleable.CardStackLayout_card_gap, getResources().getDimension(R.dimen.card_gap));\n\t\tmCardGapBottom = a.getDimension(R.styleable.CardStackLayout_card_gap_bottom,\n\t\t\t\tgetResources().getDimension(R.dimen.card_gap_bottom));\n\n\t\ta.recycle();\n\t}\n\n\t/**\n\t * @return adapter of type {@link CardStackAdapter} that is set for this\n\t *         view.\n\t */\n\tpublic CardStackAdapter getAdapter() {\n\t\treturn mAdapter;\n\t}\n\n\t/**\n\t * Set the adapter for this {@link CardStackLayout}\n\t *\n\t * @param adapter\n\t *            Should extend {@link CardStackAdapter}\n\t */\n\tpublic void setAdapter(CardStackAdapter adapter) {\n\t\tthis.mAdapter = adapter;\n\t\tmAdapter.setAdapterParams(this);\n\t\tfor (int i = 0; i < mAdapter.getCount(); i++) {\n\t\t\tmAdapter.addView(i);\n\t\t}\n\n\t\tif (mShowInitAnimation) {\n\t\t\tpostDelayed(this::restoreCards, 500);\n\t\t}\n\t}\n\n\t/**\n\t * @return currently set parallax scale value.\n\t */\n\tpublic int getParallaxScale() {\n\t\treturn mParallaxScale;\n\t}\n\n\t/**\n\t * Sets the value of parallax scale. Parallax scale is the factor which\n\t * decides how much distance a card will scroll when the user drags it down.\n\t */\n\tpublic void setParallaxScale(int mParallaxScale) {\n\t\tthis.mParallaxScale = mParallaxScale;\n\t}\n\n\tpublic boolean isParallaxEnabled() {\n\t\treturn mParallaxEnabled;\n\t}\n\n\tpublic void setParallaxEnabled(boolean mParallaxEnabled) {\n\t\tthis.mParallaxEnabled = mParallaxEnabled;\n\t}\n\n\tpublic boolean isShowInitAnimation() {\n\t\treturn mShowInitAnimation;\n\t}\n\n\tpublic void setShowInitAnimation(boolean mShowInitAnimation) {\n\t\tthis.mShowInitAnimation = mShowInitAnimation;\n\t}\n\n\t/**\n\t * @return the gap (in pixels) between two consecutive cards\n\t */\n\tpublic float getCardGap() {\n\t\treturn mCardGap;\n\t}\n\n\t/**\n\t * Set the gap (in pixels) between two consecutive cards\n\t */\n\tpublic void setCardGap(float mCardGap) {\n\t\tthis.mCardGap = mCardGap;\n\t}\n\n\t/**\n\t * @return gap between the two consecutive cards when collapsed to the\n\t *         bottom of the screen\n\t */\n\tpublic float getCardGapBottom() {\n\t\treturn mCardGapBottom;\n\t}\n\n\tpublic void setCardGapBottom(float mCardGapBottom) {\n\t\tthis.mCardGapBottom = mCardGapBottom;\n\t}\n\n\t/**\n\t * @return true if a card is selected, false otherwise\n\t */\n\tpublic boolean isCardSelected() {\n\t\treturn mAdapter.isCardSelected();\n\t}\n\n\t/**\n\t * Removes the adapter that was previously set using\n\t * {@link #setAdapter(CardStackAdapter)}\n\t */\n\tpublic void removeAdapter() {\n\t\tif (getChildCount() > 0)\n\t\t\tremoveAllViews();\n\t\tmAdapter = null;\n\t\tmOnCardSelectedListener = null;\n\t}\n\n\t/**\n\t * Animates the cards to their initial position in the layout.\n\t */\n\tpublic void restoreCards() {\n\t\tmAdapter.resetCards();\n\t}\n\n\t/**\n\t * Intimates the implementing class about the selection of a card\n\t */\n\tpublic interface OnCardSelected {\n\t\tvoid onCardSelected(View v, int position);\n\t}\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/CircularAnim.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.view.View;\nimport android.view.ViewAnimationUtils;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\n\n\npublic class CircularAnim {\n\n    public static final long PERFECT_MILLS = 618;\n    public static final int MINI_RADIUS = 0;\n\n    private static Long sPerfectMills;\n    private static Long sFullActivityPerfectMills;\n    private static Integer sColorOrImageRes;\n\n    private static long getPerfectMills() {\n        if (sPerfectMills != null)\n            return sPerfectMills;\n        else\n            return PERFECT_MILLS;\n    }\n\n    private static long getFullActivityMills() {\n        if (sFullActivityPerfectMills != null)\n            return sFullActivityPerfectMills;\n        else\n            return PERFECT_MILLS;\n    }\n\n    private static int getColorOrImageRes() {\n        if (sColorOrImageRes != null)\n            return sColorOrImageRes;\n        else\n            return android.R.color.white;\n    }\n\n    public static VisibleBuilder show(View animView) {\n        return new VisibleBuilder(animView, true);\n    }\n\n    public static VisibleBuilder hide(View animView) {\n        return new VisibleBuilder(animView, false);\n    }\n\n    public static FullActivityBuilder fullActivity(Activity activity, View triggerView) {\n        return new FullActivityBuilder(activity, triggerView);\n    }\n\n    public static void init(long perfectMills, long fullActivityPerfectMills, int colorOrImageRes) {\n        sPerfectMills = perfectMills;\n        sFullActivityPerfectMills = fullActivityPerfectMills;\n        sColorOrImageRes = colorOrImageRes;\n    }\n\n    public interface OnAnimationEndListener {\n        void onAnimationEnd();\n    }\n\n    @SuppressLint(\"NewApi\")\n    public static class VisibleBuilder {\n\n        private View mAnimView, mTriggerView;\n\n        private Float mStartRadius, mEndRadius;\n\n        private long mDurationMills = getPerfectMills();\n\n        private boolean isShow;\n\n        private OnAnimationEndListener mOnAnimationEndListener;\n\n        public VisibleBuilder(View animView, boolean isShow) {\n            mAnimView = animView;\n            this.isShow = isShow;\n\n            if (isShow) {\n                mStartRadius = MINI_RADIUS + 0F;\n            } else {\n                mEndRadius = MINI_RADIUS + 0F;\n            }\n        }\n\n        public VisibleBuilder triggerView(View triggerView) {\n            mTriggerView = triggerView;\n            return this;\n        }\n\n        public VisibleBuilder startRadius(float startRadius) {\n            mStartRadius = startRadius;\n            return this;\n        }\n\n        public VisibleBuilder endRadius(float endRadius) {\n            mEndRadius = endRadius;\n            return this;\n        }\n\n        public VisibleBuilder duration(long durationMills) {\n            mDurationMills = durationMills;\n            return this;\n        }\n\n        @Deprecated //You can use method - go(OnAnimationEndListener onAnimationEndListener).\n        public VisibleBuilder onAnimationEndListener(OnAnimationEndListener onAnimationEndListener) {\n            mOnAnimationEndListener = onAnimationEndListener;\n            return this;\n        }\n\n        public void go() {\n            go(null);\n        }\n\n        public void go(OnAnimationEndListener onAnimationEndListener) {\n            mOnAnimationEndListener = onAnimationEndListener;\n\n            // 版本判断\n            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {\n                doOnEnd();\n                return;\n            }\n\n            int rippleCX, rippleCY, maxRadius;\n            if (mTriggerView != null) {\n                int[] tvLocation = new int[2];\n                mTriggerView.getLocationInWindow(tvLocation);\n                final int tvCX = tvLocation[0] + mTriggerView.getWidth() / 2;\n                final int tvCY = tvLocation[1] + mTriggerView.getHeight() / 2;\n\n                int[] avLocation = new int[2];\n                mAnimView.getLocationInWindow(avLocation);\n                final int avLX = avLocation[0];\n                final int avTY = avLocation[1];\n\n                int triggerX = Math.max(avLX, tvCX);\n                triggerX = Math.min(triggerX, avLX + mAnimView.getWidth());\n\n                int triggerY = Math.max(avTY, tvCY);\n                triggerY = Math.min(triggerY, avTY + mAnimView.getHeight());\n\n                // 以上全为绝对坐标\n\n                int avW = mAnimView.getWidth();\n                int avH = mAnimView.getHeight();\n\n                rippleCX = triggerX - avLX;\n                rippleCY = triggerY - avTY;\n\n                // 计算水波中心点至 @mAnimView 边界的最大距离\n                int maxW = Math.max(rippleCX, avW - rippleCX);\n                int maxH = Math.max(rippleCY, avH - rippleCY);\n                maxRadius = (int) Math.sqrt(maxW * maxW + maxH * maxH) + 1;\n            } else {\n                rippleCX = (mAnimView.getLeft() + mAnimView.getRight()) / 2;\n                rippleCY = (mAnimView.getTop() + mAnimView.getBottom()) / 2;\n\n                int w = mAnimView.getWidth();\n                int h = mAnimView.getHeight();\n\n                // 勾股定理 & 进一法\n                maxRadius = (int) Math.sqrt(w * w + h * h) + 1;\n            }\n\n            if (isShow && mEndRadius == null)\n                mEndRadius = maxRadius + 0F;\n            else if (!isShow && mStartRadius == null)\n                mStartRadius = maxRadius + 0F;\n\n            try {\n                Animator anim = ViewAnimationUtils.createCircularReveal(\n                        mAnimView, rippleCX, rippleCY, mStartRadius, mEndRadius);\n\n\n                mAnimView.setVisibility(View.VISIBLE);\n                anim.setDuration(mDurationMills);\n\n                anim.addListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        super.onAnimationEnd(animation);\n                        doOnEnd();\n                    }\n                });\n\n                anim.start();\n            } catch (Exception e) {\n                e.printStackTrace();\n                doOnEnd();\n            }\n        }\n\n        private void doOnEnd() {\n            if (isShow)\n                mAnimView.setVisibility(View.VISIBLE);\n            else\n                mAnimView.setVisibility(View.INVISIBLE);\n\n            if (mOnAnimationEndListener != null)\n                mOnAnimationEndListener.onAnimationEnd();\n        }\n\n    }\n\n    @SuppressLint(\"NewApi\")\n    public static class FullActivityBuilder {\n        private Activity mActivity;\n        private View mTriggerView;\n        private float mStartRadius = MINI_RADIUS;\n        private int mColorOrImageRes = getColorOrImageRes();\n        private Long mDurationMills;\n        private OnAnimationEndListener mOnAnimationEndListener;\n        private int mEnterAnim = android.R.anim.fade_in, mExitAnim = android.R.anim.fade_out;\n\n        public FullActivityBuilder(Activity activity, View triggerView) {\n            mActivity = activity;\n            mTriggerView = triggerView;\n        }\n\n        public FullActivityBuilder startRadius(float startRadius) {\n            mStartRadius = startRadius;\n            return this;\n        }\n\n        public FullActivityBuilder colorOrImageRes(int colorOrImageRes) {\n            mColorOrImageRes = colorOrImageRes;\n            return this;\n        }\n\n        public FullActivityBuilder duration(long durationMills) {\n            mDurationMills = durationMills;\n            return this;\n        }\n\n        public FullActivityBuilder overridePendingTransition(int enterAnim, int exitAnim) {\n            mEnterAnim = enterAnim;\n            mExitAnim = exitAnim;\n            return this;\n        }\n\n        public void go(OnAnimationEndListener onAnimationEndListener) {\n            mOnAnimationEndListener = onAnimationEndListener;\n\n            // 版本判断,小于5.0则无动画.\n            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {\n                doOnEnd();\n                return;\n            }\n\n            int[] location = new int[2];\n            mTriggerView.getLocationInWindow(location);\n            final int cx = location[0] + mTriggerView.getWidth() / 2;\n            final int cy = location[1] + mTriggerView.getHeight() / 2;\n            final ImageView view = new ImageView(mActivity);\n            view.setScaleType(ImageView.ScaleType.CENTER_CROP);\n            view.setImageResource(mColorOrImageRes);\n            final ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();\n            int w = decorView.getWidth();\n            int h = decorView.getHeight();\n            decorView.addView(view, w, h);\n\n            int maxW = Math.max(cx, w - cx);\n            int maxH = Math.max(cy, h - cy);\n            final int finalRadius = (int) Math.sqrt(maxW * maxW + maxH * maxH) + 1;\n\n            try {\n                Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, mStartRadius, finalRadius);\n\n                int maxRadius = (int) Math.sqrt(w * w + h * h) + 1;\n                if (mDurationMills == null) {\n                    double rate = 1d * finalRadius / maxRadius;\n                    mDurationMills = (long) (getFullActivityMills() * Math.sqrt(rate));\n                }\n                final long finalDuration = mDurationMills;\n                anim.setDuration((long) (finalDuration * 0.9));\n                anim.addListener(new AnimatorListenerAdapter() {\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        super.onAnimationEnd(animation);\n\n                        doOnEnd();\n\n                        mActivity.overridePendingTransition(mEnterAnim, mExitAnim);\n\n                        mTriggerView.postDelayed(new Runnable() {\n                            @Override\n                            public void run() {\n                                if (mActivity.isFinishing()) return;\n                                try {\n                                    Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy,\n                                            finalRadius, mStartRadius);\n                                    anim.setDuration(finalDuration);\n                                    anim.addListener(new AnimatorListenerAdapter() {\n                                        @Override\n                                        public void onAnimationEnd(Animator animation) {\n                                            super.onAnimationEnd(animation);\n                                            try {\n                                                decorView.removeView(view);\n                                            } catch (Exception e) {\n                                                e.printStackTrace();\n                                            }\n                                        }\n                                    });\n                                    anim.start();\n                                } catch (Exception e) {\n                                    e.printStackTrace();\n                                    try {\n                                        decorView.removeView(view);\n                                    } catch (Exception e1) {\n                                        e1.printStackTrace();\n                                    }\n                                }\n                            }\n                        }, 1000);\n\n                    }\n                });\n                anim.start();\n            } catch (Exception e) {\n                e.printStackTrace();\n                doOnEnd();\n            }\n        }\n\n        private void doOnEnd() {\n            mOnAnimationEndListener.onAnimationEnd();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/DragSelectRecyclerView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.RectF;\nimport android.os.Handler;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport io.virtualapp.R;\n\n/**\n * @author Aidan Follestad (afollestad)\n */\npublic class DragSelectRecyclerView extends RecyclerView {\n\n    private static final boolean LOGGING = false;\n    private static final int AUTO_SCROLL_DELAY = 25;\n    private int mLastDraggedIndex = -1;\n    private DragSelectRecyclerViewAdapter<?> mAdapter;\n    private int mInitialSelection;\n    private boolean mDragSelectActive;\n    private int mMinReached;\n    private int mMaxReached;\n    private int mHotspotHeight;\n    private int mHotspotOffsetTop;\n    private int mHotspotOffsetBottom;\n    private int mHotspotTopBoundStart;\n    private int mHotspotTopBoundEnd;\n    private int mHotspotBottomBoundStart;\n    private int mHotspotBottomBoundEnd;\n    private int mAutoScrollVelocity;\n    private FingerListener mFingerListener;\n    private boolean mInTopHotspot;\n    private boolean mInBottomHotspot;\n    private Handler mAutoScrollHandler;\n    private Runnable mAutoScrollRunnable = new Runnable() {\n        @Override\n        public void run() {\n            if (mAutoScrollHandler == null)\n                return;\n            if (mInTopHotspot) {\n                scrollBy(0, -mAutoScrollVelocity);\n                mAutoScrollHandler.postDelayed(this, AUTO_SCROLL_DELAY);\n            } else if (mInBottomHotspot) {\n                scrollBy(0, mAutoScrollVelocity);\n                mAutoScrollHandler.postDelayed(this, AUTO_SCROLL_DELAY);\n            }\n        }\n    };\n    private RectF mTopBoundRect;\n    private RectF mBottomBoundRect;\n    private Paint mDebugPaint;\n    private boolean mDebugEnabled = false;\n\n    public DragSelectRecyclerView(Context context) {\n        super(context);\n        init(context, null);\n    }\n\n    public DragSelectRecyclerView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs);\n    }\n\n    public DragSelectRecyclerView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init(context, attrs);\n    }\n\n    private static void LOG(String message, Object... args) {\n        //noinspection PointlessBooleanExpression\n        if (!LOGGING) return;\n        if (args != null) {\n            Log.d(\"DragSelectRecyclerView\", String.format(message, args));\n        } else {\n            Log.d(\"DragSelectRecyclerView\", message);\n        }\n    }\n\n    private void init(Context context, AttributeSet attrs) {\n        mAutoScrollHandler = new Handler();\n        final int defaultHotspotHeight = context.getResources().getDimensionPixelSize(R.dimen.dsrv_defaultHotspotHeight);\n\n        if (attrs != null) {\n            TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DragSelectRecyclerView, 0, 0);\n            try {\n                boolean autoScrollEnabled = a.getBoolean(R.styleable.DragSelectRecyclerView_dsrv_autoScrollEnabled, true);\n                if (!autoScrollEnabled) {\n                    mHotspotHeight = -1;\n                    mHotspotOffsetTop = -1;\n                    mHotspotOffsetBottom = -1;\n                    LOG(\"Auto-scroll disabled\");\n                } else {\n                    mHotspotHeight = a.getDimensionPixelSize(\n                            R.styleable.DragSelectRecyclerView_dsrv_autoScrollHotspotHeight, defaultHotspotHeight);\n                    mHotspotOffsetTop = a.getDimensionPixelSize(\n                            R.styleable.DragSelectRecyclerView_dsrv_autoScrollHotspot_offsetTop, 0);\n                    mHotspotOffsetBottom = a.getDimensionPixelSize(\n                            R.styleable.DragSelectRecyclerView_dsrv_autoScrollHotspot_offsetBottom, 0);\n                    LOG(\"Hotspot height = %d\", mHotspotHeight);\n                }\n            } finally {\n                a.recycle();\n            }\n        } else {\n            mHotspotHeight = defaultHotspotHeight;\n            LOG(\"Hotspot height = %d\", mHotspotHeight);\n        }\n    }\n\n    public void setFingerListener(@Nullable FingerListener listener) {\n        this.mFingerListener = listener;\n    }\n\n    @Override\n    protected void onMeasure(int widthSpec, int heightSpec) {\n        super.onMeasure(widthSpec, heightSpec);\n        if (mHotspotHeight > -1) {\n            mHotspotTopBoundStart = mHotspotOffsetTop;\n            mHotspotTopBoundEnd = mHotspotOffsetTop + mHotspotHeight;\n            mHotspotBottomBoundStart = (getMeasuredHeight() - mHotspotHeight) - mHotspotOffsetBottom;\n            mHotspotBottomBoundEnd = getMeasuredHeight() - mHotspotOffsetBottom;\n            LOG(\"RecyclerView height = %d\", getMeasuredHeight());\n            LOG(\"Hotspot top bound = %d to %d\", mHotspotTopBoundStart, mHotspotTopBoundStart);\n            LOG(\"Hotspot bottom bound = %d to %d\", mHotspotBottomBoundStart, mHotspotBottomBoundEnd);\n        }\n    }\n\n    public boolean setDragSelectActive(boolean active, int initialSelection) {\n        if (active && mDragSelectActive) {\n            LOG(\"Drag selection is already active.\");\n            return false;\n        }\n        mLastDraggedIndex = -1;\n        mMinReached = -1;\n        mMaxReached = -1;\n        if (!mAdapter.isIndexSelectable(initialSelection)) {\n            mDragSelectActive = false;\n            mInitialSelection = -1;\n            mLastDraggedIndex = -1;\n            LOG(\"Index %d is not selectable.\", initialSelection);\n            return false;\n        }\n        mAdapter.setSelected(initialSelection, true);\n        mDragSelectActive = active;\n        mInitialSelection = initialSelection;\n        mLastDraggedIndex = initialSelection;\n        if (mFingerListener != null)\n            mFingerListener.onDragSelectFingerAction(true);\n        LOG(\"Drag selection initialized, starting at index %d.\", initialSelection);\n        return true;\n    }\n\n    /**\n     * Use {@link #setAdapter(DragSelectRecyclerViewAdapter)} instead.\n     */\n    @Override\n    @Deprecated\n    public void setAdapter(Adapter adapter) {\n        if (!(adapter instanceof DragSelectRecyclerViewAdapter<?>))\n            throw new IllegalArgumentException(\"Adapter must be a DragSelectRecyclerViewAdapter.\");\n        setAdapter((DragSelectRecyclerViewAdapter<?>) adapter);\n    }\n\n    public void setAdapter(DragSelectRecyclerViewAdapter<?> adapter) {\n        super.setAdapter(adapter);\n        mAdapter = adapter;\n    }\n\n    private int getItemPosition(MotionEvent e) {\n        final View v = findChildViewUnder(e.getX(), e.getY());\n        if (v == null) return NO_POSITION;\n        if (v.getTag() == null || !(v.getTag() instanceof ViewHolder))\n            throw new IllegalStateException(\"Make sure your adapter makes a call to super.onBindViewHolder(), and doesn't override itemView tags.\");\n        final ViewHolder holder = (ViewHolder) v.getTag();\n        return holder.getAdapterPosition();\n    }\n\n    public final void enableDebug() {\n        mDebugEnabled = true;\n        invalidate();\n    }\n\n    @Override\n    public void onDraw(Canvas c) {\n        super.onDraw(c);\n\n        if (mDebugEnabled) {\n            if (mDebugPaint == null) {\n                mDebugPaint = new Paint();\n                mDebugPaint.setColor(Color.BLACK);\n                mDebugPaint.setAntiAlias(true);\n                mDebugPaint.setStyle(Paint.Style.FILL);\n                mTopBoundRect = new RectF(0, mHotspotTopBoundStart, getMeasuredWidth(), mHotspotTopBoundEnd);\n                mBottomBoundRect = new RectF(0, mHotspotBottomBoundStart, getMeasuredWidth(), mHotspotBottomBoundEnd);\n            }\n            c.drawRect(mTopBoundRect, mDebugPaint);\n            c.drawRect(mBottomBoundRect, mDebugPaint);\n        }\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent e) {\n        if (mAdapter.getItemCount() == 0)\n            return super.dispatchTouchEvent(e);\n\n        if (mDragSelectActive) {\n            if (e.getAction() == MotionEvent.ACTION_UP) {\n                mDragSelectActive = false;\n                mInTopHotspot = false;\n                mInBottomHotspot = false;\n                mAutoScrollHandler.removeCallbacks(mAutoScrollRunnable);\n                if (mFingerListener != null)\n                    mFingerListener.onDragSelectFingerAction(false);\n                return true;\n            } else if (e.getAction() == MotionEvent.ACTION_MOVE) {\n                // Check for auto-scroll hotspot\n                if (mHotspotHeight > -1) {\n                    if (e.getY() >= mHotspotTopBoundStart && e.getY() <= mHotspotTopBoundEnd) {\n                        mInBottomHotspot = false;\n                        if (!mInTopHotspot) {\n                            mInTopHotspot = true;\n                            LOG(\"Now in TOP hotspot\");\n                            mAutoScrollHandler.removeCallbacks(mAutoScrollRunnable);\n                            mAutoScrollHandler.postDelayed(mAutoScrollRunnable, AUTO_SCROLL_DELAY);\n                        }\n\n                        final float simulatedFactor = mHotspotTopBoundEnd - mHotspotTopBoundStart;\n                        final float simulatedY = e.getY() - mHotspotTopBoundStart;\n                        mAutoScrollVelocity = (int) (simulatedFactor - simulatedY) / 2;\n\n                        LOG(\"Auto scroll velocity = %d\", mAutoScrollVelocity);\n                    } else if (e.getY() >= mHotspotBottomBoundStart && e.getY() <= mHotspotBottomBoundEnd) {\n                        mInTopHotspot = false;\n                        if (!mInBottomHotspot) {\n                            mInBottomHotspot = true;\n                            LOG(\"Now in BOTTOM hotspot\");\n                            mAutoScrollHandler.removeCallbacks(mAutoScrollRunnable);\n                            mAutoScrollHandler.postDelayed(mAutoScrollRunnable, AUTO_SCROLL_DELAY);\n                        }\n\n                        final float simulatedY = e.getY() + mHotspotBottomBoundEnd;\n                        final float simulatedFactor = mHotspotBottomBoundStart + mHotspotBottomBoundEnd;\n                        mAutoScrollVelocity = (int) (simulatedY - simulatedFactor) / 2;\n\n                        LOG(\"Auto scroll velocity = %d\", mAutoScrollVelocity);\n                    } else if (mInTopHotspot || mInBottomHotspot) {\n                        LOG(\"Left the hotspot\");\n                        mAutoScrollHandler.removeCallbacks(mAutoScrollRunnable);\n                        mInTopHotspot = false;\n                        mInBottomHotspot = false;\n                    }\n                }\n\n                // Drag selection logic\n                // NOTE: DISABLE IT\n//                if (itemPosition != NO_POSITION && mLastDraggedIndex != itemPosition) {\n//                    mLastDraggedIndex = itemPosition;\n//                    if (mMinReached == -1) mMinReached = mLastDraggedIndex;\n//                    if (mMaxReached == -1) mMaxReached = mLastDraggedIndex;\n//                    if (mLastDraggedIndex > mMaxReached)\n//                        mMaxReached = mLastDraggedIndex;\n//                    if (mLastDraggedIndex < mMinReached)\n//                        mMinReached = mLastDraggedIndex;\n//                    if (mAdapter != null)\n//                        mAdapter.selectRange(mInitialSelection, mLastDraggedIndex, mMinReached, mMaxReached);\n//                    if (mInitialSelection == mLastDraggedIndex) {\n//                        mMinReached = mLastDraggedIndex;\n//                        mMaxReached = mLastDraggedIndex;\n//                    }\n//                }\n                return true;\n            }\n        }\n        return super.dispatchTouchEvent(e);\n    }\n\n    public interface FingerListener {\n        void onDragSelectFingerAction(boolean fingerDown);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/DragSelectRecyclerViewAdapter.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.os.Bundle;\nimport android.support.annotation.CallSuper;\nimport android.support.v7.widget.RecyclerView;\n\nimport java.util.ArrayList;\n\n/**\n * @author Aidan Follestad (afollestad)\n */\npublic abstract class DragSelectRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {\n\n    private ArrayList<Integer> mSelectedIndices;\n    private SelectionListener mSelectionListener;\n    private int mLastCount = -1;\n    private int mMaxSelectionCount = -1;\n    protected DragSelectRecyclerViewAdapter() {\n        mSelectedIndices = new ArrayList<>();\n    }\n\n    private void fireSelectionListener() {\n        if (mLastCount == mSelectedIndices.size())\n            return;\n        mLastCount = mSelectedIndices.size();\n        if (mSelectionListener != null)\n            mSelectionListener.onDragSelectionChanged(mLastCount);\n    }\n\n    public void setMaxSelectionCount(int maxSelectionCount) {\n        this.mMaxSelectionCount = maxSelectionCount;\n    }\n\n    public void setSelectionListener(SelectionListener selectionListener) {\n        this.mSelectionListener = selectionListener;\n    }\n\n    public void saveInstanceState(Bundle out) {\n        saveInstanceState(\"selected_indices\", out);\n    }\n\n    public void saveInstanceState(String key, Bundle out) {\n        out.putSerializable(key, mSelectedIndices);\n    }\n\n    public void restoreInstanceState(Bundle in) {\n        restoreInstanceState(\"selected_indices\", in);\n    }\n\n    public void restoreInstanceState(String key, Bundle in) {\n        if (in != null && in.containsKey(key)) {\n            //noinspection unchecked\n            mSelectedIndices = (ArrayList<Integer>) in.getSerializable(key);\n            if (mSelectedIndices == null) mSelectedIndices = new ArrayList<>();\n            else fireSelectionListener();\n        }\n    }\n\n    public final void setSelected(int index, boolean selected) {\n        if (!isIndexSelectable(index))\n            selected = false;\n        if (selected) {\n            if (!mSelectedIndices.contains(index) &&\n                    (mMaxSelectionCount == -1 ||\n                            mSelectedIndices.size() < mMaxSelectionCount)) {\n                mSelectedIndices.add(index);\n                notifyItemChanged(index);\n            }\n        } else if (mSelectedIndices.contains(index)) {\n            mSelectedIndices.remove((Integer) index);\n            notifyItemChanged(index);\n        }\n        fireSelectionListener();\n    }\n\n    public final boolean toggleSelected(int index) {\n        boolean selectedNow = false;\n        if (isIndexSelectable(index)) {\n            if (mSelectedIndices.contains(index)) {\n                mSelectedIndices.remove((Integer) index);\n            } else if (mMaxSelectionCount == -1 ||\n                    mSelectedIndices.size() < mMaxSelectionCount) {\n                mSelectedIndices.add(index);\n                selectedNow = true;\n            }\n            notifyItemChanged(index);\n        }\n        fireSelectionListener();\n        return selectedNow;\n    }\n\n    protected boolean isIndexSelectable(int index) {\n        return true;\n    }\n\n    @CallSuper\n    @Override\n    public void onBindViewHolder(VH holder, int position) {\n        holder.itemView.setTag(holder);\n    }\n\n    public final void selectRange(int from, int to, int min, int max) {\n        if (from == to) {\n            // Finger is back on the initial item, unselect everything else\n            for (int i = min; i <= max; i++) {\n                if (i == from) continue;\n                setSelected(i, false);\n            }\n            fireSelectionListener();\n            return;\n        }\n\n        if (to < from) {\n            // When selecting from one to previous items\n            for (int i = to; i <= from; i++)\n                setSelected(i, true);\n            if (min > -1 && min < to) {\n                // Unselect items that were selected during this drag but no longer are\n                for (int i = min; i < to; i++) {\n                    if (i == from) continue;\n                    setSelected(i, false);\n                }\n            }\n            if (max > -1) {\n                for (int i = from + 1; i <= max; i++)\n                    setSelected(i, false);\n            }\n        } else {\n            // When selecting from one to next items\n            for (int i = from; i <= to; i++)\n                setSelected(i, true);\n            if (max > -1 && max > to) {\n                // Unselect items that were selected during this drag but no longer are\n                for (int i = to + 1; i <= max; i++) {\n                    if (i == from) continue;\n                    setSelected(i, false);\n                }\n            }\n            if (min > -1) {\n                for (int i = min; i < from; i++)\n                    setSelected(i, false);\n            }\n        }\n        fireSelectionListener();\n    }\n\n    public final void selectAll() {\n        int max = getItemCount();\n        mSelectedIndices.clear();\n        for (int i = 0; i < max; i++) {\n            if (isIndexSelectable(i)) {\n                mSelectedIndices.add(i);\n            }\n        }\n        notifyDataSetChanged();\n        fireSelectionListener();\n    }\n\n    public final void clearSelected() {\n        mSelectedIndices.clear();\n        notifyDataSetChanged();\n        fireSelectionListener();\n    }\n\n    public final int getSelectedCount() {\n        return mSelectedIndices.size();\n    }\n\n    public final Integer[] getSelectedIndices() {\n        return mSelectedIndices.toArray(new Integer[mSelectedIndices.size()]);\n    }\n\n    public final boolean isIndexSelected(int index) {\n        return mSelectedIndices.contains(index);\n    }\n\n    public interface SelectionListener {\n        void onDragSelectionChanged(int count);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/EatBeansView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.RectF;\nimport android.util.AttributeSet;\n\n\npublic class EatBeansView extends BaseView {\n\n    int eatSpeed = 5;\n    private Paint mPaint, mPaintEye;\n    private float mWidth = 0f;\n    private float mHigh = 0f;\n    private float mPadding = 5f;\n    private float eatErWidth = 60f;\n    private float eatErPositionX = 0f;\n    private float beansWidth = 10f;\n\n\n    private float mAngle = 34;\n    private float eatErStartAngle = mAngle;\n    private float eatErEndAngle = 360 - 2 * eatErStartAngle;\n\n    public EatBeansView(Context context) {\n        super(context);\n    }\n\n    public EatBeansView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public EatBeansView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        mWidth = getMeasuredWidth();\n        mHigh = getMeasuredHeight();\n    }\n\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        float eatRightX = mPadding + eatErWidth + eatErPositionX;\n        RectF rectF = new RectF(mPadding + eatErPositionX, mHigh / 2 - eatErWidth / 2, eatRightX, mHigh / 2 + eatErWidth / 2);\n        canvas.drawArc(rectF, eatErStartAngle, eatErEndAngle\n                , true, mPaint);\n        canvas.drawCircle(mPadding + eatErPositionX + eatErWidth / 2,\n                mHigh / 2 - eatErWidth / 4,\n                beansWidth / 2, mPaintEye);\n\n        int beansCount = (int) ((mWidth - mPadding * 2 - eatErWidth) / beansWidth / 2);\n        for (int i = 0; i < beansCount; i++) {\n\n            float x = beansCount * i + beansWidth / 2 + mPadding + eatErWidth;\n            if (x > eatRightX) {\n                canvas.drawCircle(x,\n                        mHigh / 2, beansWidth / 2, mPaint);\n            }\n        }\n\n\n    }\n\n    private void initPaint() {\n        mPaint = new Paint();\n        mPaint.setAntiAlias(true);\n        mPaint.setStyle(Paint.Style.FILL);\n        mPaint.setColor(Color.WHITE);\n\n        mPaintEye = new Paint();\n        mPaintEye.setAntiAlias(true);\n        mPaintEye.setStyle(Paint.Style.FILL);\n        mPaintEye.setColor(Color.BLACK);\n\n    }\n\n\n    public void setViewColor(int color) {\n        mPaint.setColor(color);\n        postInvalidate();\n    }\n\n    public void setEyeColor(int color) {\n        mPaintEye.setColor(color);\n        postInvalidate();\n    }\n\n\n    @Override\n    protected void InitPaint() {\n        initPaint();\n    }\n\n    @Override\n    protected void OnAnimationUpdate(ValueAnimator valueAnimator) {\n        float mAnimatedValue = (float) valueAnimator.getAnimatedValue();\n        eatErPositionX = (mWidth - 2 * mPadding - eatErWidth) * mAnimatedValue;\n        eatErStartAngle = mAngle * (1 - (mAnimatedValue * eatSpeed - (int) (mAnimatedValue * eatSpeed)));\n        eatErEndAngle = 360 - eatErStartAngle * 2;\n        invalidate();\n    }\n\n    @Override\n    protected void OnAnimationRepeat(Animator animation) {\n\n    }\n\n    @Override\n    protected int OnStopAnim() {\n        eatErPositionX = 0;\n        postInvalidate();\n        return 1;\n    }\n\n    @Override\n    protected int SetAnimRepeatMode() {\n        return ValueAnimator.RESTART;\n    }\n\n    @Override\n    protected void AnimIsRunning() {\n\n    }\n\n    @Override\n    protected int SetAnimRepeatCount() {\n        return ValueAnimator.INFINITE;\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/Indicator.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.ValueAnimator;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.Drawable;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\n\n\npublic abstract class Indicator extends Drawable implements Animatable {\n\n    private static final Rect ZERO_BOUNDS_RECT = new Rect();\n    protected Rect drawBounds = ZERO_BOUNDS_RECT;\n    private HashMap<ValueAnimator, ValueAnimator.AnimatorUpdateListener> mUpdateListeners = new HashMap<>();\n    private ArrayList<ValueAnimator> mAnimators;\n    private int alpha = 255;\n    private boolean mHasAnimators;\n\n    private Paint mPaint = new Paint();\n\n    public Indicator() {\n        mPaint.setColor(Color.WHITE);\n        mPaint.setStyle(Paint.Style.FILL);\n        mPaint.setAntiAlias(true);\n    }\n\n    public int getColor() {\n        return mPaint.getColor();\n    }\n\n    public void setColor(int color) {\n        mPaint.setColor(color);\n    }\n\n    @Override\n    public int getAlpha() {\n        return alpha;\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n        this.alpha = alpha;\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.OPAQUE;\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter colorFilter) {\n\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        draw(canvas, mPaint);\n    }\n\n    public abstract void draw(Canvas canvas, Paint paint);\n\n    public abstract ArrayList<ValueAnimator> onCreateAnimators();\n\n    @Override\n    public void start() {\n        ensureAnimators();\n\n        if (mAnimators == null) {\n            return;\n        }\n\n        // If the animators has not ended, do nothing.\n        if (isStarted()) {\n            return;\n        }\n        startAnimators();\n        invalidateSelf();\n    }\n\n    private void startAnimators() {\n        for (int i = 0; i < mAnimators.size(); i++) {\n            ValueAnimator animator = mAnimators.get(i);\n\n            //when the animator restart , add the updateListener again because they\n            // was removed by animator stop .\n            ValueAnimator.AnimatorUpdateListener updateListener = mUpdateListeners.get(animator);\n            if (updateListener != null) {\n                animator.addUpdateListener(updateListener);\n            }\n\n            animator.start();\n        }\n    }\n\n    private void stopAnimators() {\n        if (mAnimators != null) {\n            for (ValueAnimator animator : mAnimators) {\n                if (animator != null && animator.isStarted()) {\n                    animator.removeAllUpdateListeners();\n                    animator.end();\n                }\n            }\n        }\n    }\n\n    private void ensureAnimators() {\n        if (!mHasAnimators) {\n            mAnimators = onCreateAnimators();\n            mHasAnimators = true;\n        }\n    }\n\n    @Override\n    public void stop() {\n        stopAnimators();\n    }\n\n    private boolean isStarted() {\n        for (ValueAnimator animator : mAnimators) {\n            return animator.isStarted();\n        }\n        return false;\n    }\n\n    @Override\n    public boolean isRunning() {\n        for (ValueAnimator animator : mAnimators) {\n            return animator.isRunning();\n        }\n        return false;\n    }\n\n    /**\n     * Your should use this to add AnimatorUpdateListener when\n     * create animator , otherwise , animator doesn't work when\n     * the animation restart .\n     *\n     * @param updateListener\n     */\n    public void addUpdateListener(ValueAnimator animator, ValueAnimator.AnimatorUpdateListener updateListener) {\n        mUpdateListeners.put(animator, updateListener);\n    }\n\n    @Override\n    protected void onBoundsChange(Rect bounds) {\n        super.onBoundsChange(bounds);\n        setDrawBounds(bounds);\n    }\n\n    public void setDrawBounds(int left, int top, int right, int bottom) {\n        this.drawBounds = new Rect(left, top, right, bottom);\n    }\n\n    public void postInvalidate() {\n        invalidateSelf();\n    }\n\n    public Rect getDrawBounds() {\n        return drawBounds;\n    }\n\n    public void setDrawBounds(Rect drawBounds) {\n        setDrawBounds(drawBounds.left, drawBounds.top, drawBounds.right, drawBounds.bottom);\n    }\n\n    public int getWidth() {\n        return drawBounds.width();\n    }\n\n    public int getHeight() {\n        return drawBounds.height();\n    }\n\n    public int centerX() {\n        return drawBounds.centerX();\n    }\n\n    public int centerY() {\n        return drawBounds.centerY();\n    }\n\n    public float exactCenterX() {\n        return drawBounds.exactCenterX();\n    }\n\n    public float exactCenterY() {\n        return drawBounds.exactCenterY();\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/LabelView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\n\nimport io.virtualapp.R;\n\npublic class LabelView extends View {\n    private static final int DEFAULT_DEGREES = 45;\n    private String mTextContent;\n    private int mTextColor;\n    private float mTextSize;\n    private boolean mTextBold;\n    private boolean mFillTriangle;\n    private boolean mTextAllCaps;\n    private int mBackgroundColor;\n    private float mMinSize;\n    private float mPadding;\n    private int mGravity;\n    private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private Path mPath = new Path();\n\n    public LabelView(Context context) {\n        this(context, null);\n    }\n\n    public LabelView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n\n        obtainAttributes(context, attrs);\n        mTextPaint.setTextAlign(Paint.Align.CENTER);\n    }\n\n    private void obtainAttributes(Context context, AttributeSet attrs) {\n        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LabelView);\n        mTextContent = ta.getString(R.styleable.LabelView_lv_text);\n        mTextColor = ta.getColor(R.styleable.LabelView_lv_text_color, Color.parseColor(\"#ffffff\"));\n        mTextSize = ta.getDimension(R.styleable.LabelView_lv_text_size, sp2px(11));\n        mTextBold = ta.getBoolean(R.styleable.LabelView_lv_text_bold, true);\n        mTextAllCaps = ta.getBoolean(R.styleable.LabelView_lv_text_all_caps, true);\n        mFillTriangle = ta.getBoolean(R.styleable.LabelView_lv_fill_triangle, false);\n        mBackgroundColor = ta.getColor(R.styleable.LabelView_lv_background_color, Color.parseColor(\"#FF4081\"));\n        mMinSize = ta.getDimension(R.styleable.LabelView_lv_min_size, mFillTriangle ? dp2px(35) : dp2px(50));\n        mPadding = ta.getDimension(R.styleable.LabelView_lv_padding, dp2px(3.5f));\n        mGravity = ta.getInt(R.styleable.LabelView_lv_gravity, Gravity.TOP | Gravity.LEFT);\n        ta.recycle();\n    }\n\n    public String getText() {\n        return mTextContent;\n    }\n\n    public void setText(String text) {\n        mTextContent = text;\n        invalidate();\n    }\n\n    public int getTextColor() {\n        return mTextColor;\n    }\n\n    public void setTextColor(int textColor) {\n        mTextColor = textColor;\n        invalidate();\n    }\n\n    public float getTextSize() {\n        return mTextSize;\n    }\n\n    public void setTextSize(float textSize) {\n        mTextSize = sp2px(textSize);\n        invalidate();\n    }\n\n    public boolean isTextBold() {\n        return mTextBold;\n    }\n\n    public void setTextBold(boolean textBold) {\n        mTextBold = textBold;\n        invalidate();\n    }\n\n    public boolean isFillTriangle() {\n        return mFillTriangle;\n    }\n\n    public void setFillTriangle(boolean fillTriangle) {\n        mFillTriangle = fillTriangle;\n        invalidate();\n    }\n\n    public boolean isTextAllCaps() {\n        return mTextAllCaps;\n    }\n\n    public void setTextAllCaps(boolean textAllCaps) {\n        mTextAllCaps = textAllCaps;\n        invalidate();\n    }\n\n    public int getBgColor() {\n        return mBackgroundColor;\n    }\n\n    public void setBgColor(int backgroundColor) {\n        mBackgroundColor = backgroundColor;\n        invalidate();\n    }\n\n    public float getMinSize() {\n        return mMinSize;\n    }\n\n    public void setMinSize(float minSize) {\n        mMinSize = dp2px(minSize);\n        invalidate();\n    }\n\n    public float getPadding() {\n        return mPadding;\n    }\n\n    public void setPadding(float padding) {\n        mPadding = dp2px(padding);\n        invalidate();\n    }\n\n    public int getGravity() {\n        return mGravity;\n    }\n\n    /**\n     * Gravity.TOP | Gravity.LEFT\n     * Gravity.TOP | Gravity.RIGHT\n     * Gravity.BOTTOM | Gravity.LEFT\n     * Gravity.BOTTOM | Gravity.RIGHT\n     */\n    public void setGravity(int gravity) {\n        mGravity = gravity;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        int size = getHeight();\n\n        mTextPaint.setColor(mTextColor);\n        mTextPaint.setTextSize(mTextSize);\n        mTextPaint.setFakeBoldText(mTextBold);\n        mBackgroundPaint.setColor(mBackgroundColor);\n\n        float textHeight = mTextPaint.descent() - mTextPaint.ascent();\n        if (mFillTriangle) {\n            if (mGravity == (Gravity.TOP | Gravity.LEFT)) {\n                mPath.reset();\n                mPath.moveTo(0, 0);\n                mPath.lineTo(0, size);\n                mPath.lineTo(size, 0);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, true);\n            } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {\n                mPath.reset();\n                mPath.moveTo(size, 0);\n                mPath.lineTo(0, 0);\n                mPath.lineTo(size, size);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawTextWhenFill(size, DEFAULT_DEGREES, canvas, true);\n            } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {\n                mPath.reset();\n                mPath.moveTo(0, size);\n                mPath.lineTo(0, 0);\n                mPath.lineTo(size, size);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawTextWhenFill(size, DEFAULT_DEGREES, canvas, false);\n            } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {\n                mPath.reset();\n                mPath.moveTo(size, size);\n                mPath.lineTo(0, size);\n                mPath.lineTo(size, 0);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, false);\n            }\n        } else {\n            double delta = (textHeight + mPadding * 2) * Math.sqrt(2);\n            if (mGravity == (Gravity.TOP | Gravity.LEFT)) {\n                mPath.reset();\n                mPath.moveTo(0, (float) (size - delta));\n                mPath.lineTo(0, size);\n                mPath.lineTo(size, 0);\n                mPath.lineTo((float) (size - delta), 0);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawText(size, -DEFAULT_DEGREES, canvas, textHeight, true);\n            } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {\n                mPath.reset();\n                mPath.moveTo(0, 0);\n                mPath.lineTo((float) delta, 0);\n                mPath.lineTo(size, (float) (size - delta));\n                mPath.lineTo(size, size);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawText(size, DEFAULT_DEGREES, canvas, textHeight, true);\n            } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {\n                mPath.reset();\n                mPath.moveTo(0, 0);\n                mPath.lineTo(0, (float) delta);\n                mPath.lineTo((float) (size - delta), size);\n                mPath.lineTo(size, size);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawText(size, DEFAULT_DEGREES, canvas, textHeight, false);\n            } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {\n                mPath.reset();\n                mPath.moveTo(0, size);\n                mPath.lineTo((float) delta, size);\n                mPath.lineTo(size, (float) delta);\n                mPath.lineTo(size, 0);\n                mPath.close();\n                canvas.drawPath(mPath, mBackgroundPaint);\n\n                drawText(size, -DEFAULT_DEGREES, canvas, textHeight, false);\n            }\n        }\n    }\n\n    private void drawText(int size, float degrees, Canvas canvas, float textHeight, boolean isTop) {\n        canvas.save();\n        canvas.rotate(degrees, size / 2f, size / 2f);\n        float delta = isTop ? -(textHeight + mPadding * 2) / 2 : (textHeight + mPadding * 2) / 2;\n        float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta;\n        canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent,\n                getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint);\n        canvas.restore();\n    }\n\n    private void drawTextWhenFill(int size, float degrees, Canvas canvas, boolean isTop) {\n        canvas.save();\n        canvas.rotate(degrees, size / 2f, size / 2f);\n        float delta = isTop ? -size / 4 : size / 4;\n        float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta;\n        canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent,\n                getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint);\n        canvas.restore();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int measuredWidth = measureWidth(widthMeasureSpec);\n        setMeasuredDimension(measuredWidth, measuredWidth);\n    }\n\n    /**\n     * 确定View宽度大小\n     */\n    private int measureWidth(int widthMeasureSpec) {\n        int result;\n        int specMode = MeasureSpec.getMode(widthMeasureSpec);\n        int specSize = MeasureSpec.getSize(widthMeasureSpec);\n        if (specMode == MeasureSpec.EXACTLY) {//大小确定直接使用\n            result = specSize;\n        } else {\n            int padding = getPaddingLeft() + getPaddingRight();\n            mTextPaint.setColor(mTextColor);\n            mTextPaint.setTextSize(mTextSize);\n            float textWidth = mTextPaint.measureText(mTextContent + \"\");\n            result = (int) ((padding + (int) textWidth) * Math.sqrt(2));\n            //如果父视图的测量要求为AT_MOST,即限定了一个最大值,则再从系统建议值和自己计算值中去一个较小值\n            if (specMode == MeasureSpec.AT_MOST) {\n                result = Math.min(result, specSize);\n            }\n\n            result = Math.max((int) mMinSize, result);\n        }\n\n        return result;\n    }\n\n    protected int dp2px(float dp) {\n        final float scale = getResources().getDisplayMetrics().density;\n        return (int) (dp * scale + 0.5f);\n    }\n\n    protected int sp2px(float sp) {\n        final float scale = getResources().getDisplayMetrics().scaledDensity;\n        return (int) (sp * scale + 0.5f);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/LauncherIconView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.RectF;\nimport android.support.v7.widget.AppCompatImageView;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.animation.DecelerateInterpolator;\n\nimport io.virtualapp.R;\n\nimport static android.graphics.Canvas.ALL_SAVE_FLAG;\n\npublic class LauncherIconView extends AppCompatImageView implements ShimmerViewBase {\n    private static final int SMOOTH_ANIM_THRESHOLD = 5;\n\n    private static final String TAG = \"LauncherIconView\";\n\n    private ShimmerViewHelper mShimmerViewHelper;\n    private Shimmer mShimmer;\n\n    private float mProgress;\n    private int mHeight;\n    private int mWidth;\n    private int mStrokeWidth;\n    private float mRadius;\n    private float mInterDelta;\n    private int mMaskColor;\n\n    private float mMaxMaskRadius;\n    private float mMaskAnimDelta;\n    private boolean mIsSquare;\n    private boolean mMaskAnimRunning;\n\n    private long mMediumAnimTime;\n\n    private Paint mShimmerPaint;\n    private Paint mPaint;\n    private RectF mProgressOval;\n    private ValueAnimator mInterAnim;\n    private ValueAnimator mProgressAnimator;\n\n    public LauncherIconView(Context context) {\n        super(context);\n        init(context, null);\n    }\n\n    public LauncherIconView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs);\n    }\n\n    public LauncherIconView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs);\n    }\n\n    private void init(Context context, AttributeSet attrs) {\n        mMediumAnimTime = getContext().getResources().getInteger(android.R.integer.config_mediumAnimTime);\n\n        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressImageView);\n        try {\n            this.mProgress = a.getInteger(R.styleable.ProgressImageView_pi_progress, 0);\n            this.mStrokeWidth = a.getDimensionPixelOffset(R.styleable.ProgressImageView_pi_stroke, 8);\n            this.mRadius = a.getDimensionPixelOffset(R.styleable.ProgressImageView_pi_radius, 0);\n            this.mIsSquare = a.getBoolean(R.styleable.ProgressImageView_pi_force_square, false);\n            this.mMaskColor = a.getColor(R.styleable.ProgressImageView_pi_mask_color, Color.argb(180, 0, 0, 0));\n\n            this.mPaint = new Paint();\n            mPaint.setColor(mMaskColor);\n            mPaint.setAntiAlias(true);\n\n            this.mShimmerPaint = new Paint();\n            mShimmerPaint.setColor(Color.WHITE);\n        } finally {\n            a.recycle();\n        }\n        mShimmerViewHelper = new ShimmerViewHelper(this, mShimmerPaint, attrs);\n    }\n\n    private void initParams() {\n        if (mWidth == 0)\n            mWidth = getWidth();\n\n        if (mHeight == 0)\n            mHeight = getHeight();\n\n        if (mWidth != 0 && mHeight != 0) {\n            if (mRadius == 0)\n                mRadius = Math.min(mWidth, mHeight) / 4f;\n\n            if (mMaxMaskRadius == 0)\n                mMaxMaskRadius = (float) (0.5f * Math.sqrt(mWidth * mWidth + mHeight * mHeight));\n\n            if (mProgressOval == null)\n                mProgressOval = new RectF(\n                        mWidth / 2f - mRadius + mStrokeWidth,\n                        mHeight / 2f - mRadius + mStrokeWidth,\n                        mWidth / 2f + mRadius - mStrokeWidth,\n                        mHeight / 2f + mRadius - mStrokeWidth);\n        }\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        if (mShimmerViewHelper != null) {\n            mShimmerViewHelper.onDraw();\n        }\n        super.onDraw(canvas);\n        int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, ALL_SAVE_FLAG);\n\n        initParams();\n\n        if (mProgress < 100) {\n            drawMask(canvas);\n\n            if (mProgress == 0)\n                updateInterAnim(canvas);\n            else\n                drawProgress(canvas);\n        }\n\n        if (mMaskAnimRunning)\n            updateMaskAnim(canvas);\n\n        canvas.restoreToCount(sc);\n    }\n\n    private void drawMask(Canvas canvas) {\n        canvas.drawRect(0, 0, mWidth, mHeight, mPaint);\n    }\n\n    private void drawProgress(Canvas canvas) {\n        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));\n        canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, mPaint);\n        mPaint.setXfermode(null);\n\n        //start angle : -90 ~ 270;sweep Angle : 360 ~ 0;\n        canvas.drawArc(mProgressOval, -90 + mProgress * 3.6f, 360 - mProgress * 3.6f, true, mPaint);\n    }\n\n    private void updateInterAnim(Canvas canvas) {\n//        if (!mInterAnimRunning) mInterDelta = 0.f;\n\n        //outer circle\n        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));\n        canvas.drawCircle(mWidth / 2.f, mHeight / 2.f, mRadius, mPaint);\n        mPaint.setXfermode(null);\n\n        //inner circle\n        canvas.drawCircle(mWidth / 2.f, mHeight / 2.f, mRadius - mInterDelta, mPaint);\n    }\n\n    private void updateMaskAnim(Canvas canvas) {\n        canvas.drawRect(0, 0, mWidth, mHeight, mPaint);\n\n        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));\n        canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius + mMaskAnimDelta, mPaint);//mRatio : 0 ~ mRatio * 1.5\n        mPaint.setXfermode(null);\n    }\n\n    private void startInterAnim(final int progress) {\n        if (mInterAnim != null)\n            mInterAnim.cancel();\n\n        mInterAnim = ValueAnimator.ofFloat(0.f, mStrokeWidth);\n        mInterAnim.setInterpolator(new DecelerateInterpolator());\n        mInterAnim.setDuration(getContext().getResources().getInteger(android.R.integer.config_shortAnimTime));\n        mInterAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                mInterDelta = (float) animation.getAnimatedValue();\n                invalidate();\n            }\n        });\n        mInterAnim.addListener(new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n                super.onAnimationStart(animation);\n//                mInterAnimRunning = true;\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n                super.onAnimationCancel(animation);\n//                mInterAnimRunning = false;\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                super.onAnimationEnd(animation);\n//                mInterAnimRunning = false;\n\n                if (progress > 0)\n                    startProgressAnim(0, progress);\n            }\n        });\n        mInterAnim.start();\n    }\n\n    private void startProgressAnim(float from, float to) {\n        if (mProgressAnimator != null)\n            mProgressAnimator.cancel();\n\n        final boolean isReverse = from > to;\n\n        mProgressAnimator = ValueAnimator.ofFloat(from, to);\n        mProgressAnimator.setInterpolator(new DecelerateInterpolator());\n        mProgressAnimator.setDuration(mMediumAnimTime);\n        mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                mProgress = (float) animation.getAnimatedValue();\n\n                if (0 < mProgress && mProgress < 100)\n                    invalidate();\n                else if (mProgress == 100 && !isReverse)\n                    startMaskAnim();\n            }\n        });\n        mProgressAnimator.start();\n    }\n\n    private void startMaskAnim() {\n        if (mProgressAnimator != null)\n            mProgressAnimator.cancel();\n\n        ValueAnimator animator = ValueAnimator.ofFloat(0.f, mMaxMaskRadius);\n        animator.setInterpolator(new DecelerateInterpolator());\n        animator.setDuration(mMediumAnimTime);\n        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                mMaskAnimRunning = true;\n                mMaskAnimDelta = (float) animation.getAnimatedValue();\n                invalidate();\n            }\n        });\n        animator.addListener(new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n                super.onAnimationStart(animation);\n                mMaskAnimRunning = true;\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n                super.onAnimationCancel(animation);\n                mMaskAnimRunning = false;\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                super.onAnimationEnd(animation);\n                mMaskAnimRunning = false;\n            }\n        });\n        animator.start();\n    }\n\n    /**\n     * get the stroke width.\n     *\n     * @return the stroke width in pixel.\n     */\n    public int getStrokeWidth() {\n        return mStrokeWidth;\n    }\n\n    /**\n     * set the stroke width.default is 8dp.\n     *\n     * @param strokeWidth stroke width in pixel\n     */\n    public void setStrokeWidth(int strokeWidth) {\n        this.mStrokeWidth = strokeWidth;\n        this.mProgressOval = null;\n        invalidate();\n    }\n\n    /**\n     * get the radius of inner progress circle.\n     *\n     * @return the inner circle radius in pixel.\n     */\n    public float getRadius() {\n        return mRadius;\n    }\n\n    /**\n     * set the radius of the inner progress circle.\n     *\n     * @param radius radius in pixel\n     */\n    public void setRadius(float radius) {\n        this.mRadius = radius;\n        this.mProgressOval = null;\n        invalidate();\n    }\n\n    /**\n     * get the color for mask .\n     *\n     * @return the mask color\n     */\n    public int getMaskColor() {\n        return mMaskColor;\n    }\n\n    /**\n     * set the color for mask. Argb will looks better. Default is Color.argb(180,0,0,0)\n     *\n     * @param maskColor the color value.\n     */\n    public void setMaskColor(int maskColor) {\n        mMaskColor = maskColor;\n        mPaint.setColor(mMaskColor);\n        invalidate();\n    }\n\n    /**\n     * get current progress.\n     *\n     * @return current progress value.\n     */\n    public int getProgress() {\n        return (int) mProgress;\n    }\n\n    /**\n     * @param progress the progress ,range [0,100]\n     */\n    public void setProgress(int progress) {\n        setProgress(progress, true);\n    }\n\n    /**\n     * @param progress the progress in [0,100]\n     * @param animate  true to enable smooth animation when progress changed more than 5.\n     */\n    public void setProgress(int progress, boolean animate) {\n        progress = Math.min(Math.max(progress, 0), 100);\n\n        Log.d(TAG, \"setProgress: p:\" + progress + \",mp:\" + mProgress);\n\n        if (Math.abs(progress - mProgress) > SMOOTH_ANIM_THRESHOLD && animate) {\n            if (mProgress == 0) {\n                startInterAnim(progress);\n            } else {\n                startProgressAnim(mProgress, progress);\n            }\n        } else if (progress == 100 && animate) {\n            mProgress = 100;\n            startMaskAnim();\n        } else {\n            mProgress = progress;\n\n            if (mProgress == 0.f)\n                mInterDelta = 0.f;\n\n            invalidate();\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        if (mIsSquare) {\n            int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);\n            int size = measuredWidth == 0 ? MeasureSpec.getSize(heightMeasureSpec) : measuredWidth;\n            setMeasuredDimension(size, size);\n        }\n    }\n\n\n    @Override\n    public float getGradientX() {\n        return mShimmerViewHelper.getGradientX();\n    }\n\n    @Override\n    public void setGradientX(float gradientX) {\n        mShimmerViewHelper.setGradientX(gradientX);\n    }\n\n    @Override\n    public boolean isShimmering() {\n        return mShimmerViewHelper.isShimmering();\n    }\n\n    @Override\n    public void setShimmering(boolean isShimmering) {\n        mShimmerViewHelper.setShimmering(isShimmering);\n    }\n\n    @Override\n    public boolean isSetUp() {\n        return mShimmerViewHelper.isSetUp();\n    }\n\n    @Override\n    public void setAnimationSetupCallback(ShimmerViewHelper.AnimationSetupCallback callback) {\n        mShimmerViewHelper.setAnimationSetupCallback(callback);\n    }\n\n    @Override\n    public int getPrimaryColor() {\n        return mShimmerViewHelper.getPrimaryColor();\n    }\n\n    @Override\n    public void setPrimaryColor(int primaryColor) {\n        mShimmerViewHelper.setPrimaryColor(primaryColor);\n    }\n\n    @Override\n    public int getReflectionColor() {\n        return mShimmerViewHelper.getReflectionColor();\n    }\n\n    @Override\n    public void setReflectionColor(int reflectionColor) {\n        mShimmerViewHelper.setReflectionColor(reflectionColor);\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        if (mShimmerViewHelper != null) {\n            mShimmerViewHelper.onSizeChanged();\n        }\n    }\n\n    public void stopShimmer() {\n        if (mShimmer != null && mShimmer.isAnimating()) {\n            mShimmer.cancel();\n            mShimmer = null;\n        }\n    }\n\n    public void startShimmer() {\n        stopShimmer();\n        mShimmer = new Shimmer();\n        mShimmer.setRepeatCount(1)\n                .setStartDelay(800L)\n                .setDirection(Shimmer.ANIMATION_DIRECTION_LTR)\n                .start(this);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/LoadingIndicatorView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.animation.AnimationUtils;\n\nimport io.virtualapp.R;\n\npublic class LoadingIndicatorView extends View {\n\n    private static final String TAG = \"LoadingIndicatorView\";\n\n    private static final Indicator DEFAULT_INDICATOR = new BallGridBeatIndicator();\n\n    private static final int MIN_SHOW_TIME = 500; // ms\n    private static final int MIN_DELAY = 500; // ms\n    int mMinWidth;\n    int mMaxWidth;\n    int mMinHeight;\n    int mMaxHeight;\n    private long mStartTime = -1;\n    private boolean mPostedHide = false;\n    private boolean mPostedShow = false;\n    private boolean mDismissed = false;\n    private Indicator mIndicator;\n    private int mIndicatorColor;\n    private boolean mShouldStartAnimationDrawable;\n    private final Runnable mDelayedHide = new Runnable() {\n\n        @Override\n        public void run() {\n            mPostedHide = false;\n            mStartTime = -1;\n            setVisibility(View.GONE);\n        }\n    };\n    private final Runnable mDelayedShow = new Runnable() {\n\n        @Override\n        public void run() {\n            mPostedShow = false;\n            if (!mDismissed) {\n                mStartTime = System.currentTimeMillis();\n                setVisibility(View.VISIBLE);\n            }\n        }\n    };\n\n    public LoadingIndicatorView(Context context) {\n        super(context);\n        init(context, null, 0, 0);\n    }\n\n    public LoadingIndicatorView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0, R.style.AVLoadingIndicatorView);\n    }\n\n    public LoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr, R.style.AVLoadingIndicatorView);\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    public LoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n        init(context, attrs, defStyleAttr, R.style.AVLoadingIndicatorView);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        mMinWidth = 24;\n        mMaxWidth = 48;\n        mMinHeight = 24;\n        mMaxHeight = 48;\n\n        final TypedArray a = context.obtainStyledAttributes(\n                attrs, R.styleable.LoadingIndicatorView, defStyleAttr, defStyleRes);\n\n        mMinWidth = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_minWidth, mMinWidth);\n        mMaxWidth = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_maxWidth, mMaxWidth);\n        mMinHeight = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_minHeight, mMinHeight);\n        mMaxHeight = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_maxHeight, mMaxHeight);\n        String indicatorName = a.getString(R.styleable.LoadingIndicatorView_indicatorName);\n        mIndicatorColor = a.getColor(R.styleable.LoadingIndicatorView_indicatorColor, Color.WHITE);\n        setIndicator(indicatorName);\n        if (mIndicator == null) {\n            setIndicator(DEFAULT_INDICATOR);\n        }\n        a.recycle();\n    }\n\n    public Indicator getIndicator() {\n        return mIndicator;\n    }\n\n    /**\n     * You should pay attention to pass this parameter with two way:\n     * for example:\n     * 1. Only class Name,like \"SimpleIndicator\".(This way would use default package name with\n     * \"com.wang.avi.indicators\")\n     * 2. Class name with full package,like \"com.my.android.indicators.SimpleIndicator\".\n     *\n     * @param indicatorName the class must be extend Indicator .\n     */\n    public void setIndicator(String indicatorName) {\n        if (TextUtils.isEmpty(indicatorName)) {\n            return;\n        }\n        StringBuilder drawableClassName = new StringBuilder();\n        if (!indicatorName.contains(\".\")) {\n            String defaultPackageName = getClass().getPackage().getName();\n            drawableClassName.append(defaultPackageName)\n                    .append(\".\");\n        }\n        drawableClassName.append(indicatorName);\n        try {\n            Class<?> drawableClass = Class.forName(drawableClassName.toString());\n            Indicator indicator = (Indicator) drawableClass.newInstance();\n            setIndicator(indicator);\n        } catch (ClassNotFoundException e) {\n            Log.e(TAG, \"Didn't find your class , check the name again !\");\n        } catch (InstantiationException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void setIndicator(Indicator d) {\n        if (mIndicator != d) {\n            if (mIndicator != null) {\n                mIndicator.setCallback(null);\n                unscheduleDrawable(mIndicator);\n            }\n\n            mIndicator = d;\n            //need to set indicator color again if you didn't specified when you update the indicator .\n            setIndicatorColor(mIndicatorColor);\n            if (d != null) {\n                d.setCallback(this);\n            }\n            postInvalidate();\n        }\n    }\n\n    /**\n     * setIndicatorColor(0xFF00FF00)\n     * or\n     * setIndicatorColor(Color.BLUE)\n     * or\n     * setIndicatorColor(Color.parseColor(\"#FF4081\"))\n     * or\n     * setIndicatorColor(0xFF00FF00)\n     * or\n     * setIndicatorColor(getResources().getColor(android.R.color.black))\n     *\n     * @param color\n     */\n    public void setIndicatorColor(int color) {\n        this.mIndicatorColor = color;\n        mIndicator.setColor(color);\n    }\n\n    public void smoothToShow() {\n        startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in));\n        setVisibility(VISIBLE);\n    }\n\n    public void smoothToHide() {\n        startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_out));\n        setVisibility(GONE);\n    }\n\n    public void hide() {\n        mDismissed = true;\n        removeCallbacks(mDelayedShow);\n        long diff = System.currentTimeMillis() - mStartTime;\n        if (diff >= MIN_SHOW_TIME || mStartTime == -1) {\n            // The progress spinner has been shown long enough\n            // OR was not shown yet. If it wasn't shown yet,\n            // it will just never be shown.\n            setVisibility(View.GONE);\n        } else {\n            // The progress spinner is shown, but not long enough,\n            // so put a delayed message in to hide it when its been\n            // shown long enough.\n            if (!mPostedHide) {\n                postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);\n                mPostedHide = true;\n            }\n        }\n    }\n\n    public void show() {\n        // Reset the start time.\n        mStartTime = -1;\n        mDismissed = false;\n        removeCallbacks(mDelayedHide);\n        if (!mPostedShow) {\n            postDelayed(mDelayedShow, MIN_DELAY);\n            mPostedShow = true;\n        }\n    }\n\n    @Override\n    protected boolean verifyDrawable(Drawable who) {\n        return who == mIndicator\n                || super.verifyDrawable(who);\n    }\n\n    void startAnimation() {\n        if (getVisibility() != VISIBLE) {\n            return;\n        }\n\n        if (mIndicator instanceof Animatable) {\n            mShouldStartAnimationDrawable = true;\n        }\n        postInvalidate();\n    }\n\n    void stopAnimation() {\n        if (mIndicator instanceof Animatable) {\n            mIndicator.stop();\n            mShouldStartAnimationDrawable = false;\n        }\n        postInvalidate();\n    }\n\n    @Override\n    public void setVisibility(int v) {\n        if (getVisibility() != v) {\n            super.setVisibility(v);\n            if (v == GONE || v == INVISIBLE) {\n                stopAnimation();\n            } else {\n                startAnimation();\n            }\n        }\n    }\n\n    @Override\n    protected void onVisibilityChanged(View changedView, int visibility) {\n        super.onVisibilityChanged(changedView, visibility);\n        if (visibility == GONE || visibility == INVISIBLE) {\n            stopAnimation();\n        } else {\n            startAnimation();\n        }\n    }\n\n    @Override\n    public void invalidateDrawable(Drawable dr) {\n        if (verifyDrawable(dr)) {\n            final Rect dirty = dr.getBounds();\n            final int scrollX = getScrollX() + getPaddingLeft();\n            final int scrollY = getScrollY() + getPaddingTop();\n\n            invalidate(dirty.left + scrollX, dirty.top + scrollY,\n                    dirty.right + scrollX, dirty.bottom + scrollY);\n        } else {\n            super.invalidateDrawable(dr);\n        }\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        updateDrawableBounds(w, h);\n    }\n\n    private void updateDrawableBounds(int w, int h) {\n        // onDraw will translate the canvas so we draw starting at 0,0.\n        // Subtract out padding for the purposes of the calculations below.\n        w -= getPaddingRight() + getPaddingLeft();\n        h -= getPaddingTop() + getPaddingBottom();\n\n        int right = w;\n        int bottom = h;\n        int top = 0;\n        int left = 0;\n\n        if (mIndicator != null) {\n            // Maintain aspect ratio. Certain kinds of animated drawables\n            // get very confused otherwise.\n            final int intrinsicWidth = mIndicator.getIntrinsicWidth();\n            final int intrinsicHeight = mIndicator.getIntrinsicHeight();\n            final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;\n            final float boundAspect = (float) w / h;\n            if (intrinsicAspect != boundAspect) {\n                if (boundAspect > intrinsicAspect) {\n                    // New width is larger. Make it smaller to match height.\n                    final int width = (int) (h * intrinsicAspect);\n                    left = (w - width) / 2;\n                    right = left + width;\n                } else {\n                    // New height is larger. Make it smaller to match width.\n                    final int height = (int) (w * (1 / intrinsicAspect));\n                    top = (h - height) / 2;\n                    bottom = top + height;\n                }\n            }\n            mIndicator.setBounds(left, top, right, bottom);\n        }\n    }\n\n    @Override\n    protected synchronized void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        drawTrack(canvas);\n    }\n\n    void drawTrack(Canvas canvas) {\n        final Drawable d = mIndicator;\n        if (d != null) {\n            // Translate canvas so a indeterminate circular progress bar with padding\n            // rotates properly in its animation\n            final int saveCount = canvas.save();\n\n            canvas.translate(getPaddingLeft(), getPaddingTop());\n\n            d.draw(canvas);\n            canvas.restoreToCount(saveCount);\n\n            if (mShouldStartAnimationDrawable && d instanceof Animatable) {\n                ((Animatable) d).start();\n                mShouldStartAnimationDrawable = false;\n            }\n        }\n    }\n\n    @Override\n    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int dw = 0;\n        int dh = 0;\n\n        final Drawable d = mIndicator;\n        if (d != null) {\n            dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));\n            dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));\n        }\n\n        updateDrawableState();\n\n        dw += getPaddingLeft() + getPaddingRight();\n        dh += getPaddingTop() + getPaddingBottom();\n\n        final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0);\n        final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0);\n        setMeasuredDimension(measuredWidth, measuredHeight);\n    }\n\n    @Override\n    protected void drawableStateChanged() {\n        super.drawableStateChanged();\n        updateDrawableState();\n    }\n\n    private void updateDrawableState() {\n        final int[] state = getDrawableState();\n        if (mIndicator != null && mIndicator.isStateful()) {\n            mIndicator.setState(state);\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    @Override\n    public void drawableHotspotChanged(float x, float y) {\n        super.drawableHotspotChanged(x, y);\n\n        if (mIndicator != null) {\n            mIndicator.setHotspot(x, y);\n        }\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        startAnimation();\n        removeCallbacks();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        stopAnimation();\n        // This should come after stopAnimation(), otherwise an invalidate message remains in the\n        // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation\n        super.onDetachedFromWindow();\n        removeCallbacks();\n    }\n\n    private void removeCallbacks() {\n        removeCallbacks(mDelayedHide);\n        removeCallbacks(mDelayedShow);\n    }\n\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/MarqueeTextView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.support.v7.widget.AppCompatTextView;\nimport android.util.AttributeSet;\n\npublic class MarqueeTextView extends AppCompatTextView {\n\n    private boolean isStop = false;\n\n    public MarqueeTextView(Context context) {\n        super(context);\n    }\n\n    public MarqueeTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public boolean isFocused() {\n        if (this.isStop) {\n            return super.isFocused();\n        }\n        return true;\n    }\n\n    public void stopScroll() {\n        this.isStop = true;\n    }\n\n    public void start() {\n        this.isStop = false;\n    }\n\n    protected void onDetachedFromWindow() {\n        stopScroll();\n        super.onDetachedFromWindow();\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/MaterialRippleLayout.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.util.Property;\nimport android.util.TypedValue;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.animation.AccelerateInterpolator;\nimport android.view.animation.DecelerateInterpolator;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.AdapterView;\nimport android.widget.FrameLayout;\n\nimport io.virtualapp.R;\n\nimport static android.view.GestureDetector.SimpleOnGestureListener;\nimport static android.view.ViewGroup.LayoutParams.MATCH_PARENT;\n\npublic class MaterialRippleLayout extends FrameLayout {\n\n    private static final int     DEFAULT_DURATION        = 350;\n    private static final int     DEFAULT_FADE_DURATION   = 75;\n    private static final float   DEFAULT_DIAMETER_DP     = 35;\n    private static final float   DEFAULT_ALPHA           = 0.2f;\n    private static final int     DEFAULT_COLOR           = Color.BLACK;\n    private static final int     DEFAULT_BACKGROUND      = Color.TRANSPARENT;\n    private static final boolean DEFAULT_HOVER           = true;\n    private static final boolean DEFAULT_DELAY_CLICK     = true;\n    private static final boolean DEFAULT_PERSISTENT      = false;\n    private static final boolean DEFAULT_SEARCH_ADAPTER  = false;\n    private static final boolean DEFAULT_RIPPLE_OVERLAY  = false;\n    private static final int     DEFAULT_ROUNDED_CORNERS = 0;\n\n    private static final int  FADE_EXTRA_DELAY = 50;\n    private static final long HOVER_DURATION   = 2500;\n\n    private final Paint paint  = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private final Rect  bounds = new Rect();\n\n    private int      rippleColor;\n    private boolean  rippleOverlay;\n    private boolean  rippleHover;\n    private int      rippleDiameter;\n    private int      rippleDuration;\n    private int      rippleAlpha;\n    private boolean  rippleDelayClick;\n    private int      rippleFadeDuration;\n    private boolean  ripplePersistent;\n    private Drawable rippleBackground;\n    private boolean  rippleInAdapter;\n    private float    rippleRoundedCorners;\n\n    private float radius;\n\n    private AdapterView parentAdapter;\n    private View        childView;\n\n    private AnimatorSet    rippleAnimator;\n    private ObjectAnimator hoverAnimator;\n\n    private Point currentCoords  = new Point();\n    private Point previousCoords = new Point();\n\n    private int layerType;\n\n    private boolean eventCancelled;\n    private boolean prepressed;\n    private int     positionInAdapter;\n\n    private GestureDetector   gestureDetector;\n    private PerformClickEvent pendingClickEvent;\n    private PressedEvent      pendingPressEvent;\n    private boolean hasPerformedLongPress;\n    /*\n     * Animations\n     */\n    private Property<MaterialRippleLayout, Float> radiusProperty\n        = new Property<MaterialRippleLayout, Float>(Float.class, \"radius\") {\n        @Override\n        public Float get(MaterialRippleLayout object) {\n            return object.getRadius();\n        }\n\n        @Override\n        public void set(MaterialRippleLayout object, Float value) {\n            object.setRadius(value);\n        }\n    };\n    private Property<MaterialRippleLayout, Integer> circleAlphaProperty\n        = new Property<MaterialRippleLayout, Integer>(Integer.class, \"rippleAlpha\") {\n        @Override\n        public Integer get(MaterialRippleLayout object) {\n            return object.getRippleAlpha();\n        }\n\n        @Override\n        public void set(MaterialRippleLayout object, Integer value) {\n            object.setRippleAlpha(value);\n        }\n    };\n    private SimpleOnGestureListener longClickListener = new GestureDetector.SimpleOnGestureListener() {\n        public void onLongPress(MotionEvent e) {\n            hasPerformedLongPress = childView.performLongClick();\n            if (hasPerformedLongPress) {\n                if (rippleHover) {\n                    startRipple(null);\n                }\n                cancelPressedEvent();\n            }\n        }\n\n        @Override\n        public boolean onDown(MotionEvent e) {\n            hasPerformedLongPress = false;\n            return super.onDown(e);\n        }\n    };\n\n\n    public MaterialRippleLayout(Context context) {\n        this(context, null, 0);\n    }\n\n    public MaterialRippleLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialRippleLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n\n        setWillNotDraw(false);\n        gestureDetector = new GestureDetector(context, longClickListener);\n\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MaterialRippleLayout);\n        rippleColor = a.getColor(R.styleable.MaterialRippleLayout_mrl_rippleColor, DEFAULT_COLOR);\n        rippleDiameter = a.getDimensionPixelSize(\n            R.styleable.MaterialRippleLayout_mrl_rippleDimension,\n            (int) dpToPx(getResources(), DEFAULT_DIAMETER_DP)\n        );\n        rippleOverlay = a.getBoolean(R.styleable.MaterialRippleLayout_mrl_rippleOverlay, DEFAULT_RIPPLE_OVERLAY);\n        rippleHover = a.getBoolean(R.styleable.MaterialRippleLayout_mrl_rippleHover, DEFAULT_HOVER);\n        rippleDuration = a.getInt(R.styleable.MaterialRippleLayout_mrl_rippleDuration, DEFAULT_DURATION);\n        rippleAlpha = (int) (255 * a.getFloat(R.styleable.MaterialRippleLayout_mrl_rippleAlpha, DEFAULT_ALPHA));\n        rippleDelayClick = a.getBoolean(R.styleable.MaterialRippleLayout_mrl_rippleDelayClick, DEFAULT_DELAY_CLICK);\n        rippleFadeDuration = a.getInteger(R.styleable.MaterialRippleLayout_mrl_rippleFadeDuration, DEFAULT_FADE_DURATION);\n        rippleBackground = new ColorDrawable(a.getColor(R.styleable.MaterialRippleLayout_mrl_rippleBackground, DEFAULT_BACKGROUND));\n        ripplePersistent = a.getBoolean(R.styleable.MaterialRippleLayout_mrl_ripplePersistent, DEFAULT_PERSISTENT);\n        rippleInAdapter = a.getBoolean(R.styleable.MaterialRippleLayout_mrl_rippleInAdapter, DEFAULT_SEARCH_ADAPTER);\n        rippleRoundedCorners = a.getDimensionPixelSize(R.styleable.MaterialRippleLayout_mrl_rippleRoundedCorners, DEFAULT_ROUNDED_CORNERS);\n\n        a.recycle();\n\n        paint.setColor(rippleColor);\n        paint.setAlpha(rippleAlpha);\n\n        enableClipPathSupportIfNecessary();\n    }\n\n    public static RippleBuilder on(View view) {\n        return new RippleBuilder(view);\n    }\n\n    static float dpToPx(Resources resources, float dp) {\n        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends View> T getChildView() {\n        return (T) childView;\n    }\n\n    @Override\n    public final void addView(View child, int index, ViewGroup.LayoutParams params) {\n        if (getChildCount() > 0) {\n            throw new IllegalStateException(\"MaterialRippleLayout can host only one child\");\n        }\n        //noinspection unchecked\n        childView = child;\n        super.addView(child, index, params);\n    }\n\n    @Override\n    public void setOnClickListener(OnClickListener onClickListener) {\n        if (childView == null) {\n            throw new IllegalStateException(\"MaterialRippleLayout must have a child view to handle clicks\");\n        }\n        childView.setOnClickListener(onClickListener);\n    }\n\n    @Override\n    public void setOnLongClickListener(OnLongClickListener onClickListener) {\n        if (childView == null) {\n            throw new IllegalStateException(\"MaterialRippleLayout must have a child view to handle clicks\");\n        }\n        childView.setOnLongClickListener(onClickListener);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent event) {\n        return !findClickableViewInChild(childView, (int) event.getX(), (int) event.getY());\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        boolean superOnTouchEvent = super.onTouchEvent(event);\n\n        if (!isEnabled() || !childView.isEnabled()) return superOnTouchEvent;\n\n        boolean isEventInBounds = bounds.contains((int) event.getX(), (int) event.getY());\n\n        if (isEventInBounds) {\n            previousCoords.set(currentCoords.x, currentCoords.y);\n            currentCoords.set((int) event.getX(), (int) event.getY());\n        }\n\n        boolean gestureResult = gestureDetector.onTouchEvent(event);\n        if (gestureResult || hasPerformedLongPress) {\n            return true;\n        } else {\n            int action = event.getActionMasked();\n            switch (action) {\n                case MotionEvent.ACTION_UP:\n                    pendingClickEvent = new PerformClickEvent();\n\n                    if (prepressed) {\n                        childView.setPressed(true);\n                        postDelayed(\n                            new Runnable() {\n                                @Override public void run() {\n                                    childView.setPressed(false);\n                                }\n                            }, ViewConfiguration.getPressedStateDuration());\n                    }\n\n                    if (isEventInBounds) {\n                        startRipple(pendingClickEvent);\n                    } else if (!rippleHover) {\n                        setRadius(0);\n                    }\n                    if (!rippleDelayClick && isEventInBounds) {\n                        pendingClickEvent.run();\n                    }\n                    cancelPressedEvent();\n                    break;\n                case MotionEvent.ACTION_DOWN:\n                    setPositionInAdapter();\n                    eventCancelled = false;\n                    pendingPressEvent = new PressedEvent(event);\n                    if (isInScrollingContainer()) {\n                        cancelPressedEvent();\n                        prepressed = true;\n                        postDelayed(pendingPressEvent, ViewConfiguration.getTapTimeout());\n                    } else {\n                        pendingPressEvent.run();\n                    }\n                    break;\n                case MotionEvent.ACTION_CANCEL:\n                    if (rippleInAdapter) {\n                        // dont use current coords in adapter since they tend to jump drastically on scroll\n                        currentCoords.set(previousCoords.x, previousCoords.y);\n                        previousCoords = new Point();\n                    }\n                    childView.onTouchEvent(event);\n                    if (rippleHover) {\n                        if (!prepressed) {\n                            startRipple(null);\n                        }\n                    } else {\n                        childView.setPressed(false);\n                    }\n                    cancelPressedEvent();\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (rippleHover) {\n                        if (isEventInBounds && !eventCancelled) {\n                            invalidate();\n                        } else if (!isEventInBounds) {\n                            startRipple(null);\n                        }\n                    }\n\n                    if (!isEventInBounds) {\n                        cancelPressedEvent();\n                        if (hoverAnimator != null) {\n                            hoverAnimator.cancel();\n                        }\n                        childView.onTouchEvent(event);\n                        eventCancelled = true;\n                    }\n                    break;\n            }\n            return true;\n        }\n    }\n\n    private void cancelPressedEvent() {\n        if (pendingPressEvent != null) {\n            removeCallbacks(pendingPressEvent);\n            prepressed = false;\n        }\n    }\n\n    private void startHover() {\n        if (eventCancelled) return;\n\n        if (hoverAnimator != null) {\n            hoverAnimator.cancel();\n        }\n        final float radius = (float) (Math.sqrt(Math.pow(getWidth(), 2) + Math.pow(getHeight(), 2)) * 1.2f);\n        hoverAnimator = ObjectAnimator.ofFloat(this, radiusProperty, rippleDiameter, radius)\n            .setDuration(HOVER_DURATION);\n        hoverAnimator.setInterpolator(new LinearInterpolator());\n        hoverAnimator.start();\n    }\n\n    private void startRipple(final Runnable animationEndRunnable) {\n        if (eventCancelled) return;\n\n        float endRadius = getEndRadius();\n\n        cancelAnimations();\n\n        rippleAnimator = new AnimatorSet();\n        rippleAnimator.addListener(new AnimatorListenerAdapter() {\n            @Override public void onAnimationEnd(Animator animation) {\n                if (!ripplePersistent) {\n                    setRadius(0);\n                    setRippleAlpha(rippleAlpha);\n                }\n                if (animationEndRunnable != null && rippleDelayClick) {\n                    animationEndRunnable.run();\n                }\n                childView.setPressed(false);\n            }\n        });\n\n        ObjectAnimator ripple = ObjectAnimator.ofFloat(this, radiusProperty, radius, endRadius);\n        ripple.setDuration(rippleDuration);\n        ripple.setInterpolator(new DecelerateInterpolator());\n        ObjectAnimator fade = ObjectAnimator.ofInt(this, circleAlphaProperty, rippleAlpha, 0);\n        fade.setDuration(rippleFadeDuration);\n        fade.setInterpolator(new AccelerateInterpolator());\n        fade.setStartDelay(rippleDuration - rippleFadeDuration - FADE_EXTRA_DELAY);\n\n        if (ripplePersistent) {\n            rippleAnimator.play(ripple);\n        } else if (getRadius() > endRadius) {\n            fade.setStartDelay(0);\n            rippleAnimator.play(fade);\n        } else {\n            rippleAnimator.playTogether(ripple, fade);\n        }\n        rippleAnimator.start();\n    }\n\n    private void cancelAnimations() {\n        if (rippleAnimator != null) {\n            rippleAnimator.cancel();\n            rippleAnimator.removeAllListeners();\n        }\n\n        if (hoverAnimator != null) {\n            hoverAnimator.cancel();\n        }\n    }\n\n    private float getEndRadius() {\n        final int width = getWidth();\n        final int height = getHeight();\n\n        final int halfWidth = width / 2;\n        final int halfHeight = height / 2;\n\n        final float radiusX = halfWidth > currentCoords.x ? width - currentCoords.x : currentCoords.x;\n        final float radiusY = halfHeight > currentCoords.y ? height - currentCoords.y : currentCoords.y;\n\n        return (float) Math.sqrt(Math.pow(radiusX, 2) + Math.pow(radiusY, 2)) * 1.2f;\n    }\n\n    private boolean isInScrollingContainer() {\n        ViewParent p = getParent();\n        while (p != null && p instanceof ViewGroup) {\n            if (((ViewGroup) p).shouldDelayChildPressedState()) {\n                return true;\n            }\n            p = p.getParent();\n        }\n        return false;\n    }\n\n    private AdapterView findParentAdapterView() {\n        if (parentAdapter != null) {\n            return parentAdapter;\n        }\n        ViewParent current = getParent();\n        while (true) {\n            if (current instanceof AdapterView) {\n                parentAdapter = (AdapterView) current;\n                return parentAdapter;\n            } else {\n                try {\n                    current = current.getParent();\n                } catch (NullPointerException npe) {\n                    throw new RuntimeException(\"Could not find a parent AdapterView\");\n                }\n            }\n        }\n    }\n\n    private void setPositionInAdapter() {\n        if (rippleInAdapter) {\n            positionInAdapter = findParentAdapterView().getPositionForView(MaterialRippleLayout.this);\n        }\n    }\n\n    private boolean adapterPositionChanged() {\n        if (rippleInAdapter) {\n            int newPosition = findParentAdapterView().getPositionForView(MaterialRippleLayout.this);\n            final boolean changed = newPosition != positionInAdapter;\n            positionInAdapter = newPosition;\n            if (changed) {\n                cancelPressedEvent();\n                cancelAnimations();\n                childView.setPressed(false);\n                setRadius(0);\n            }\n            return changed;\n        }\n        return false;\n    }\n\n    private boolean findClickableViewInChild(View view, int x, int y) {\n        if (view instanceof ViewGroup) {\n            ViewGroup viewGroup = (ViewGroup) view;\n            for (int i = 0; i < viewGroup.getChildCount(); i++) {\n                View child = viewGroup.getChildAt(i);\n                final Rect rect = new Rect();\n                child.getHitRect(rect);\n\n                final boolean contains = rect.contains(x, y);\n                if (contains) {\n                    return findClickableViewInChild(child, x - rect.left, y - rect.top);\n                }\n            }\n        } else if (view != childView) {\n            return (view.isEnabled() && (view.isClickable() || view.isLongClickable() || view.isFocusableInTouchMode()));\n        }\n\n        return view.isFocusableInTouchMode();\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        bounds.set(0, 0, w, h);\n        rippleBackground.setBounds(bounds);\n    }\n\n    @Override\n    public boolean isInEditMode() {\n        return true;\n    }\n\n    /*\n     * Drawing\n     */\n    @Override\n    public void draw(Canvas canvas) {\n        final boolean positionChanged = adapterPositionChanged();\n        if (rippleOverlay) {\n            if (!positionChanged) {\n                rippleBackground.draw(canvas);\n            }\n            super.draw(canvas);\n            if (!positionChanged) {\n                if (rippleRoundedCorners != 0) {\n                    Path clipPath = new Path();\n                    RectF rect = new RectF(0, 0, canvas.getWidth(), canvas.getHeight());\n                    clipPath.addRoundRect(rect, rippleRoundedCorners, rippleRoundedCorners, Path.Direction.CW);\n                    canvas.clipPath(clipPath);\n                }\n                canvas.drawCircle(currentCoords.x, currentCoords.y, radius, paint);\n            }\n        } else {\n            if (!positionChanged) {\n                rippleBackground.draw(canvas);\n                canvas.drawCircle(currentCoords.x, currentCoords.y, radius, paint);\n            }\n            super.draw(canvas);\n        }\n    }\n\n    private float getRadius() {\n        return radius;\n    }\n\n    public void setRadius(float radius) {\n        this.radius = radius;\n        invalidate();\n    }\n\n    public int getRippleAlpha() {\n        return paint.getAlpha();\n    }\n\n    public void setRippleAlpha(Integer rippleAlpha) {\n        paint.setAlpha(rippleAlpha);\n        invalidate();\n    }\n\n    /*\n    * Accessor\n     */\n    public void setRippleColor(int rippleColor) {\n        this.rippleColor = rippleColor;\n        paint.setColor(rippleColor);\n        paint.setAlpha(rippleAlpha);\n        invalidate();\n    }\n\n    public void setRippleOverlay(boolean rippleOverlay) {\n        this.rippleOverlay = rippleOverlay;\n    }\n\n    public void setRippleDiameter(int rippleDiameter) {\n        this.rippleDiameter = rippleDiameter;\n    }\n\n    public void setRippleDuration(int rippleDuration) {\n        this.rippleDuration = rippleDuration;\n    }\n\n    public void setRippleBackground(int color) {\n        rippleBackground = new ColorDrawable(color);\n        rippleBackground.setBounds(bounds);\n        invalidate();\n    }\n\n    public void setRippleHover(boolean rippleHover) {\n        this.rippleHover = rippleHover;\n    }\n\n    public void setRippleDelayClick(boolean rippleDelayClick) {\n        this.rippleDelayClick = rippleDelayClick;\n    }\n\n    public void setRippleFadeDuration(int rippleFadeDuration) {\n        this.rippleFadeDuration = rippleFadeDuration;\n    }\n\n    public void setRipplePersistent(boolean ripplePersistent) {\n        this.ripplePersistent = ripplePersistent;\n    }\n\n    public void setRippleInAdapter(boolean rippleInAdapter) {\n        this.rippleInAdapter = rippleInAdapter;\n    }\n\n    public void setRippleRoundedCorners(int rippleRoundedCorner) {\n        this.rippleRoundedCorners = rippleRoundedCorner;\n        enableClipPathSupportIfNecessary();\n    }\n\n    public void setDefaultRippleAlpha(float alpha) {\n        this.rippleAlpha = (int) (255 * alpha);\n        paint.setAlpha(rippleAlpha);\n        invalidate();\n    }\n\n    public void performRipple() {\n        currentCoords = new Point(getWidth() / 2, getHeight() / 2);\n        startRipple(null);\n    }\n\n    public void performRipple(Point anchor) {\n        currentCoords = new Point(anchor.x, anchor.y);\n        startRipple(null);\n    }\n\n    /**\n     * {@link Canvas#clipPath(Path)} is not supported in hardware accelerated layers\n     * before API 18. Use software layer instead\n     * <p/>\n     * https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported\n     */\n    private void enableClipPathSupportIfNecessary() {\n        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            if (rippleRoundedCorners != 0) {\n                layerType = getLayerType();\n                setLayerType(LAYER_TYPE_SOFTWARE, null);\n            } else {\n                setLayerType(layerType, null);\n            }\n        }\n    }\n\n    public static class RippleBuilder {\n\n        private final Context context;\n        private final View    child;\n\n        private int     rippleColor         = DEFAULT_COLOR;\n        private boolean rippleOverlay       = DEFAULT_RIPPLE_OVERLAY;\n        private boolean rippleHover         = DEFAULT_HOVER;\n        private float   rippleDiameter      = DEFAULT_DIAMETER_DP;\n        private int     rippleDuration      = DEFAULT_DURATION;\n        private float   rippleAlpha         = DEFAULT_ALPHA;\n        private boolean rippleDelayClick    = DEFAULT_DELAY_CLICK;\n        private int     rippleFadeDuration  = DEFAULT_FADE_DURATION;\n        private boolean ripplePersistent    = DEFAULT_PERSISTENT;\n        private int     rippleBackground    = DEFAULT_BACKGROUND;\n        private boolean rippleSearchAdapter = DEFAULT_SEARCH_ADAPTER;\n        private float   rippleRoundedCorner = DEFAULT_ROUNDED_CORNERS;\n\n        public RippleBuilder(View child) {\n            this.child = child;\n            this.context = child.getContext();\n        }\n\n        public RippleBuilder rippleColor(int color) {\n            this.rippleColor = color;\n            return this;\n        }\n\n        public RippleBuilder rippleOverlay(boolean overlay) {\n            this.rippleOverlay = overlay;\n            return this;\n        }\n\n        public RippleBuilder rippleHover(boolean hover) {\n            this.rippleHover = hover;\n            return this;\n        }\n\n        public RippleBuilder rippleDiameterDp(int diameterDp) {\n            this.rippleDiameter = diameterDp;\n            return this;\n        }\n\n        public RippleBuilder rippleDuration(int duration) {\n            this.rippleDuration = duration;\n            return this;\n        }\n\n        public RippleBuilder rippleAlpha(float alpha) {\n            this.rippleAlpha = alpha;\n            return this;\n        }\n\n        public RippleBuilder rippleDelayClick(boolean delayClick) {\n            this.rippleDelayClick = delayClick;\n            return this;\n        }\n\n        public RippleBuilder rippleFadeDuration(int fadeDuration) {\n            this.rippleFadeDuration = fadeDuration;\n            return this;\n        }\n\n        public RippleBuilder ripplePersistent(boolean persistent) {\n            this.ripplePersistent = persistent;\n            return this;\n        }\n\n        public RippleBuilder rippleBackground(int color) {\n            this.rippleBackground = color;\n            return this;\n        }\n\n        public RippleBuilder rippleInAdapter(boolean inAdapter) {\n            this.rippleSearchAdapter = inAdapter;\n            return this;\n        }\n\n        public RippleBuilder rippleRoundedCorners(int radiusDp) {\n            this.rippleRoundedCorner = radiusDp;\n            return this;\n        }\n\n        public MaterialRippleLayout create() {\n            MaterialRippleLayout layout = new MaterialRippleLayout(context);\n            layout.setRippleColor(rippleColor);\n            layout.setDefaultRippleAlpha(rippleAlpha);\n            layout.setRippleDelayClick(rippleDelayClick);\n            layout.setRippleDiameter((int) dpToPx(context.getResources(), rippleDiameter));\n            layout.setRippleDuration(rippleDuration);\n            layout.setRippleFadeDuration(rippleFadeDuration);\n            layout.setRippleHover(rippleHover);\n            layout.setRipplePersistent(ripplePersistent);\n            layout.setRippleOverlay(rippleOverlay);\n            layout.setRippleBackground(rippleBackground);\n            layout.setRippleInAdapter(rippleSearchAdapter);\n            layout.setRippleRoundedCorners((int) dpToPx(context.getResources(), rippleRoundedCorner));\n\n            ViewGroup.LayoutParams params = child.getLayoutParams();\n            ViewGroup parent = (ViewGroup) child.getParent();\n            int index = 0;\n\n            if (parent != null && parent instanceof MaterialRippleLayout) {\n                throw new IllegalStateException(\"MaterialRippleLayout could not be created: parent of the view already is a MaterialRippleLayout\");\n            }\n\n            if (parent != null) {\n                index = parent.indexOfChild(child);\n                parent.removeView(child);\n            }\n\n            layout.addView(child, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));\n\n            if (parent != null) {\n                parent.addView(layout, index, params);\n            }\n\n            return layout;\n        }\n    }\n\n    /*\n     * Helper\n     */\n    private class PerformClickEvent implements Runnable {\n\n        @Override public void run() {\n            if (hasPerformedLongPress) return;\n\n            // if parent is an AdapterView, try to call its ItemClickListener\n            if (getParent() instanceof AdapterView) {\n                // try clicking direct child first\n                if (!childView.performClick())\n                    // if it did not handle it dispatch to adapterView\n                    clickAdapterView((AdapterView) getParent());\n            } else if (rippleInAdapter) {\n                // find adapter view\n                clickAdapterView(findParentAdapterView());\n            } else {\n                // otherwise, just perform click on child\n                childView.performClick();\n            }\n        }\n\n        private void clickAdapterView(AdapterView parent) {\n            final int position = parent.getPositionForView(MaterialRippleLayout.this);\n            final long itemId = parent.getAdapter() != null\n                ? parent.getAdapter().getItemId(position)\n                : 0;\n            if (position != AdapterView.INVALID_POSITION) {\n                parent.performItemClick(MaterialRippleLayout.this, position, itemId);\n            }\n        }\n    }\n\n    /*\n     * Builder\n     */\n\n    private final class PressedEvent implements Runnable {\n\n        private final MotionEvent event;\n\n        public PressedEvent(MotionEvent event) {\n            this.event = event;\n        }\n\n        @Override\n        public void run() {\n            prepressed = false;\n            childView.setLongClickable(false);//prevent the child's long click,let's the ripple layout call it's performLongClick\n            childView.onTouchEvent(event);\n            childView.setPressed(true);\n            if (rippleHover) {\n                startHover();\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/RippleButton.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.RadialGradient;\nimport android.graphics.Rect;\nimport android.graphics.Shader;\nimport android.os.Build;\nimport android.support.v7.widget.AppCompatButton;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.animation.AccelerateDecelerateInterpolator;\n\nimport com.nineoldandroids.animation.Animator;\nimport com.nineoldandroids.animation.ObjectAnimator;\nimport com.nineoldandroids.view.ViewHelper;\n\nimport io.virtualapp.R;\n\n@SuppressLint(\"ClickableViewAccessibility\")\npublic class RippleButton extends AppCompatButton {\n\n    private float mDownX;\n    private float mDownY;\n    private float mAlphaFactor;\n    private float mDensity;\n    private float mRadius;\n    private float mMaxRadius;\n\n    private int mRippleColor;\n    private boolean mIsAnimating = false;\n    private boolean mHover = true;\n\n    private RadialGradient mRadialGradient;\n    private Paint mPaint;\n    private ObjectAnimator mRadiusAnimator;\n    private boolean mAnimationIsCancel;\n    private Rect mRect;\n    private Path mPath = new Path();\n\n    public RippleButton(Context context) {\n        this(context, null);\n    }\n\n    public RippleButton(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public RippleButton(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n        TypedArray a = context.obtainStyledAttributes(attrs,\n                R.styleable.RippleButton);\n        mRippleColor = a.getColor(R.styleable.RippleButton_rippleColor,\n                mRippleColor);\n        mAlphaFactor = a.getFloat(R.styleable.RippleButton_alphaFactor,\n                mAlphaFactor);\n        mHover = a.getBoolean(R.styleable.RippleButton_hover, mHover);\n        a.recycle();\n    }\n\n    private int dp(int dp) {\n        return (int) (dp * mDensity + 0.5f);\n    }\n\n    public void init() {\n        mDensity = getContext().getResources().getDisplayMetrics().density;\n\n        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        mPaint.setAlpha(100);\n        setRippleColor(Color.BLACK, 0.2f);\n\n    }\n\n    public void setRippleColor(int rippleColor, float alphaFactor) {\n        mRippleColor = rippleColor;\n        mAlphaFactor = alphaFactor;\n    }\n\n    public void setHover(boolean enabled) {\n        mHover = enabled;\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        mMaxRadius = (float) Math.sqrt(w * w + h * h);\n    }\n\n    @Override\n    public boolean onTouchEvent(final MotionEvent event) {\n        Log.d(\"TouchEvent\", String.valueOf(event.getActionMasked()));\n        Log.d(\"mIsAnimating\", String.valueOf(mIsAnimating));\n        Log.d(\"mAnimationIsCancel\", String.valueOf(mAnimationIsCancel));\n        boolean superResult = super.onTouchEvent(event);\n        if (event.getActionMasked() == MotionEvent.ACTION_DOWN\n                && this.isEnabled() && mHover) {\n            mRect = new Rect(getLeft(), getTop(), getRight(), getBottom());\n            mAnimationIsCancel = false;\n            mDownX = event.getX();\n            mDownY = event.getY();\n\n            mRadiusAnimator = ObjectAnimator.ofFloat(this, \"radius\", 0, dp(50))\n                    .setDuration(400);\n            mRadiusAnimator\n                    .setInterpolator(new AccelerateDecelerateInterpolator());\n            mRadiusAnimator.addListener(new Animator.AnimatorListener() {\n                @Override\n                public void onAnimationStart(Animator animator) {\n                    mIsAnimating = true;\n                }\n\n                @Override\n                public void onAnimationEnd(Animator animator) {\n                    setRadius(0);\n                    ViewHelper.setAlpha(RippleButton.this, 1);\n                    mIsAnimating = false;\n                }\n\n                @Override\n                public void onAnimationCancel(Animator animator) {\n\n                }\n\n                @Override\n                public void onAnimationRepeat(Animator animator) {\n\n                }\n            });\n            mRadiusAnimator.start();\n            if (!superResult) {\n                return true;\n            }\n        } else if (event.getActionMasked() == MotionEvent.ACTION_MOVE\n                && this.isEnabled() && mHover) {\n            mDownX = event.getX();\n            mDownY = event.getY();\n\n            // Cancel the ripple animation when moved outside\n            if (mAnimationIsCancel = !mRect.contains(\n                    getLeft() + (int) event.getX(),\n                    getTop() + (int) event.getY())) {\n                setRadius(0);\n            } else {\n                setRadius(dp(50));\n            }\n            if (!superResult) {\n                return true;\n            }\n        } else if (event.getActionMasked() == MotionEvent.ACTION_UP\n                && !mAnimationIsCancel && this.isEnabled()) {\n            mDownX = event.getX();\n            mDownY = event.getY();\n\n            final float tempRadius = (float) Math.sqrt(mDownX * mDownX + mDownY\n                    * mDownY);\n            float targetRadius = Math.max(tempRadius, mMaxRadius);\n\n            if (mIsAnimating) {\n                mRadiusAnimator.cancel();\n            }\n            mRadiusAnimator = ObjectAnimator.ofFloat(this, \"radius\", dp(50),\n                    targetRadius);\n            mRadiusAnimator.setDuration(500);\n            mRadiusAnimator\n                    .setInterpolator(new AccelerateDecelerateInterpolator());\n            mRadiusAnimator.addListener(new Animator.AnimatorListener() {\n                @Override\n                public void onAnimationStart(Animator animator) {\n                    mIsAnimating = true;\n                }\n\n                @Override\n                public void onAnimationEnd(Animator animator) {\n                    setRadius(0);\n                    ViewHelper.setAlpha(RippleButton.this, 1);\n                    mIsAnimating = false;\n                }\n\n                @Override\n                public void onAnimationCancel(Animator animator) {\n\n                }\n\n                @Override\n                public void onAnimationRepeat(Animator animator) {\n\n                }\n            });\n            mRadiusAnimator.start();\n            if (!superResult) {\n                return true;\n            }\n        }\n        return superResult;\n    }\n\n    public int adjustAlpha(int color, float factor) {\n        int alpha = Math.round(Color.alpha(color) * factor);\n        int red = Color.red(color);\n        int green = Color.green(color);\n        int blue = Color.blue(color);\n        return Color.argb(alpha, red, green, blue);\n    }\n\n    public void setRadius(final float radius) {\n        mRadius = radius;\n        if (mRadius > 0) {\n            mRadialGradient = new RadialGradient(mDownX, mDownY, mRadius,\n                    adjustAlpha(mRippleColor, mAlphaFactor), mRippleColor,\n                    Shader.TileMode.MIRROR);\n            mPaint.setShader(mRadialGradient);\n        }\n        invalidate();\n    }\n\n    @Override\n    protected void onDraw(final Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (isInEditMode()) {\n            return;\n        }\n\n        canvas.save(Canvas.CLIP_SAVE_FLAG);\n\n        mPath.reset();\n        mPath.addCircle(mDownX, mDownY, mRadius, Path.Direction.CW);\n\n        canvas.clipPath(mPath);\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)\n            canvas.restore();\n\n        canvas.drawCircle(mDownX, mDownY, mRadius, mPaint);\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/ShadowProperty.java",
    "content": "package io.virtualapp.widgets;\n\npublic class ShadowProperty {\n    public static final int ALL = 0x1111;\n    public static final int LEFT = 0x0001;\n    public static final int TOP = 0x0010;\n    public static final int RIGHT = 0x0100;\n    public static final int BOTTOM = 0x1000;\n\n    /**\n     * 阴影颜色\n     */\n    private int shadowColor;\n    /**\n     * 阴影半径\n     */\n    private int shadowRadius;\n    /**\n     * 阴影x偏移\n     */\n    private int shadowDx;\n    /**\n     * 阴影y偏移\n     */\n    private int shadowDy;\n\n    /**\n     * 阴影边\n     */\n    private int shadowSide = ALL;\n\n    public int getShadowSide() {\n        return shadowSide;\n    }\n\n    public ShadowProperty setShadowSide(int shadowSide) {\n        this.shadowSide = shadowSide;\n        return this;\n    }\n\n    public int getShadowOffset() {\n        return getShadowOffsetHalf() * 2;\n    }\n\n    public int getShadowOffsetHalf() {\n        return 0 >= shadowRadius ? 0 : Math.max(shadowDx, shadowDy) + shadowRadius;\n    }\n\n    public int getShadowColor() {\n        return shadowColor;\n    }\n\n    public ShadowProperty setShadowColor(int shadowColor) {\n        this.shadowColor = shadowColor;\n        return this;\n    }\n\n    public int getShadowRadius() {\n        return shadowRadius;\n    }\n\n    public ShadowProperty setShadowRadius(int shadowRadius) {\n        this.shadowRadius = shadowRadius;\n        return this;\n    }\n\n    public int getShadowDx() {\n        return shadowDx;\n    }\n\n    public ShadowProperty setShadowDx(int shadowDx) {\n        this.shadowDx = shadowDx;\n        return this;\n    }\n\n    public int getShadowDy() {\n        return shadowDy;\n    }\n\n    public ShadowProperty setShadowDy(int shadowDy) {\n        this.shadowDy = shadowDy;\n        return this;\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/ShadowViewDrawable.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.PixelFormat;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Drawable;\nimport android.support.annotation.NonNull;\n\npublic class ShadowViewDrawable extends Drawable {\n    private Paint paint;\n\n    private RectF bounds = new RectF();\n\n    private int width;\n    private int height;\n\n    private ShadowProperty shadowProperty;\n    private int shadowOffset;\n\n    private RectF drawRect;\n\n    private float rx;\n    private float ry;\n    private PorterDuffXfermode srcOut = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);\n\n    public ShadowViewDrawable(ShadowProperty shadowProperty, int color, float rx, float ry) {\n        this.shadowProperty = shadowProperty;\n        shadowOffset = this.shadowProperty.getShadowOffset();\n\n        this.rx = rx;\n        this.ry = ry;\n\n        paint = new Paint();\n        paint.setAntiAlias(true);\n        /**\n         * 解决旋转时的锯齿问题\n         */\n        paint.setFilterBitmap(true);\n        paint.setDither(true);\n        paint.setStyle(Paint.Style.FILL);\n        paint.setColor(color);\n        /**\n         * 设置阴影\n         */\n        paint.setShadowLayer(shadowProperty.getShadowRadius(), shadowProperty.getShadowDx(), shadowProperty.getShadowDy(), shadowProperty.getShadowColor());\n\n        drawRect = new RectF();\n    }\n\n    @Override\n    protected void onBoundsChange(Rect bounds) {\n        super.onBoundsChange(bounds);\n        if (bounds.right - bounds.left > 0 && bounds.bottom - bounds.top > 0) {\n            this.bounds.left = bounds.left;\n            this.bounds.right = bounds.right;\n            this.bounds.top = bounds.top;\n            this.bounds.bottom = bounds.bottom;\n            width = (int) (this.bounds.right - this.bounds.left);\n            height = (int) (this.bounds.bottom - this.bounds.top);\n\n\n            int shadowSide = shadowProperty.getShadowSide();\n            int left = (shadowSide & ShadowProperty.LEFT) == ShadowProperty.LEFT ? shadowOffset : 0;\n            int top = (shadowSide & ShadowProperty.TOP) == ShadowProperty.TOP ? shadowOffset : 0;\n            int right = width - ((shadowSide & ShadowProperty.RIGHT) == ShadowProperty.RIGHT ? shadowOffset : 0);\n            int bottom = height - ((shadowSide & ShadowProperty.BOTTOM) == ShadowProperty.BOTTOM ? shadowOffset : 0);\n\n            drawRect = new RectF(left, top, right, bottom);\n\n\n            invalidateSelf();\n\n        }\n    }\n\n    @Override\n    public void draw(@NonNull Canvas canvas) {\n        paint.setXfermode(null);\n        canvas.drawRoundRect(\n                drawRect,\n                rx, ry,\n                paint\n        );\n        paint.setXfermode(srcOut);\n        canvas.drawRoundRect(drawRect, rx, ry, paint);\n    }\n\n    public ShadowViewDrawable setColor(int color) {\n        paint.setColor(color);\n        return this;\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter cf) {\n\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.UNKNOWN;\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/Shimmer.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.ObjectAnimator;\nimport android.animation.ValueAnimator;\nimport android.os.Build;\nimport android.view.View;\n\npublic class Shimmer {\n\n    public static final int ANIMATION_DIRECTION_LTR = 0;\n    public static final int ANIMATION_DIRECTION_RTL = 1;\n\n    private static final int DEFAULT_REPEAT_COUNT = ValueAnimator.INFINITE;\n    private static final long DEFAULT_DURATION = 1000;\n    private static final long DEFAULT_START_DELAY = 0;\n    private static final int DEFAULT_DIRECTION = ANIMATION_DIRECTION_LTR;\n\n    private int repeatCount;\n    private long duration;\n    private long startDelay;\n    private int direction;\n    private Animator.AnimatorListener animatorListener;\n\n    private ObjectAnimator animator;\n\n    public Shimmer() {\n        repeatCount = DEFAULT_REPEAT_COUNT;\n        duration = DEFAULT_DURATION;\n        startDelay = DEFAULT_START_DELAY;\n        direction = DEFAULT_DIRECTION;\n    }\n\n    public int getRepeatCount() {\n        return repeatCount;\n    }\n\n    public Shimmer setRepeatCount(int repeatCount) {\n        this.repeatCount = repeatCount;\n        return this;\n    }\n\n    public long getDuration() {\n        return duration;\n    }\n\n    public Shimmer setDuration(long duration) {\n        this.duration = duration;\n        return this;\n    }\n\n    public long getStartDelay() {\n        return startDelay;\n    }\n\n    public Shimmer setStartDelay(long startDelay) {\n        this.startDelay = startDelay;\n        return this;\n    }\n\n    public int getDirection() {\n        return direction;\n    }\n\n    public Shimmer setDirection(int direction) {\n\n        if (direction != ANIMATION_DIRECTION_LTR && direction != ANIMATION_DIRECTION_RTL) {\n            throw new IllegalArgumentException(\"The animation direction must be either ANIMATION_DIRECTION_LTR or ANIMATION_DIRECTION_RTL\");\n        }\n\n        this.direction = direction;\n        return this;\n    }\n\n    public Animator.AnimatorListener getAnimatorListener() {\n        return animatorListener;\n    }\n\n    public Shimmer setAnimatorListener(Animator.AnimatorListener animatorListener) {\n        this.animatorListener = animatorListener;\n        return this;\n    }\n\n    public <V extends View & ShimmerViewBase> void start(final V shimmerView) {\n\n        if (isAnimating()) {\n            return;\n        }\n\n        final Runnable animate = new Runnable() {\n            @Override\n            public void run() {\n\n                shimmerView.setShimmering(true);\n\n                float fromX = 0;\n                float toX = shimmerView.getWidth();\n                if (direction == ANIMATION_DIRECTION_RTL) {\n                    fromX = shimmerView.getWidth();\n                    toX = 0;\n                }\n\n                animator = ObjectAnimator.ofFloat(shimmerView, \"gradientX\", fromX, toX);\n                animator.setRepeatCount(repeatCount);\n                animator.setDuration(duration);\n                animator.setStartDelay(startDelay);\n                animator.addListener(new Animator.AnimatorListener() {\n                    @Override\n                    public void onAnimationStart(Animator animation) {\n                    }\n\n                    @Override\n                    public void onAnimationEnd(Animator animation) {\n                        shimmerView.setShimmering(false);\n\n                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n                            shimmerView.postInvalidate();\n                        } else {\n                            shimmerView.postInvalidateOnAnimation();\n                        }\n\n                        animator = null;\n                    }\n\n                    @Override\n                    public void onAnimationCancel(Animator animation) {\n\n                    }\n\n                    @Override\n                    public void onAnimationRepeat(Animator animation) {\n\n                    }\n                });\n\n                if (animatorListener != null) {\n                    animator.addListener(animatorListener);\n                }\n\n                animator.start();\n            }\n        };\n\n        if (!shimmerView.isSetUp()) {\n            shimmerView.setAnimationSetupCallback(new ShimmerViewHelper.AnimationSetupCallback() {\n                @Override\n                public void onSetupAnimation(final View target) {\n                    animate.run();\n                }\n            });\n        } else {\n            animate.run();\n        }\n    }\n\n    public void cancel() {\n        if (animator != null) {\n            animator.cancel();\n        }\n    }\n\n    public boolean isAnimating() {\n        return animator != null && animator.isRunning();\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/ShimmerViewBase.java",
    "content": "package io.virtualapp.widgets;\n\npublic interface ShimmerViewBase {\n\n    float getGradientX();\n\n    void setGradientX(float gradientX);\n\n    boolean isShimmering();\n\n    void setShimmering(boolean isShimmering);\n\n    boolean isSetUp();\n\n    void setAnimationSetupCallback(ShimmerViewHelper.AnimationSetupCallback callback);\n\n    int getPrimaryColor();\n\n    void setPrimaryColor(int primaryColor);\n\n    int getReflectionColor();\n\n    void setReflectionColor(int reflectionColor);\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/ShimmerViewHelper.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.content.res.TypedArray;\nimport android.graphics.LinearGradient;\nimport android.graphics.Matrix;\nimport android.graphics.Paint;\nimport android.graphics.Shader;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport io.virtualapp.R;\n\npublic class ShimmerViewHelper {\n\n    private static final int DEFAULT_REFLECTION_COLOR = 0xFFFFFFFF;\n    private View view;\n    private Paint paint;\n    // center position of the gradient\n    private float gradientX;\n    // shader applied on the text view\n    // only null until the first global layout\n    private LinearGradient linearGradient;\n    // shader's local matrix\n    // never null\n    private Matrix linearGradientMatrix;\n    private int primaryColor;\n    // shimmer reflection color\n    private int reflectionColor;\n    // true when animating\n    private boolean isShimmering;\n    // true after first global layout\n    private boolean isSetUp;\n    // callback called after first global layout\n    private AnimationSetupCallback callback;\n\n    public ShimmerViewHelper(View view, Paint paint, AttributeSet attributeSet) {\n        this.view = view;\n        this.paint = paint;\n        init(attributeSet);\n    }\n\n    public float getGradientX() {\n        return gradientX;\n    }\n\n    public void setGradientX(float gradientX) {\n        this.gradientX = gradientX;\n        view.invalidate();\n    }\n\n    public boolean isShimmering() {\n        return isShimmering;\n    }\n\n    public void setShimmering(boolean isShimmering) {\n        this.isShimmering = isShimmering;\n    }\n\n    public boolean isSetUp() {\n        return isSetUp;\n    }\n\n    public void setAnimationSetupCallback(AnimationSetupCallback callback) {\n        this.callback = callback;\n    }\n\n    public int getPrimaryColor() {\n        return primaryColor;\n    }\n\n    public void setPrimaryColor(int primaryColor) {\n        this.primaryColor = primaryColor;\n        if (isSetUp) {\n            resetLinearGradient();\n        }\n    }\n\n    public int getReflectionColor() {\n        return reflectionColor;\n    }\n\n    public void setReflectionColor(int reflectionColor) {\n        this.reflectionColor = reflectionColor;\n        if (isSetUp) {\n            resetLinearGradient();\n        }\n    }\n\n    private void init(AttributeSet attributeSet) {\n\n        reflectionColor = DEFAULT_REFLECTION_COLOR;\n\n        if (attributeSet != null) {\n            TypedArray a = view.getContext().obtainStyledAttributes(attributeSet, R.styleable.ShimmerView, 0, 0);\n            if (a != null) {\n                try {\n                    reflectionColor = a.getColor(R.styleable.ShimmerView_reflectionColor, DEFAULT_REFLECTION_COLOR);\n                } catch (Exception e) {\n                    android.util.Log.e(\"ShimmerTextView\", \"Error while creating the view:\", e);\n                } finally {\n                    a.recycle();\n                }\n            }\n        }\n\n        linearGradientMatrix = new Matrix();\n    }\n\n    private void resetLinearGradient() {\n\n        // our gradient is a simple linear gradient from textColor to reflectionColor. its axis is at the center\n        // when it's outside of the view, the outer color (textColor) will be repeated (Shader.TileMode.CLAMP)\n        // initially, the linear gradient is positioned on the left side of the view\n        linearGradient = new LinearGradient(-view.getWidth(), 0, 0, 0,\n                new int[]{\n                        primaryColor,\n                        reflectionColor,\n                        primaryColor,\n                },\n                new float[]{\n                        0,\n                        0.5f,\n                        1\n                },\n                Shader.TileMode.CLAMP\n        );\n\n        paint.setShader(linearGradient);\n    }\n\n    protected void onSizeChanged() {\n\n        resetLinearGradient();\n\n        if (!isSetUp) {\n            isSetUp = true;\n\n            if (callback != null) {\n                callback.onSetupAnimation(view);\n            }\n        }\n    }\n\n    /**\n     * content of the wrapping view's onDraw(Canvas)\n     * MUST BE CALLED BEFORE SUPER STATEMENT\n     */\n    public void onDraw() {\n\n        // only draw the shader gradient over the text while animating\n        if (isShimmering) {\n\n            // first onDraw() when shimmering\n            if (paint.getShader() == null) {\n                paint.setShader(linearGradient);\n            }\n\n            // translate the shader local matrix\n            linearGradientMatrix.setTranslate(2 * gradientX, 0);\n\n            // this is required in order to invalidate the shader's position\n            linearGradient.setLocalMatrix(linearGradientMatrix);\n\n        } else {\n            // we're not animating, remove the shader from the paint\n            paint.setShader(null);\n        }\n\n    }\n\n    public interface AnimationSetupCallback {\n        void onSetupAnimation(View target);\n    }\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/TwoGearsView.java",
    "content": "package io.virtualapp.widgets;\n\nimport android.animation.Animator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\n\n\npublic class TwoGearsView extends BaseView {\n    ValueAnimator valueAnimator = null;\n    float mAnimatedValue = 0f;\n    float hypotenuse = 0f;\n    float smallRingCenterX = 0f;\n    float smallRingCenterY = 0f;\n    float bigRingCenterX = 0f;\n    float bigRingCenterY = 0f;\n    private float mWidth = 0f;\n    private Paint mPaint, mPaintAxle;\n    private Paint mPaintRing;\n    private float mPadding = 0f;\n    private float mWheelLength;\n    private int mWheelSmallSpace = 10;\n    private int mWheelBigSpace = 8;\n\n    public TwoGearsView(Context context) {\n        super(context);\n    }\n\n    public TwoGearsView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public TwoGearsView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        if (getMeasuredWidth() > getHeight())\n            mWidth = getMeasuredHeight();\n        else\n            mWidth = getMeasuredWidth();\n    }\n\n\n    private void drawSmallRing(Canvas canvas) {\n\n        hypotenuse = (float) (mWidth * Math.sqrt(2));\n        smallRingCenterX = (float) ((hypotenuse / 6.f) * Math.cos(45 * Math.PI / 180f));\n        smallRingCenterY = (float) ((hypotenuse / 6.f) * Math.sin(45 * Math.PI / 180f));\n        mPaintRing.setStrokeWidth(dip2px(1.0f));\n        canvas.drawCircle(mPadding + smallRingCenterX, smallRingCenterY + mPadding, smallRingCenterX, mPaintRing);\n        mPaintRing.setStrokeWidth(dip2px(1.5f));\n        canvas.drawCircle(mPadding + smallRingCenterX, smallRingCenterY + mPadding, smallRingCenterX / 2, mPaintRing);\n    }\n\n\n    private void drawSmallGear(Canvas canvas) {\n\n        mPaint.setStrokeWidth(dip2px(1));\n\n        for (int i = 0; i < 360; i = i + mWheelSmallSpace) {\n            int angle = (int) (mAnimatedValue * mWheelSmallSpace + i);\n            float x3 = (float) ((smallRingCenterX) * Math.cos(angle * Math.PI / 180f));\n            float y3 = (float) ((smallRingCenterY) * Math.sin(angle * Math.PI / 180f));\n            float x4 = (float) ((smallRingCenterX + mWheelLength) * Math.cos(angle * Math.PI / 180f));\n            float y4 = (float) ((smallRingCenterY + mWheelLength) * Math.sin(angle * Math.PI / 180f));\n\n            canvas.drawLine(mPadding + smallRingCenterX - x4,\n                    smallRingCenterY + mPadding - y4,\n                    smallRingCenterX + mPadding - x3,\n                    smallRingCenterY + mPadding - y3,\n                    mPaint);\n\n        }\n\n    }\n\n    private void drawBigGear(Canvas canvas) {\n        bigRingCenterX = (float) ((hypotenuse / 2.f) * Math.cos(45 * Math.PI / 180f));\n        bigRingCenterY = (float) ((hypotenuse / 2.f) * Math.sin(45 * Math.PI / 180f));\n        float strokeWidth = dip2px(1.5f) / 4;\n        mPaint.setStrokeWidth(dip2px(1.5f));\n        for (int i = 0; i < 360; i = i + mWheelBigSpace) {\n            int angle = (int) (360 - (mAnimatedValue * mWheelBigSpace + i));\n            float x3 = (float) ((bigRingCenterX - smallRingCenterX) * Math.cos(angle * Math.PI / 180f));\n            float y3 = (float) ((bigRingCenterY - smallRingCenterY) * Math.sin(angle * Math.PI / 180f));\n            float x4 = (float) ((bigRingCenterX - smallRingCenterX + mWheelLength) * Math.cos(angle * Math.PI / 180f));\n            float y4 = (float) ((bigRingCenterY - smallRingCenterY + mWheelLength) * Math.sin(angle * Math.PI / 180f));\n            canvas.drawLine(bigRingCenterX + mPadding - x4 + mWheelLength * 2 + strokeWidth,\n                    bigRingCenterY + mPadding - y4 + mWheelLength * 2 + strokeWidth,\n                    bigRingCenterX + mPadding - x3 + mWheelLength * 2 + strokeWidth,\n                    bigRingCenterY + mPadding - y3 + mWheelLength * 2 + strokeWidth,\n                    mPaint);\n\n        }\n\n    }\n\n    private void drawBigRing(Canvas canvas) {\n        float strokeWidth = dip2px(1.5f) / 4;\n        mPaintRing.setStrokeWidth(dip2px(1.5f));\n        canvas.drawCircle(bigRingCenterX + mPadding + mWheelLength * 2 + strokeWidth,\n                bigRingCenterY + mPadding + mWheelLength * 2 + strokeWidth,\n                bigRingCenterX - smallRingCenterX - strokeWidth, mPaintRing);\n        mPaintRing.setStrokeWidth(dip2px(1.5f));\n        canvas.drawCircle(bigRingCenterX + mPadding + mWheelLength * 2 + strokeWidth,\n                bigRingCenterY + mPadding + mWheelLength * 2 + strokeWidth,\n                (bigRingCenterX - smallRingCenterX) / 2 - strokeWidth, mPaintRing);\n\n    }\n\n\n    private void drawAxle(Canvas canvas) {\n\n\n        for (int i = 0; i < 3; i++) {\n            float x3 = (float) ((smallRingCenterX) * Math.cos(i * (360 / 3) * Math.PI / 180f));\n            float y3 = (float) ((smallRingCenterY) * Math.sin(i * (360 / 3) * Math.PI / 180f));\n            canvas.drawLine(mPadding + smallRingCenterX,\n                    mPadding + smallRingCenterY,\n                    mPadding + smallRingCenterX - x3,\n                    mPadding + smallRingCenterY - y3, mPaintAxle);\n\n        }\n\n        for (int i = 0; i < 3; i++) {\n            float x3 = (float) ((bigRingCenterX - smallRingCenterX) * Math.cos(i * (360 / 3) * Math.PI / 180f));\n            float y3 = (float) ((bigRingCenterY - smallRingCenterY) * Math.sin(i * (360 / 3) * Math.PI / 180f));\n            canvas.drawLine(bigRingCenterX + mPadding + mWheelLength * 2,\n                    bigRingCenterY + mPadding + mWheelLength * 2,\n                    bigRingCenterX + mPadding + mWheelLength * 2 - x3,\n                    bigRingCenterY + mPadding + mWheelLength * 2 - y3,\n                    mPaintAxle);\n\n        }\n\n\n    }\n\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        mPadding = dip2px(5);\n        canvas.save();\n        canvas.rotate(180, mWidth / 2, mWidth / 2);\n        drawSmallRing(canvas);\n        drawSmallGear(canvas);\n        drawBigGear(canvas);\n        drawBigRing(canvas);\n        drawAxle(canvas);\n        canvas.restore();\n    }\n\n    private void initPaint() {\n        mPaintRing = new Paint();\n        mPaintRing.setAntiAlias(true);\n        mPaintRing.setStyle(Paint.Style.STROKE);\n        mPaintRing.setColor(Color.WHITE);\n        mPaintRing.setStrokeWidth(dip2px(1.5f));\n\n\n        mPaint = new Paint();\n        mPaint.setAntiAlias(true);\n        mPaint.setStyle(Paint.Style.STROKE);\n        mPaint.setColor(Color.WHITE);\n        mPaint.setStrokeWidth(dip2px(1));\n\n\n        mPaintAxle = new Paint();\n        mPaintAxle.setAntiAlias(true);\n        mPaintAxle.setStyle(Paint.Style.FILL);\n        mPaintAxle.setColor(Color.WHITE);\n        mPaintAxle.setStrokeWidth(dip2px(1.5f));\n        mWheelLength = dip2px(2f);\n\n\n    }\n\n    public void setViewColor(int color) {\n        mPaint.setColor(color);\n        mPaintAxle.setColor(color);\n        mPaintRing.setColor(color);\n        postInvalidate();\n    }\n\n\n    @Override\n    protected void InitPaint() {\n        initPaint();\n    }\n\n    @Override\n    protected void OnAnimationUpdate(ValueAnimator valueAnimator) {\n        mAnimatedValue = (float) valueAnimator.getAnimatedValue();\n        postInvalidate();\n    }\n\n    @Override\n    protected void OnAnimationRepeat(Animator animation) {\n\n    }\n\n    @Override\n    protected int OnStopAnim() {\n        postInvalidate();\n        return 1;\n    }\n\n\n    @Override\n    protected int SetAnimRepeatMode() {\n        return ValueAnimator.RESTART;\n    }\n\n    @Override\n    protected void AnimIsRunning() {\n\n    }\n\n    @Override\n    protected int SetAnimRepeatCount() {\n        return ValueAnimator.INFINITE;\n    }\n\n    private int dip2px(float dpValue) {\n        final float scale = getContext().getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/io/virtualapp/widgets/ViewHelper.java",
    "content": "package io.virtualapp.widgets;\n\nimport io.virtualapp.VApp;\n\n/**\n * @author Lody\n */\npublic class ViewHelper {\n\n    public static int dip2px(float dpValue) {\n        final float scale = VApp.getApp().getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/blue_circle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\" >\n\n    <solid android:color=\"#03a9f4\" />\n\n    <corners android:radius=\"5dip\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/fab_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\">\n        <layer-list>\n            <!-- Shadow -->\n            <item android:right=\"1dp\" android:top=\"1dp\">\n                <layer-list>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#08000000\"/>\n                            <padding\n                                android:bottom=\"3px\"\n                                android:left=\"3px\"\n                                android:right=\"3px\"\n                                android:top=\"3px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#09000000\"/>\n                            <padding\n                                android:bottom=\"2px\"\n                                android:left=\"2px\"\n                                android:right=\"2px\"\n                                android:top=\"2px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#10000000\"/>\n                            <padding\n                                android:bottom=\"2px\"\n                                android:left=\"2px\"\n                                android:right=\"2px\"\n                                android:top=\"2px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#11000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#12000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#13000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#14000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#15000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#16000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                </layer-list>\n            </item>\n\n            <!-- Blue button pressed -->\n            <item>\n                <shape android:shape=\"oval\">\n                    <solid android:color=\"#90CAF9\"/>\n                </shape>\n            </item>\n        </layer-list>\n    </item>\n\n    <item android:state_enabled=\"true\">\n\n        <layer-list>\n            <!-- Shadow -->\n            <item android:right=\"1dp\" android:top=\"2dp\">\n                <layer-list>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#08000000\"/>\n                            <padding\n                                android:bottom=\"4px\"\n                                android:left=\"4px\"\n                                android:right=\"4px\"\n                                android:top=\"4px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#09000000\"/>\n                            <padding\n                                android:bottom=\"2px\"\n                                android:left=\"2px\"\n                                android:right=\"2px\"\n                                android:top=\"2px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#10000000\"/>\n                            <padding\n                                android:bottom=\"2px\"\n                                android:left=\"2px\"\n                                android:right=\"2px\"\n                                android:top=\"2px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#11000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#12000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#13000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#14000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#15000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                    <item>\n                        <shape android:shape=\"oval\">\n                            <solid android:color=\"#16000000\"/>\n                            <padding\n                                android:bottom=\"1px\"\n                                android:left=\"1px\"\n                                android:right=\"1px\"\n                                android:top=\"1px\"\n                                />\n                        </shape>\n                    </item>\n                </layer-list>\n            </item>\n\n            <!-- Blue button -->\n\n            <item>\n                <shape android:shape=\"oval\">\n                    <solid android:color=\"#03A9F4\"/>\n                </shape>\n            </item>\n        </layer-list>\n\n    </item>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/home_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n        android:angle=\"0\"\n        android:endColor=\"#0a2746\"\n        android:startColor=\"#21263d\"/>\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/icon_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:drawable=\"@color/black_20_transparent\"/>\n    <item android:drawable=\"@color/transparent\"/>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/sel_clone_app_btn.xml",
    "content": "<selector xmlns:Android=\"http://schemas.android.com/apk/res/android\">\n\n\n    <item Android:drawable=\"@drawable/shape_clone_app_btn\" Android:state_pressed=\"true\"/>\n    <item Android:drawable=\"@drawable/shape_clone_app_btn_pressed\"/>\n\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/sel_guide_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n    <corners\n        android:radius=\"25dp\"\n        />\n    <solid\n        android:color=\"@color/colorPrimaryDark\"\n        />\n    <padding\n        android:bottom=\"0dp\"\n        android:left=\"0dp\"\n        android:right=\"0dp\"\n        android:top=\"0dp\"\n        />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_clone_app_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"#03a9f4\" />\n    <corners android:radius=\"5dip\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_clone_app_btn_pressed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"#0083be\" />\n    <corners android:radius=\"5dip\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/layout/activity_clone_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/clone_app_tool_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\">\n\n        <android.support.design.widget.TabLayout\n            android:id=\"@+id/clone_app_tab_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:tabMode=\"scrollable\" />\n\n    </android.support.v7.widget.Toolbar>\n\n    <android.support.v4.view.ViewPager\n        android:id=\"@+id/clone_app_view_pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_home.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:fab=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/colorPrimaryDark\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"60dp\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginStart=\"20dp\"\n            android:gravity=\"start\"\n            android:orientation=\"horizontal\"\n            fab:layout_heightPercent=\"12%\">\n\n            <TextView\n                android:id=\"@+id/textView\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"match_parent\"\n                android:gravity=\"center|start\"\n                android:text=\"@string/app_name\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"22sp\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:gravity=\"center|end\"\n                android:orientation=\"horizontal\"\n                android:visibility=\"visible\">\n\n                <io.virtualapp.widgets.MaterialRippleLayout\n                    android:layout_width=\"60dp\"\n                    android:layout_height=\"60dp\">\n\n                    <ImageView\n                        android:id=\"@+id/home_menu\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"match_parent\"\n                        android:padding=\"15dp\"\n                        android:src=\"@drawable/ic_menu\" />\n                </io.virtualapp.widgets.MaterialRippleLayout>\n\n            </LinearLayout>\n\n        </LinearLayout>\n\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <android.support.v7.widget.RecyclerView\n                android:id=\"@+id/home_launcher\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:clipToPadding=\"false\"\n                android:scrollbars=\"vertical\"\n                tools:listitem=\"@layout/item_launcher_app\" />\n\n            <io.virtualapp.widgets.TwoGearsView\n                android:id=\"@+id/pb_loading_app\"\n                android:layout_width=\"100dp\"\n                android:layout_height=\"100dp\"\n                android:layout_gravity=\"center\"\n                android:layout_marginBottom=\"30dp\" />\n        </FrameLayout>\n    </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/bottom_area\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"60dp\"\n        android:layout_gravity=\"bottom|center\"\n        android:baselineAligned=\"false\"\n        android:orientation=\"horizontal\"\n        android:visibility=\"gone\"\n        android:weightSum=\"1\">\n\n        <LinearLayout\n            android:id=\"@+id/create_shortcut_area\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"0.5\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:layout_width=\"35dp\"\n                android:layout_height=\"35dp\"\n                android:src=\"@drawable/ic_shortcut\" />\n\n            <TextView\n                android:id=\"@+id/create_shortcut_text\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginStart=\"10dp\"\n                android:background=\"@color/colorPrimary\"\n                android:gravity=\"center|start\"\n                android:padding=\"5dp\"\n                android:text=\"@string/create_shortcut\"\n                android:textColor=\"@android:color/white\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/delete_app_area\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"0.5\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:layout_width=\"35dp\"\n                android:layout_height=\"35dp\"\n                android:src=\"@drawable/ic_crash\" />\n\n            <TextView\n                android:id=\"@+id/delete_app_text\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginStart=\"10dp\"\n                android:background=\"@color/colorPrimary\"\n                android:gravity=\"center|start\"\n                android:padding=\"5dp\"\n                android:text=\"@string/delete\"\n                android:textColor=\"@android:color/white\" />\n        </LinearLayout>\n\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_install.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:orientation=\"vertical\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\">\n\n    <FrameLayout\n        android:layout_weight=\"0.9\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\">\n\n    </FrameLayout>\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_weight=\"0.1\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\">\n\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#77000000\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"128dp\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"#F8F8F8\">\n\n        <io.virtualapp.widgets.EatBeansView\n            android:id=\"@+id/loading_anim\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"30dp\" />\n\n        <ImageView\n            android:id=\"@+id/app_icon\"\n            android:layout_width=\"64dp\"\n            android:layout_height=\"64dp\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginLeft=\"64dp\"\n            android:layout_marginStart=\"64dp\" />\n\n\n        <TextView\n            android:id=\"@+id/app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignBottom=\"@id/app_icon\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginStart=\"20dp\"\n            android:layout_toEndOf=\"@id/app_icon\"\n            android:layout_toRightOf=\"@id/app_icon\"\n            android:gravity=\"center\"\n            android:paddingBottom=\"25dp\"\n            android:text=\"@string/preparing\"\n            android:textColor=\"@android:color/black\" />\n\n    </RelativeLayout>\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_splash.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"bottom\"\n    android:orientation=\"vertical\"\n    tools:context=\".splash.SplashActivity\">\n\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|center\"\n        android:layout_marginBottom=\"20dp\"\n        android:text=\"@string/app_name\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"26sp\" />\n\n    <com.google.android.gms.ads.AdView\n        xmlns:ads=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/splash_banner\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"70dp\"\n        ads:adSize=\"SMART_BANNER\"\n        ads:adUnitId=\"ca-app-pub-1609791120068944/6624015319\">\n    </com.google.android.gms.ads.AdView>\n\n\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_users.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:orientation=\"vertical\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\">\n\n    <com.yanzhenjie.recyclerview.swipe.SwipeMenuRecyclerView\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginBottom=\"10dp\"\n        android:id=\"@+id/user_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_list_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n\n    <io.virtualapp.widgets.DragSelectRecyclerView\n        android:id=\"@+id/select_app_recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbars=\"vertical\"\n        app:dsrv_autoScrollEnabled=\"false\" />\n\n    <ProgressBar\n        android:id=\"@+id/select_app_progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n\n    <Button\n        android:id=\"@+id/select_app_install_btn\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\"\n        android:layout_gravity=\"bottom|center\"\n        android:layout_margin=\"15dp\"\n        android:background=\"@drawable/sel_clone_app_btn\"\n        android:text=\"Install to SandBox (1)\"\n        android:textSize=\"17sp\"\n        tools:ignore=\"HardcodedText\" />\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?attr/selectableItemBackground\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"60dp\"\n        android:orientation=\"horizontal\">\n\n        <ImageView\n            android:id=\"@+id/item_app_icon\"\n            android:layout_width=\"60dp\"\n            android:layout_height=\"60dp\" />\n\n        <TextView\n            android:id=\"@+id/item_app_name\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginStart=\"20dp\"\n            android:gravity=\"center|start\" />\n\n    </LinearLayout>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_clone_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@color/desktopColorA\">\n\n    <ImageView\n        android:id=\"@+id/item_app_checked\"\n        android:layout_width=\"23dp\"\n        android:layout_height=\"23dp\"\n        android:layout_gravity=\"top|end\"\n        android:src=\"@drawable/ic_no_check\" />\n\n    <io.virtualapp.widgets.LabelView xmlns:lv=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/item_app_clone_count\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|start\"\n        android:visibility=\"invisible\"\n        lv:lv_background_color=\"#F6CE59\"\n        lv:lv_fill_triangle=\"true\"\n        lv:lv_gravity=\"BOTTOM_LEFT\"\n        lv:lv_text=\"2\"\n        lv:lv_text_all_caps=\"false\"\n        lv:lv_text_color=\"@android:color/white\"\n        lv:lv_text_size=\"12sp\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"35dp\"\n        android:paddingLeft=\"20dp\"\n        android:paddingRight=\"20dp\"\n        android:paddingTop=\"35dp\">\n\n        <ImageView\n            android:id=\"@+id/item_app_icon\"\n            android:layout_width=\"60dp\"\n            android:layout_height=\"60dp\"\n            android:layout_marginBottom=\"12dp\" />\n\n        <io.virtualapp.widgets.MarqueeTextView\n            android:id=\"@+id/item_app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:focusableInTouchMode=\"true\"\n            android:gravity=\"center\"\n            android:marqueeRepeatLimit=\"marquee_forever\"\n            android:singleLine=\"true\"\n            android:textColor=\"#ffffff\"\n            android:textSize=\"12sp\" />\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_launcher_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.v7.widget.CardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center\"\n    app:cardElevation=\"10dp\">\n\n    <io.virtualapp.widgets.LabelView\n        xmlns:lv=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/item_app_space_idx\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"end\"\n        android:visibility=\"invisible\"\n        lv:lv_background_color=\"#3F9FE0\"\n        lv:lv_gravity=\"TOP_RIGHT\"\n        lv:lv_text=\"2\"\n        lv:lv_text_size=\"12sp\"/>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"35dp\"\n        android:paddingLeft=\"20dp\"\n        android:paddingRight=\"20dp\"\n        android:paddingTop=\"35dp\">\n\n        <io.virtualapp.widgets.LauncherIconView\n            android:id=\"@+id/item_app_icon\"\n            android:layout_width=\"45dp\"\n            android:layout_height=\"45dp\"\n            android:layout_marginBottom=\"12dp\"\n            app:pi_mask_color=\"#CC233333\"\n            app:pi_progress=\"0\"\n            app:pi_radius=\"40dp\"\n            app:pi_stroke=\"6dp\" />\n\n\n        <io.virtualapp.widgets.MarqueeTextView\n            android:id=\"@+id/item_app_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:focusableInTouchMode=\"true\"\n            android:gravity=\"center\"\n            android:marqueeRepeatLimit=\"marquee_forever\"\n            android:singleLine=\"true\"\n            android:textColor=\"#ffffff\"\n            android:textSize=\"12sp\" />\n\n        <View\n            android:id=\"@+id/item_first_open_dot\"\n            android:layout_width=\"8dp\"\n            android:layout_height=\"8dp\"\n            android:layout_marginTop=\"12dp\"\n            android:background=\"@drawable/blue_circle\"\n            android:visibility=\"invisible\" />\n\n    </LinearLayout>\n\n</android.support.v7.widget.CardView>"
  },
  {
    "path": "app/src/main/res/layout/item_user.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"60dp\"\n              android:background=\"?selectableItemBackground\"\n              android:gravity=\"center_vertical\"\n              android:orientation=\"horizontal\">\n\n    <ImageView\n        android:id=\"@+id/iv_icon\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\" />\n\n    <TextView\n        android:id=\"@+id/tv_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"10dp\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/menu/user_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/action_refresh\"\n        android:orderInCategory=\"100\"\n        app:showAsAction=\"always\"\n        android:title=\"@string/new_user\"\n        android:icon=\"@drawable/ic_add\"/>\n</menu>"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"CardStackLayout\">\n        <attr name=\"parallax_enabled\" format=\"boolean\" />\n        <attr name=\"parallax_scale\" format=\"integer\" />\n        <attr name=\"card_gap\" format=\"dimension\" />\n        <attr name=\"card_gap_bottom\" format=\"dimension\" />\n        <attr name=\"showInitAnimation\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ProgressImageView\">\n        <attr name=\"pi_progress\" format=\"integer\" />\n        <attr name=\"pi_mask_color\" format=\"color\" />\n        <attr name=\"pi_stroke\" format=\"dimension\" />\n        <attr name=\"pi_radius\" format=\"dimension\" />\n        <attr name=\"pi_force_square\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"ShimmerView\">\n        <attr name=\"reflectionColor\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"DragSelectRecyclerView\">\n        <attr name=\"dsrv_autoScrollHotspotHeight\" format=\"dimension\" />\n        <attr name=\"dsrv_autoScrollEnabled\" format=\"boolean\" />\n        <attr name=\"dsrv_autoScrollHotspot_offsetTop\" format=\"dimension\" />\n        <attr name=\"dsrv_autoScrollHotspot_offsetBottom\" format=\"dimension\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"LoadingIndicatorView\">\n        <attr name=\"minWidth\" format=\"dimension\" />\n        <attr name=\"maxWidth\" format=\"dimension\" />\n        <attr name=\"minHeight\" format=\"dimension\" />\n        <attr name=\"maxHeight\" format=\"dimension\" />\n        <attr name=\"indicatorName\" format=\"string\" />\n        <attr name=\"indicatorColor\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"RippleButton\">\n        <attr name=\"rippleColor\" format=\"color\" />\n        <attr name=\"alphaFactor\" format=\"float\" />\n        <attr name=\"hover\" format=\"boolean\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"MaterialRippleLayout\">\n        <attr name=\"mrl_rippleColor\" format=\"color\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleDimension\" format=\"dimension\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleOverlay\" format=\"boolean\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleAlpha\" format=\"float\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleDuration\" format=\"integer\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleFadeDuration\" format=\"integer\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleHover\" format=\"boolean\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleBackground\" format=\"color\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleDelayClick\" format=\"boolean\" localization=\"suggested\" />\n        <attr name=\"mrl_ripplePersistent\" format=\"boolean\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleInAdapter\" format=\"boolean\" localization=\"suggested\" />\n        <attr name=\"mrl_rippleRoundedCorners\" format=\"dimension\" localization=\"suggested\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"LabelView\">\n        <!-- 设置文字内容 -->\n        <attr name=\"lv_text\" format=\"string\"/>\n        <!-- 设置文字颜色,默认#ffffff -->\n        <attr name=\"lv_text_color\" format=\"color\"/>\n        <!-- 设置文字大小,默认11sp -->\n        <attr name=\"lv_text_size\" format=\"dimension\"/>\n        <!-- 设置文字是否支持加粗,默认true -->\n        <attr name=\"lv_text_bold\" format=\"boolean\"/>\n        <!-- 设置文字是否支持全部大写,默认true-->\n        <attr name=\"lv_text_all_caps\" format=\"boolean\"/>\n        <!-- 设置背景颜色,默认\"#FF4081\"-->\n        <attr name=\"lv_background_color\" format=\"color\"/>\n        <!-- 设置LabelView所在矩形最小宽高,默认mFillTriangle?35dp:50dp-->\n        <attr name=\"lv_min_size\" format=\"dimension\"/>\n        <!-- 设置文字上下padding,默认3.5dp,mFillTriangle为true时无效-->\n        <attr name=\"lv_padding\" format=\"dimension\"/>\n        <!-- 设置LabelView方向 -->\n        <attr name=\"lv_gravity\" format=\"enum\">\n            <enum name=\"TOP_LEFT\" value=\"51\"/>\n            <enum name=\"TOP_RIGHT\" value=\"53\"/>\n            <enum name=\"BOTTOM_LEFT\" value=\"83\"/>\n            <enum name=\"BOTTOM_RIGHT\" value=\"85\"/>\n        </attr>\n        <!-- 设置是否填充三角区域,默认false -->\n        <attr name=\"lv_fill_triangle\" format=\"boolean\"/>\n\n    </declare-styleable>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#607191</color>\n    <color name=\"colorPrimaryDark\">#2A364C</color>\n    <color name=\"colorPrimaryRavel\">#607191</color>\n    <color name=\"colorAccent\">#607191</color>\n    <color name=\"mainTextColor\">#F0DEB7</color>\n\n    <color name=\"desktopColorA\">#2C3B4E</color>\n    <color name=\"desktopColorB\">#314155</color>\n    <color name=\"desktopColorC\">#324257</color>\n    <color name=\"desktopColorD\">#2a3646</color>\n\n    <color name=\"holo_blue_dark\">#33cccc</color>\n    <color name=\"holo_yellow_dark\">#ff9640</color>\n    <color name=\"holo_green_dark\">#67e667</color>\n    <color name=\"holo_purple_dark\">#df38b1</color>\n    <color name=\"holo_red_dark\">#ff4040</color>\n\n    <integer-array name=\"progress_colors\">\n        <item>@color/holo_blue_dark</item>\n        <item>@color/holo_yellow_dark</item>\n        <item>@color/holo_green_dark</item>\n        <item>@color/holo_purple_dark</item>\n        <item>@color/holo_red_dark</item>\n    </integer-array>\n\n    <color name=\"transparent\">#00000000</color>\n    <color name=\"black_20_transparent\">#55000000</color>\n\n    <color name=\"md_transparent\">#00FFFFFF</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n    <!--Card Stack-->\n    <dimen name=\"card_gap\">60dp</dimen>\n    <dimen name=\"card_gap_bottom\">5dp</dimen>\n    <dimen name=\"dp30\">30dp</dimen>\n    <dimen name=\"dp8\">8dp</dimen>\n    <dimen name=\"dp10\">10dp</dimen>\n    <dimen name=\"dp80\">80dp</dimen>\n    <dimen name=\"card_radius\">5dp</dimen>\n    <dimen name=\"dp16\">16dp</dimen>\n    <dimen name=\"dp24\">24dp</dimen>\n\n    <!--Desktop-->\n    <dimen name=\"desktop_divider\">0.5dp</dimen>\n    <dimen name=\"item_height\">60dp</dimen>\n\n    <dimen name=\"dsrv_defaultHotspotHeight\">56dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"cardstack_internal_position_tag\" type=\"id\"/>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/integers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"parallax_scale_default\">-2</integer>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">VirtualApp</string>\n    <string name=\"wait\">Please wait…</string>\n    <string name=\"desktop\">Desktop</string>\n    <string name=\"add_app\">Add App</string>\n    <string name=\"preparing\">Opening the app…</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"create_shortcut\">Create shortcut</string>\n    <string name=\"new_user\">New User</string>\n    <string name=\"enable\">Enable</string>\n    <string name=\"save\">Save</string>\n    <string name=\"save_success\">Save success!</string>\n    <string name=\"manufacturer\">Manufacturer</string>\n    <string name=\"brand\">Brand</string>\n    <string name=\"device\">Device</string>\n    <string name=\"fake_device_info\">Fake Device Info</string>\n    <string name=\"wifi_status\">Wifi Status</string>\n    <string name=\"config_device_info\">Device Info</string>\n    <string name=\"about\">About</string>\n    <string name=\"clone_apps\">Clone Apps</string>\n    <string name=\"external_storage\">External Storage</string>\n    <string name=\"install_d\">Install (%d)</string>\n    <string name=\"install_too_much_once_time\">No more then 9 apps can be chosen at a time!</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.NoActionBar\">\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowIsTranslucent\">false</item>\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n        <item name=\"android:textAllCaps\">false</item>\n    </style>\n\n    <style name=\"UITheme\" parent=\"Theme.AppCompat.NoActionBar\">\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n        <item name=\"android:textAllCaps\">false</item>\n    </style>\n\n    <style name=\"TransparentTheme\" parent=\"AppTheme\">\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"colorPrimary\">@android:color/background_dark</item>\n        <item name=\"colorPrimaryDark\">@android:color/background_dark</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowAnimationStyle\">@null</item>\n    </style>\n\n    <style name=\"VAAlertTheme\" parent=\"Theme.AppCompat.Light.Dialog.Alert\" />\n\n    <style name=\"AVLoadingIndicatorView\">\n        <item name=\"minWidth\">48dip</item>\n        <item name=\"maxWidth\">48dip</item>\n        <item name=\"minHeight\">48dip</item>\n        <item name=\"maxHeight\">48dip</item>\n        <item name=\"indicatorName\">BallPulseIndicator</item>\n    </style>\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">VirtualApp</string>\n    <string name=\"desktop\">桌面</string>\n    <string name=\"add_app\">添加App</string>\n    <string name=\"preparing\">正在打开App，请稍等…</string>\n    <string name=\"delete\">删除</string>\n    <string name=\"create_shortcut\">创建快捷方式</string>\n    <string name=\"new_user\">新的用户</string>\n    <string name=\"enable\">开启</string>\n    <string name=\"save\">保存</string>\n    <string name=\"save_success\">保存成功!</string>\n    <string name=\"manufacturer\">制造商</string>\n    <string name=\"wait\">请稍后…</string>\n    <string name=\"brand\">品牌</string>\n    <string name=\"device\">机型</string>\n    <string name=\"fake_device_info\">伪造设备信息</string>\n    <string name=\"wifi_status\">Wifi状态</string>\n    <string name=\"config_device_info\">配置设备信息</string>\n    <string name=\"about\">关于</string>\n    <string name=\"clone_apps\">克隆App</string>\n    <string name=\"external_storage\">外置存储</string>\n    <string name=\"install_d\">安装 (%d)</string>\n    <string name=\"install_too_much_once_time\">不能一次性安装超过9个App!</string>\n</resources>\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.3.1'\n        classpath 'me.tatarka:gradle-retrolambda:3.6.0'\n        classpath 'com.android.tools.build:gradle-experimental:0.8.0'\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        maven {\n            url \"https://jitpack.io\"\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Jan 15 17:29:32 CST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-3.3-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useDeprecatedNdk=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "lib/.gitignore",
    "content": "/build\n.externalNativeBuild/\nobj/\n"
  },
  {
    "path": "lib/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"25.0.2\"\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 22\n        versionCode 1\n        versionName \"1.0\"\n        externalNativeBuild {\n            ndkBuild {\n                abiFilters \"armeabi\", \"armeabi-v7a\", \"x86\"\n            }\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    externalNativeBuild {\n        ndkBuild {\n            path file(\"src/main/jni/Android.mk\")\n        }\n    }\n    lintOptions {\n        //IJobService need NewApi\n        warning 'NewApi','OnClick'\n    }\n}\n\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    //    compile 'net.lingala.zip4j:zip4j:1.3.2'\n    compile files('src/main/libs/android-art-interpret-3.0.0.jar')\n    compile files('src/main/libs/dalvik_hack-3.0.0.5.jar')\n}\n"
  },
  {
    "path": "lib/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/lody/Desktop/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "lib/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.lody.virtual\">\n\n    <uses-permission android:name=\"com.huawei.authentication.HW_ACCESS_AUTH_SERVICE\" />\n\n    <uses-permission android:name=\"com.samsung.svoice.sync.READ_DATABASE\" />\n    <uses-permission android:name=\"com.samsung.svoice.sync.ACCESS_SERVICE\" />\n    <uses-permission android:name=\"com.samsung.svoice.sync.WRITE_DATABASE\" />\n    <uses-permission android:name=\"com.sec.android.app.voicenote.Controller\" />\n    <uses-permission android:name=\"com.sec.android.permission.VOIP_INTERFACE\" />\n    <uses-permission android:name=\"com.sec.android.permission.LAUNCH_PERSONAL_PAGE_SERVICE\" />\n    <uses-permission android:name=\"com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY\" />\n    <uses-permission android:name=\"com.samsung.android.providers.context.permission.READ_RECORD_AUDIO\" />\n    <uses-permission android:name=\"com.samsung.android.providers.context.permission.WRITE_RECORD_AUDIO\" />\n    <uses-permission android:name=\"com.sec.android.settings.permission.SOFT_RESET\" />\n    <uses-permission android:name=\"sec.android.permission.READ_MSG_PREF\" />\n    <uses-permission android:name=\"com.samsung.android.scloud.backup.lib.read\" />\n    <uses-permission android:name=\"com.samsung.android.scloud.backup.lib.write\" />\n\n    <uses-permission android:name=\"android.permission.BIND_DIRECTORY_SEARCH\" />\n    <uses-permission android:name=\"android.permission.UPDATE_APP_OPS_STATS\" />\n    <uses-permission android:name=\"com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL\" />\n\n    <uses-permission\n        android:name=\"android.permission.ACCOUNT_MANAGER\"\n        tools:ignore=\"ProtectedPermissions\" />\n\n    <uses-permission\n        android:name=\"android.permission.PACKAGE_USAGE_STATS\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission android:name=\"android.permission.USE_CREDENTIALS\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\" />\n    <uses-permission\n        android:name=\"android.permission.ACCESS_MOCK_LOCATION\"\n        tools:ignore=\"MockLocation\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIMAX_STATE\" />\n    <uses-permission android:name=\"android.permission.AUTHENTICATE_ACCOUNTS\" />\n    <uses-permission\n        android:name=\"android.permission.BIND_APPWIDGET\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission android:name=\"android.permission.BLUETOOTH\" />\n    <uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" />\n    <uses-permission android:name=\"android.permission.BODY_SENSORS\" />\n    <uses-permission android:name=\"android.permission.BROADCAST_STICKY\" />\n    <uses-permission android:name=\"android.permission.CALL_PHONE\" />\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_WIFI_MULTICAST_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_WIMAX_STATE\" />\n    <uses-permission android:name=\"android.permission.CLEAR_APP_CACHE\" />\n    <uses-permission android:name=\"android.permission.DISABLE_KEYGUARD\" />\n    <uses-permission android:name=\"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION\" />\n    <uses-permission android:name=\"android.permission.EXPAND_STATUS_BAR\" />\n    <uses-permission android:name=\"android.permission.FLASHLIGHT\" />\n    <uses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n    <uses-permission android:name=\"android.permission.GET_CLIPS\" />\n    <uses-permission android:name=\"android.permission.GET_PACKAGE_SIZE\" />\n    <uses-permission android:name=\"android.permission.GET_TASKS\" />\n    <uses-permission android:name=\"android.permission.KILL_BACKGROUND_PROCESSES\" />\n    <uses-permission android:name=\"android.permission.MANAGE_ACCOUNTS\" />\n    <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.NFC\" />\n    <uses-permission android:name=\"android.permission.PERSISTENT_ACTIVITY\" />\n    <uses-permission android:name=\"android.permission.PROCESS_OUTGOING_CALLS\" />\n    <uses-permission android:name=\"android.permission.READ_CALENDAR\" />\n    <uses-permission android:name=\"android.permission.READ_CALL_LOG\" />\n    <uses-permission android:name=\"android.permission.READ_CELL_BROADCASTS\" />\n    <uses-permission android:name=\"android.permission.READ_CLIPS\" />\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.READ_INSTALL_SESSIONS\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_PROFILE\" />\n    <uses-permission android:name=\"android.permission.READ_SMS\" />\n    <uses-permission android:name=\"android.permission.READ_SOCIAL_STREAM\" />\n    <uses-permission android:name=\"android.permission.READ_SYNC_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.READ_SYNC_STATS\" />\n    <uses-permission android:name=\"android.permission.READ_USER_DICTIONARY\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_MMS\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_SMS\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_WAP_PUSH\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.REORDER_TASKS\" />\n    <uses-permission android:name=\"android.permission.RESTART_PACKAGES\" />\n    <uses-permission android:name=\"android.permission.SEND_SMS\" />\n    <uses-permission android:name=\"android.permission.SET_TIME_ZONE\" />\n    <uses-permission android:name=\"android.permission.SET_WALLPAPER\" />\n    <uses-permission android:name=\"android.permission.SET_WALLPAPER_HINTS\" />\n    <uses-permission android:name=\"android.permission.SUBSCRIBED_FEEDS_READ\" />\n    <uses-permission android:name=\"android.permission.SUBSCRIBED_FEEDS_WRITE\" />\n    <uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />\n    <uses-permission android:name=\"android.permission.TRANSMIT_IR\" />\n    <uses-permission android:name=\"android.permission.USE_SIP\" />\n    <uses-permission android:name=\"android.permission.VIBRATE\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"android.permission.WRITE_CALENDAR\" />\n    <uses-permission android:name=\"android.permission.WRITE_CALL_LOG\" />\n    <uses-permission android:name=\"android.permission.WRITE_CLIPS\" />\n    <uses-permission android:name=\"android.permission.WRITE_CONTACTS\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_PROFILE\" />\n    <uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.WRITE_SMS\" />\n    <uses-permission android:name=\"android.permission.WRITE_SOCIAL_STREAM\" />\n    <uses-permission android:name=\"android.permission.WRITE_SYNC_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.WRITE_USER_DICTIONARY\" />\n    <uses-permission android:name=\"android.permission.USE_FINGERPRINT\" />\n    <uses-permission android:name=\"com.android.alarm.permission.SET_ALARM\" />\n    <uses-permission android:name=\"com.android.browser.permission.READ_HISTORY_BOOKMARKS\" />\n    <uses-permission android:name=\"com.android.browser.permission.WRITE_HISTORY_BOOKMARKS\" />\n    <uses-permission android:name=\"com.android.launcher.permission.INSTALL_SHORTCUT\" />\n    <uses-permission android:name=\"com.android.launcher.permission.UNINSTALL_SHORTCUT\" />\n    <uses-permission android:name=\"com.android.vending.BILLING\" />\n    <uses-permission android:name=\"com.android.vending.CHECK_LICENSE\" />\n    <uses-permission android:name=\"com.android.voicemail.permission.ADD_VOICEMAIL\" />\n    <uses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\" />\n    <uses-permission android:name=\"com.google.android.gms.permission.ACTIVITY_RECOGNITION\" />\n    <uses-permission android:name=\"com.google.android.gms.permission.AD_ID_NOTIFICATION\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.OTHER_SERVICES\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.YouTubeUser\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.adsense\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.adwords\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.ah\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.android\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.androidsecure\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.blogger\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.cl\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.cp\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.dodgeball\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.finance\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.gbase\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.grandcentral\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.groups2\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.health\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.ig\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.jotspot\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.knol\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.lh2\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.local\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.mail\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.mobile\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.news\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.notebook\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.orkut\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.print\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.sierra\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.sierraqa\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.sierrasandbox\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.sitemaps\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.speech\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.speechpersonalization\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.talk\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.wifi\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.wise\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.writely\" />\n    <uses-permission android:name=\"com.google.android.googleapps.permission.GOOGLE_AUTH.youtube\" />\n    <uses-permission android:name=\"com.google.android.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.google.android.providers.gsf.permission.READ_GSERVICES\" />\n    <uses-permission android:name=\"com.google.android.providers.talk.permission.READ_ONLY\" />\n    <uses-permission android:name=\"com.google.android.providers.talk.permission.WRITE_ONLY\" />\n    <uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />\n    <uses-permission android:name=\"android.permission.READ_LOGS\" />\n    <uses-permission\n        android:name=\"android.permission.INSTALL_PACKAGES\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission\n        android:name=\"android.permission.DELETE_PACKAGES\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission\n        android:name=\"android.permission.CLEAR_APP_USER_DATA\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission\n        android:name=\"android.permission.WRITE_MEDIA_STORAGE\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission\n        android:name=\"android.permission.ACCESS_CACHE_FILESYSTEM\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission android:name=\"android.permission.READ_OWNER_DATA\" />\n    <uses-permission android:name=\"android.permission.WRITE_OWNER_DATA\" />\n    <uses-permission android:name=\"android.permission.CHANGE_CONFIGURATION\" />\n    <uses-permission\n        android:name=\"android.permission.DEVICE_POWER\"\n        tools:ignore=\"ProtectedPermissions\" />\n    <uses-permission android:name=\"android.permission.BATTERY_STATS\" />\n    <uses-permission android:name=\"android.permission.ACCESS_DOWNLOAD_MANAGER\" />\n    <uses-permission android:name=\"com.android.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.android.launcher.permission.WRITE_SETTINGS\" />\n    <uses-permission android:name=\"com.android.launcher3.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.android.launcher2.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.teslacoilsw.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.actionlauncher.playstore.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.mx.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.anddoes.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.apusapps.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.tsf.shell.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.htc.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.lenovo.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.oppo.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.bbk.launcher2.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.s.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"cn.nubia.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.huawei.android.launcher.permission.READ_SETTINGS\" />\n    <uses-permission android:name=\"com.huawei.android.launcher.permission.CHANGE_BADGE\" />\n    <uses-permission android:name=\"android.permission.GET_INTENT_SENDER_INTENT\" />\n\n    <uses-permission\n        android:name=\"android.permission.WRITE_APN_SETTINGS\"\n        tools:ignore=\"ProtectedPermissions\" />\n\n    <uses-feature android:name=\"android.hardware.camera\" />\n    <uses-feature\n        android:name=\"android.hardware.camera.autofocus\"\n        android:required=\"false\" />\n\n    <application>\n        <service\n            android:name=\"com.lody.virtual.client.stub.DaemonService\"\n            android:process=\"@string/engine_process_name\" />\n\n        <service\n            android:name=\"com.lody.virtual.client.stub.DaemonService$InnerService\"\n            android:process=\"@string/engine_process_name\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.ShortcutHandleActivity\"\n            android:exported=\"true\"\n            android:process=\"@string/engine_process_name\"\n            android:theme=\"@android:style/Theme.Translucent.NoTitleBar\" />\n\n        <activity\n            android:name=\".client.stub.StubPendingActivity\"\n            android:process=\"@string/engine_process_name\" />\n\n        <service\n            android:name=\".client.stub.StubPendingService\"\n            android:process=\"@string/engine_process_name\" />\n        <receiver\n            android:name=\".client.stub.StubPendingReceiver\"\n            android:process=\"@string/engine_process_name\" />\n\n        <service\n            android:name=\".client.stub.StubJob\"\n            android:permission=\"android.permission.BIND_JOB_SERVICE\"\n            android:process=\"@string/engine_process_name\" />\n\n        <activity\n            android:name=\".client.stub.ChooseAccountTypeActivity\"\n            android:configChanges=\"keyboard|keyboardHidden|orientation\"\n            android:excludeFromRecents=\"true\"\n            android:exported=\"false\"\n            android:process=\"@string/engine_process_name\"\n            android:screenOrientation=\"portrait\" />\n\n        <activity\n            android:name=\".client.stub.ChooseTypeAndAccountActivity\"\n            android:configChanges=\"keyboard|keyboardHidden|orientation\"\n            android:excludeFromRecents=\"true\"\n            android:exported=\"false\"\n            android:process=\"@string/engine_process_name\"\n            android:screenOrientation=\"portrait\" />\n\n        <activity\n            android:name=\".client.stub.ChooserActivity\"\n            android:configChanges=\"keyboard|keyboardHidden|orientation\"\n            android:excludeFromRecents=\"true\"\n            android:exported=\"true\"\n            android:finishOnCloseSystemDialogs=\"true\"\n            android:process=\"@string/engine_process_name\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/VAAlertTheme\" />\n\n        <activity\n            android:name=\".client.stub.ResolverActivity\"\n            android:configChanges=\"keyboard|keyboardHidden|orientation\"\n            android:excludeFromRecents=\"true\"\n            android:exported=\"true\"\n            android:finishOnCloseSystemDialogs=\"true\"\n            android:process=\"@string/engine_process_name\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/VAAlertTheme\" />\n\n        <provider\n            android:name=\"com.lody.virtual.server.BinderProvider\"\n            android:authorities=\"${applicationId}.virtual.service.BinderProvider\"\n            android:exported=\"false\"\n            android:process=\"@string/engine_process_name\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C0\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p0\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C1\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p1\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C2\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p2\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C3\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p3\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C4\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p4\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C5\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p5\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C6\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p6\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C7\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p7\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C8\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p8\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C9\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p9\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C10\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p10\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C11\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p11\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C12\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p12\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C13\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p13\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C14\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p14\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C15\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p15\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C16\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p16\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C17\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p17\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C18\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p18\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C19\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p19\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C20\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p20\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C21\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p21\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C22\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p22\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C23\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p23\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C24\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p24\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C25\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p25\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C26\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p26\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C27\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p27\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C28\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p28\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C29\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p29\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C30\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p30\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C31\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p31\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C32\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p32\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C33\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p33\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C34\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n\n            android:process=\":p34\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C35\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p35\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C36\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p36\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C37\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p37\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C38\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p38\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C39\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p39\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C40\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p40\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C41\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p41\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C42\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p42\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C43\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p43\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C44\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p44\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C45\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p45\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C46\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p46\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C47\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p47\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C48\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p48\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubActivity$C49\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p49\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@style/VATheme\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C0\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p0\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C1\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p1\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C2\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p2\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C3\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p3\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C4\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p4\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C5\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p5\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C6\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p6\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C7\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p7\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C8\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p8\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C9\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p9\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C10\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p10\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C11\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p11\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C12\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p12\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C13\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p13\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C14\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p14\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C15\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p15\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C16\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p16\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C17\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p17\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C18\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p18\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C19\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p19\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C20\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p20\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C21\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p21\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C22\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p22\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C23\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p23\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C24\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p24\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C25\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p25\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C26\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p26\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C27\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p27\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C28\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p28\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C29\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p29\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C30\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p30\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C31\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p31\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C32\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p32\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C33\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p33\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C34\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p34\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C35\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p35\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C36\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p36\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C37\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p37\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C38\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p38\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C39\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p39\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C40\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p40\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C41\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p41\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C42\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p42\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C43\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p43\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C44\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p44\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C45\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p45\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C46\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p46\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C47\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p47\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C48\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p48\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <activity\n            android:name=\"com.lody.virtual.client.stub.StubDialog$C49\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale\"\n            android:process=\":p49\"\n            android:taskAffinity=\"com.lody.virtual.vt\"\n            android:theme=\"@android:style/Theme.Dialog\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C0\"\n            android:authorities=\"${applicationId}.virtual_stub_0\"\n            android:exported=\"false\"\n            android:process=\":p0\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C1\"\n            android:authorities=\"${applicationId}.virtual_stub_1\"\n            android:exported=\"false\"\n            android:process=\":p1\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C2\"\n            android:authorities=\"${applicationId}.virtual_stub_2\"\n            android:exported=\"false\"\n            android:process=\":p2\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C3\"\n            android:authorities=\"${applicationId}.virtual_stub_3\"\n            android:exported=\"false\"\n            android:process=\":p3\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C4\"\n            android:authorities=\"${applicationId}.virtual_stub_4\"\n            android:exported=\"false\"\n            android:process=\":p4\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C5\"\n            android:authorities=\"${applicationId}.virtual_stub_5\"\n            android:exported=\"false\"\n            android:process=\":p5\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C6\"\n            android:authorities=\"${applicationId}.virtual_stub_6\"\n            android:exported=\"false\"\n            android:process=\":p6\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C7\"\n            android:authorities=\"${applicationId}.virtual_stub_7\"\n            android:exported=\"false\"\n            android:process=\":p7\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C8\"\n            android:authorities=\"${applicationId}.virtual_stub_8\"\n            android:exported=\"false\"\n            android:process=\":p8\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C9\"\n            android:authorities=\"${applicationId}.virtual_stub_9\"\n            android:exported=\"false\"\n            android:process=\":p9\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C10\"\n            android:authorities=\"${applicationId}.virtual_stub_10\"\n            android:exported=\"false\"\n            android:process=\":p10\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C11\"\n            android:authorities=\"${applicationId}.virtual_stub_11\"\n            android:exported=\"false\"\n            android:process=\":p11\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C12\"\n            android:authorities=\"${applicationId}.virtual_stub_12\"\n            android:exported=\"false\"\n            android:process=\":p12\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C13\"\n            android:authorities=\"${applicationId}.virtual_stub_13\"\n            android:exported=\"false\"\n            android:process=\":p13\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C14\"\n            android:authorities=\"${applicationId}.virtual_stub_14\"\n            android:exported=\"false\"\n            android:process=\":p14\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C15\"\n            android:authorities=\"${applicationId}.virtual_stub_15\"\n            android:exported=\"false\"\n            android:process=\":p15\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C16\"\n            android:authorities=\"${applicationId}.virtual_stub_16\"\n            android:exported=\"false\"\n            android:process=\":p16\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C17\"\n            android:authorities=\"${applicationId}.virtual_stub_17\"\n            android:exported=\"false\"\n            android:process=\":p17\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C18\"\n            android:authorities=\"${applicationId}.virtual_stub_18\"\n            android:exported=\"false\"\n            android:process=\":p18\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C19\"\n            android:authorities=\"${applicationId}.virtual_stub_19\"\n            android:exported=\"false\"\n            android:process=\":p19\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C20\"\n            android:authorities=\"${applicationId}.virtual_stub_20\"\n            android:exported=\"false\"\n            android:process=\":p20\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C21\"\n            android:authorities=\"${applicationId}.virtual_stub_21\"\n            android:exported=\"false\"\n            android:process=\":p21\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C22\"\n            android:authorities=\"${applicationId}.virtual_stub_22\"\n            android:exported=\"false\"\n            android:process=\":p22\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C23\"\n            android:authorities=\"${applicationId}.virtual_stub_23\"\n            android:exported=\"false\"\n            android:process=\":p23\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C24\"\n            android:authorities=\"${applicationId}.virtual_stub_24\"\n            android:exported=\"false\"\n            android:process=\":p24\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C25\"\n            android:authorities=\"${applicationId}.virtual_stub_25\"\n            android:exported=\"false\"\n            android:process=\":p25\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C26\"\n            android:authorities=\"${applicationId}.virtual_stub_26\"\n            android:exported=\"false\"\n            android:process=\":p26\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C27\"\n            android:authorities=\"${applicationId}.virtual_stub_27\"\n            android:exported=\"false\"\n            android:process=\":p27\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C28\"\n            android:authorities=\"${applicationId}.virtual_stub_28\"\n            android:exported=\"false\"\n            android:process=\":p28\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C29\"\n            android:authorities=\"${applicationId}.virtual_stub_29\"\n            android:exported=\"false\"\n            android:process=\":p29\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C30\"\n            android:authorities=\"${applicationId}.virtual_stub_30\"\n            android:exported=\"false\"\n            android:process=\":p30\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C31\"\n            android:authorities=\"${applicationId}.virtual_stub_31\"\n            android:exported=\"false\"\n            android:process=\":p31\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C32\"\n            android:authorities=\"${applicationId}.virtual_stub_32\"\n            android:exported=\"false\"\n            android:process=\":p32\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C33\"\n            android:authorities=\"${applicationId}.virtual_stub_33\"\n            android:exported=\"false\"\n            android:process=\":p33\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C34\"\n            android:authorities=\"${applicationId}.virtual_stub_34\"\n            android:exported=\"false\"\n            android:process=\":p34\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C35\"\n            android:authorities=\"${applicationId}.virtual_stub_35\"\n            android:exported=\"false\"\n            android:process=\":p35\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C36\"\n            android:authorities=\"${applicationId}.virtual_stub_36\"\n            android:exported=\"false\"\n            android:process=\":p36\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C37\"\n            android:authorities=\"${applicationId}.virtual_stub_37\"\n            android:exported=\"false\"\n            android:process=\":p37\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C38\"\n            android:authorities=\"${applicationId}.virtual_stub_38\"\n            android:exported=\"false\"\n            android:process=\":p38\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C39\"\n            android:authorities=\"${applicationId}.virtual_stub_39\"\n            android:exported=\"false\"\n            android:process=\":p39\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C40\"\n            android:authorities=\"${applicationId}.virtual_stub_40\"\n            android:exported=\"false\"\n            android:process=\":p40\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C41\"\n            android:authorities=\"${applicationId}.virtual_stub_41\"\n            android:exported=\"false\"\n            android:process=\":p41\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C42\"\n            android:authorities=\"${applicationId}.virtual_stub_42\"\n            android:exported=\"false\"\n            android:process=\":p42\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C43\"\n            android:authorities=\"${applicationId}.virtual_stub_43\"\n            android:exported=\"false\"\n            android:process=\":p43\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C44\"\n            android:authorities=\"${applicationId}.virtual_stub_44\"\n            android:exported=\"false\"\n            android:process=\":p44\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C45\"\n            android:authorities=\"${applicationId}.virtual_stub_45\"\n            android:exported=\"false\"\n            android:process=\":p45\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C46\"\n            android:authorities=\"${applicationId}.virtual_stub_46\"\n            android:exported=\"false\"\n            android:process=\":p46\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C47\"\n            android:authorities=\"${applicationId}.virtual_stub_47\"\n            android:exported=\"false\"\n            android:process=\":p47\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C48\"\n            android:authorities=\"${applicationId}.virtual_stub_48\"\n            android:exported=\"false\"\n            android:process=\":p48\" />\n\n        <provider\n            android:name=\"com.lody.virtual.client.stub.StubContentProvider$C49\"\n            android:authorities=\"${applicationId}.virtual_stub_49\"\n            android:exported=\"false\"\n            android:process=\":p49\" />\n\n    </application>\n</manifest>"
  },
  {
    "path": "lib/src/main/aidl/android/accounts/IAccountAuthenticator.aidl",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.accounts;\n\nimport android.accounts.IAccountAuthenticatorResponse;\nimport android.accounts.Account;\nimport android.os.Bundle;\n\n/**\n * Service that allows the interaction with an authentication server.\n * @hide\n */\ninterface IAccountAuthenticator {\n    /**\n     * prompts the user for account information and adds the result to the IAccountManager\n     */\n    void addAccount(in IAccountAuthenticatorResponse response, String accountType,\n        String authTokenType, in String[] requiredFeatures, in Bundle options);\n\n    /**\n     * prompts the user for the credentials of the account\n     */\n    void confirmCredentials(in IAccountAuthenticatorResponse response, in Account account,\n        in Bundle options);\n\n    /**\n     * gets the password by either prompting the user or querying the IAccountManager\n     */\n    void getAuthToken(in IAccountAuthenticatorResponse response, in Account account,\n        String authTokenType, in Bundle options);\n\n    /**\n     * Gets the user-visible label of the given authtoken type.\n     */\n    void getAuthTokenLabel(in IAccountAuthenticatorResponse response, String authTokenType);\n\n    /**\n     * prompts the user for a new password and writes it to the IAccountManager\n     */\n    void updateCredentials(in IAccountAuthenticatorResponse response, in Account account,\n        String authTokenType, in Bundle options);\n\n    /**\n     * launches an activity that lets the user edit and set the properties for an authenticator\n     */\n    void editProperties(in IAccountAuthenticatorResponse response, String accountType);\n\n    /**\n     * returns a Bundle where the boolean value BOOLEAN_RESULT_KEY is set if the account has the\n     * specified features\n     */\n    void hasFeatures(in IAccountAuthenticatorResponse response, in Account account, \n        in String[] features);\n\n    /**\n     * Gets whether or not the account is allowed to be removed.\n     */\n    void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account);\n\n    /**\n     * Returns a Bundle containing the required credentials to copy the account across users.\n     */\n    void getAccountCredentialsForCloning(in IAccountAuthenticatorResponse response,\n            in Account account);\n\n    /**\n     * Uses the Bundle containing credentials from another instance of the authenticator to create\n     * a copy of the account on this user.\n     */\n    void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account,\n            in Bundle accountCredentials);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/accounts/IAccountAuthenticatorResponse.aidl",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.accounts;\nimport android.os.Bundle;\n\n/**\n * The interface used to return responses from an {@link IAccountAuthenticator}\n */\ninterface IAccountAuthenticatorResponse {\n    void onResult(in Bundle value);\n    void onRequestContinued();\n    void onError(int errorCode, String errorMessage);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/accounts/IAccountManagerResponse.aidl",
    "content": "package android.accounts;\n\nimport android.os.Bundle;\n\n/**\n * The interface used to return responses for asynchronous calls to the {@link IAccountManager}\n */\ninterface IAccountManagerResponse {\n    void onResult(in Bundle value);\n    void onError(int errorCode, String errorMessage);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/app/IActivityManager/ContentProviderHolder.aidl",
    "content": "// ContentProviderHolder.aidl\npackage android.app.IActivityManager;\n\nparcelable ContentProviderHolder;"
  },
  {
    "path": "lib/src/main/aidl/android/app/IServiceConnection.aidl",
    "content": "/* //device/java/android/android/app/IServiceConnection.aidl\n**\n** Copyright 2007, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\npackage android.app;\n\nimport android.content.ComponentName;\n\n/** @hide */\ninterface IServiceConnection {\n    void connected(in ComponentName name, IBinder service);\n}\n\n"
  },
  {
    "path": "lib/src/main/aidl/android/app/IStopUserCallback.aidl",
    "content": "/*\n** Copyright 2012, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n\npackage android.app;\n\n/**\n * Callback to find out when we have finished stopping a user.\n * {@hide}\n */\ninterface IStopUserCallback\n{\n    void userStopped(int userId);\n    void userStopAborted(int userId);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/app/job/IJobCallback.aidl",
    "content": "/**\n * Copyright 2014, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.app.job;\n\n/**\n * The server side of the JobScheduler IPC protocols.  The app-side implementation\n * invokes on this interface to indicate completion of the (asynchronous) instructions\n * issued by the server.\n *\n * In all cases, the 'who' parameter is the caller's service binder, used to track\n * which Job Service instance is reporting.\n *\n */\ninterface IJobCallback {\n    /**\n     * Immediate callback to the system after sending a start signal, used to quickly detect ANR.\n     *\n     * @param jobId Unique integer used to identify this job.\n     * @param ongoing True to indicate that the client is processing the job. False if the job is\n     * complete\n     */\n    void acknowledgeStartMessage(int jobId, boolean ongoing);\n    /**\n     * Immediate callback to the system after sending a stop signal, used to quickly detect ANR.\n     *\n     * @param jobId Unique integer used to identify this job.\n     * @param reschedule Whether or not to reschedule this job.\n     */\n    void acknowledgeStopMessage(int jobId, boolean reschedule);\n    /*\n     * Tell the job manager that the client is done with its execution, so that it can go on to\n     * the next one and stop attributing wakelock time to us etc.\n     *\n     * @param jobId Unique integer used to identify this job.\n     * @param reschedule Whether or not to reschedule this job.\n     */\n    void jobFinished(int jobId, boolean reschedule);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/app/job/IJobService.aidl",
    "content": "package android.app.job;\n\nimport android.app.job.JobParameters;\n\n/**\n * Interface that the framework uses to communicate with application code that implements a\n * JobService.  End user code does not implement this interface directly; instead, the app's\n * service implementation will extend android.app.job.JobService.\n */\ninterface IJobService {\n    /** Begin execution of application's job. */\n    void startJob(in JobParameters jobParams);\n    /** Stop execution of application's job. */\n    void stopJob(in JobParameters jobParams);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/IIntentReceiver.aidl",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.content;\n\nimport android.content.Intent;\nimport android.os.Bundle;\n\n/**\n * System private API for dispatching intent broadcasts.  This is given to the\n * activity manager as part of registering for an intent broadcasts, and is\n * called when it receives intents.\n *\n */\ninterface IIntentReceiver {\n    void performReceive(in Intent intent, int resultCode, String data,\n            in Bundle extras, boolean ordered, boolean sticky, int sendingUser);\n}\n\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/ISyncAdapter.aidl",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.content;\n\nimport android.accounts.Account;\nimport android.os.Bundle;\nimport android.content.ISyncContext;\n\n/**\n * Interface used to control the sync activity on a SyncAdapter\n */\ninterface ISyncAdapter {\n    /**\n     * Initiate a sync for this account. SyncAdapter-specific parameters may\n     * be specified in extras, which is guaranteed to not be null.\n     *\n     * @param syncContext the ISyncContext used to indicate the progress of the sync. When\n     *   the sync is finished (successfully or not) ISyncContext.onFinished() must be called.\n     * @param authority the authority that should be synced\n     * @param account the account that should be synced\n     * @param extras SyncAdapter-specific parameters\n     */\n    void startSync(ISyncContext syncContext, String authority,\n      in Account account, in Bundle extras);\n\n    /**\n     * Cancel the most recently initiated sync. Due to race conditions, this may arrive\n     * after the ISyncContext.onFinished() for that sync was called.\n     * @param syncContext the ISyncContext that was passed to {@link #startSync}\n     */\n    void cancelSync(ISyncContext syncContext);\n\n    /**\n     * Initialize the SyncAdapter for this account and authority.\n     *\n     * @param account the account that should be synced\n     * @param authority the authority that should be synced\n     */\n    void initialize(in Account account, String authority);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/ISyncContext.aidl",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.content;\n\nimport android.content.SyncResult;\n\n/**\n * Interface used by the SyncAdapter to indicate its progress.\n * @hide\n */\ninterface ISyncContext {\n    /**\n     * Call to indicate that the SyncAdapter is making progress. E.g., if this SyncAdapter\n     * downloads or sends records to/from the server, this may be called after each record\n     * is downloaded or uploaded.\n     */\n    void sendHeartbeat();\n\n    /**\n     * Signal that the corresponding sync session is completed.\n     * @param result information about this sync session\n     */\n    void onFinished(in SyncResult result);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/ISyncStatusObserver.aidl",
    "content": "package android.content;\n\n\ninterface ISyncStatusObserver {\n    void onStatusChanged(int which);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/pm/IPackageDataObserver.aidl",
    "content": "package android.content.pm;\n\n/**\n * API for package data change related callbacks from the Package Manager.\n * Some usage scenarios include deletion of cache directory, generate\n * statistics related to code, data, cache usage(TODO)\n */\ninterface IPackageDataObserver {\n    void onRemoveCompleted(in String packageName, boolean succeeded);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/pm/IPackageDeleteObserver2.aidl",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.content.pm;\n\nimport android.content.Intent;\n\ninterface IPackageDeleteObserver2 {\n    void onUserActionRequired(in Intent intent);\n    void onPackageDeleted(String packageName, int returnCode, String msg);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/pm/IPackageInstallObserver.aidl",
    "content": "package android.content.pm;\n\n/**\n * API for installation callbacks from the Package Manager.\n */\ninterface IPackageInstallObserver {\n    void packageInstalled(in String packageName, int returnCode);\n}\n\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/pm/IPackageInstallObserver2.aidl",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.content.pm;\n\nimport android.content.Intent;\nimport android.os.Bundle;\n\n/**\n * API for installation callbacks from the Package Manager.  In certain result cases\n * additional information will be provided.\n */\ninterface IPackageInstallObserver2 {\n    void onUserActionRequired(in Intent intent);\n\n    /**\n     * The install operation has completed.  {@code returnCode} holds a numeric code\n     * indicating success or failure.  In certain cases the {@code extras} Bundle will\n     * contain additional details:\n     *\n     * <p><table>\n     * <tr>\n     *   <td>INSTALL_FAILED_DUPLICATE_PERMISSION</td>\n     *   <td>Two strings are provided in the extras bundle: EXTRA_EXISTING_PERMISSION\n     *       is the name of the permission that the app is attempting to define, and\n     *       EXTRA_EXISTING_PACKAGE is the package name of the app which has already\n     *       defined the permission.</td>\n     * </tr>\n     * </table>\n     */\n    void onPackageInstalled(String basePackageName, int returnCode, String msg, in Bundle extras);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/pm/IPackageInstallerCallback.aidl",
    "content": "package android.content.pm;\n\ninterface IPackageInstallerCallback {\n    void onSessionCreated(int sessionId);\n    void onSessionBadgingChanged(int sessionId);\n    void onSessionActiveChanged(int sessionId, boolean active);\n    void onSessionProgressChanged(int sessionId, float progress);\n    void onSessionFinished(int sessionId, boolean success);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/content/pm/IPackageInstallerSession.aidl",
    "content": "package android.content.pm;\n\nimport android.content.pm.IPackageInstallObserver2;\nimport android.content.IntentSender;\nimport android.os.ParcelFileDescriptor;\n\ninterface IPackageInstallerSession {\n    void setClientProgress(float progress);\n    void addClientProgress(float progress);\n\n    String[] getNames();\n    ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);\n    ParcelFileDescriptor openRead(String name);\n\n    void removeSplit(String splitName);\n\n    void close();\n    void commit(in IntentSender statusReceiver);\n    void abandon();\n}\n"
  },
  {
    "path": "lib/src/main/aidl/android/net/IConnectivityManager.aidl",
    "content": "package android.net;\n\nimport android.net.NetworkInfo;\nimport android.net.LinkProperties;\n\ninterface IConnectivityManager {\n\n    NetworkInfo getActiveNetworkInfo();\n    NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked);\n\n    NetworkInfo getNetworkInfo(int networkType);\n    NetworkInfo[] getAllNetworkInfo();\n    boolean isActiveNetworkMetered();\n    boolean requestRouteToHostAddress(int networkType, int address);\n    LinkProperties getActiveLinkProperties();\n    LinkProperties getLinkProperties(int networkType);\n\n}"
  },
  {
    "path": "lib/src/main/aidl/android/net/wifi/IWifiScanner.aidl",
    "content": "package android.net.wifi;\n\nimport android.os.Messenger;\nimport android.os.Bundle;\n\ninterface IWifiScanner\n{\n    Messenger getMessenger();\n\n    Bundle getAvailableChannels(int band);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/client/IVClient.aidl",
    "content": "// IVClient.aidl\npackage com.lody.virtual.client;\n\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ProviderInfo;\n\nimport com.lody.virtual.remote.PendingResultData;\n\ninterface IVClient {\n    void scheduleReceiver(in String processName,in ComponentName component, in Intent intent, in PendingResultData resultData);\n    void scheduleNewIntent(in String creator, in IBinder token, in Intent intent);\n    void finishActivity(in IBinder token);\n    IBinder createProxyService(in ComponentName component, in IBinder binder);\n    IBinder acquireProviderClient(in ProviderInfo info);\n    IBinder getAppThread();\n    IBinder getToken();\n    String getDebugInfo();\n}"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/os/VUserInfo.aidl",
    "content": "// VUserInfo.aidl\npackage com.lody.virtual.os;\n\nparcelable VUserInfo;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/AppTaskInfo.aidl",
    "content": "// AppTaskInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable AppTaskInfo;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/InstallResult.aidl",
    "content": "// InstallResult.aidl\npackage com.lody.virtual.remote;\n\nparcelable InstallResult;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/InstalledAppInfo.aidl",
    "content": "// AppSetting.aidl\npackage com.lody.virtual.remote;\n\nparcelable InstalledAppInfo;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/PendingIntentData.aidl",
    "content": "// PendingIntentData.aidl\npackage com.lody.virtual.remote;\n\nparcelable PendingIntentData;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/PendingResultData.aidl",
    "content": "// PendingResultData.aidl\npackage com.lody.virtual.remote;\n\nparcelable PendingResultData;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/Problem.aidl",
    "content": "// Problem.aidl\npackage com.lody.virtual.remote;\n\nparcelable Problem;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/ReceiverInfo.aidl",
    "content": "// ReceiverInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable ReceiverInfo;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/VDeviceInfo.aidl",
    "content": "// VDeviceInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable VDeviceInfo;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/remote/VParceledListSlice.aidl",
    "content": "// VParceledListSlice.aidl\npackage com.lody.virtual.remote;\n\nparcelable VParceledListSlice;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IAccountManager.aidl",
    "content": "package com.lody.virtual.server;\n\nimport android.accounts.IAccountManagerResponse;\nimport android.accounts.Account;\nimport android.accounts.AuthenticatorDescription;\nimport android.os.Bundle;\n\n\n/**\n * Central application service that provides account management.\n * @hide\n */\ninterface IAccountManager {\n    AuthenticatorDescription[] getAuthenticatorTypes(int userId);\n    void getAccountsByFeatures(int userId, in IAccountManagerResponse response, in String type, in String[] features);\n    String getPreviousName(int userId, in Account account);\n    Account[] getAccounts(int userId, in String type);\n    void getAuthToken(int userId, in IAccountManagerResponse response, in Account account, in String authTokenType, in boolean notifyOnAuthFailure, in boolean expectActivityLaunch, in Bundle loginOptions);\n    void setPassword(int userId, in Account account, in String password);\n    void setAuthToken(int userId, in Account account, in String authTokenType, in String authToken);\n    void setUserData(int userId, in Account account, in String key, in String value);\n    void hasFeatures(int userId, in IAccountManagerResponse response,\n    \t\t\t\t\t\t\tin Account account, in String[] features);\n    void updateCredentials(int userId, in IAccountManagerResponse response, in Account account,\n    \t\t\t\t\t\t\t\t  in String authTokenType, in boolean expectActivityLaunch,\n    \t\t\t\t\t\t\t\t  in Bundle loginOptions);\n    void editProperties(int userId, in IAccountManagerResponse response, in String accountType,\n    \t\t\t\t\t\t\t   in boolean expectActivityLaunch);\n    void getAuthTokenLabel(int userId, in IAccountManagerResponse response, in String accountType,\n    \t\t\t\t\t\t\t\t  in String authTokenType);\n    String getUserData(int userId, in Account account, in String key);\n    String getPassword(int userId, in Account account);\n    void confirmCredentials(int userId, in IAccountManagerResponse response, in Account account, in Bundle options, in boolean expectActivityLaunch);\n    void addAccount(int userId, in IAccountManagerResponse response, in String accountType,\n    \t\t\t\t\t\t   in String authTokenType, in String[] requiredFeatures,\n    \t\t\t\t\t\t   in boolean expectActivityLaunch, in Bundle optionsIn);\n    boolean addAccountExplicitly(int userId, in Account account, in String password, in Bundle extras);\n    boolean removeAccountExplicitly(int userId, in Account account);\n    void renameAccount(int userId, in IAccountManagerResponse response, in Account accountToRename, in String newName);\n    void removeAccount(in int userId, in IAccountManagerResponse response, in Account account,\n    \t\t\t\t\t\t\t  in boolean expectActivityLaunch);\n    void clearPassword(int userId, in Account account);\n    boolean accountAuthenticated(int userId, in Account account);\n    void invalidateAuthToken(int userId, in String accountType, in String authToken);\n    String peekAuthToken(int userId, in Account account, in String authTokenType);\n\n}"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IActivityManager.aidl",
    "content": "// IActivityManager.aidl\npackage com.lody.virtual.server;\n\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.remote.AppTaskInfo;\nimport com.lody.virtual.remote.PendingIntentData;\nimport com.lody.virtual.remote.PendingResultData;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.app.Notification;\nimport android.app.IServiceConnection;\nimport android.app.IActivityManager.ContentProviderHolder;\nimport com.lody.virtual.server.interfaces.IProcessObserver;\n\n\ninterface IActivityManager {\n\n    int initProcess(in String packageName, in String processName, int userId);\n\n    int getFreeStubCount();\n\n    int getSystemPid();\n\n    int getUidByPid(int pid);\n\n    boolean isAppProcess(String processName);\n\n    boolean isAppRunning(String packageName, int userId);\n\n    boolean isAppPid(int pid);\n\n    String getAppProcessName(int pid);\n\n    List<String> getProcessPkgList(int pid);\n\n    void killAllApps();\n\n    void killAppByPkg(String pkg, int userId);\n\n    void killApplicationProcess(String procName, int vuid);\n\n    void dump();\n\n    void registerProcessObserver(in IProcessObserver observer);\n\n    void unregisterProcessObserver(in IProcessObserver observer);\n\n    String getInitialPackage(int pid);\n\n    void handleApplicationCrash();\n\n    void appDoneExecuting();\n\n    int startActivities(in Intent[] intents, in String[] resolvedTypes, in IBinder token, in Bundle options, in int userId);\n\n    int startActivity(in Intent intent, in ActivityInfo info, in IBinder resultTo, in Bundle options, String resultWho, int requestCode, int userId);\n\n    void onActivityCreated(in ComponentName component, in ComponentName caller, in IBinder token, in Intent intent, in String affinity, int taskId, int launchMode, int flags);\n\n    void onActivityResumed(int userId, in IBinder token);\n\n    boolean onActivityDestroyed(int userId, in IBinder token);\n\n    ComponentName getActivityClassForToken(int userId, in IBinder token);\n\n    String getCallingPackage(int userId, in IBinder token);\n\n    ComponentName getCallingActivity(int userId, in IBinder token);\n\n    AppTaskInfo getTaskInfo(int taskId);\n\n    String getPackageForToken(int userId, in IBinder token);\n\n    boolean isVAServiceToken(in IBinder token);\n\n    ComponentName startService(in IBinder caller,in Intent service, String resolvedType, int userId);\n\n    int stopService(in IBinder caller, in Intent service, String resolvedType, int userId);\n\n    boolean stopServiceToken(in ComponentName className, in IBinder token, int startId, int userId);\n\n    void setServiceForeground(in ComponentName className, in IBinder token, int id,\n                            in Notification notification, boolean removeNotification, int userId);\n\n    int bindService(in IBinder caller, in IBinder token, in Intent service,\n                    String resolvedType, in IServiceConnection connection, int flags, int userId);\n\n    boolean unbindService(in IServiceConnection connection, int userId);\n\n    void unbindFinished(in IBinder token, in Intent service, in boolean doRebind, int userId);\n\n    void serviceDoneExecuting(in IBinder token, in int type, in int startId, in int res, int userId);\n\n    IBinder peekService(in Intent service, String resolvedType, int userId);\n\n    void publishService(in IBinder token, in Intent intent, in IBinder service, int userId);\n\n    VParceledListSlice getServices(int maxNum, int flags, int userId);\n\n    IBinder acquireProviderClient(int userId, in ProviderInfo info);\n\n    PendingIntentData getPendingIntent(IBinder binder);\n\n    void addPendingIntent(IBinder binder, String packageName);\n\n    void removePendingIntent(IBinder binder);\n\n    String getPackageForIntentSender(IBinder binder);\n\n    void processRestarted(in String packageName, in String processName, int userId);\n\n    void broadcastFinish(in PendingResultData res);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IAppManager.aidl",
    "content": "// IAppManager.aidl\npackage com.lody.virtual.server;\n\nimport com.lody.virtual.server.interfaces.IPackageObserver;\nimport com.lody.virtual.server.interfaces.IAppRequestListener;\nimport com.lody.virtual.remote.InstalledAppInfo;\nimport com.lody.virtual.remote.InstallResult;\n\ninterface IAppManager {\n    int[] getPackageInstalledUsers(String packageName);\n    void scanApps();\n    void addVisibleOutsidePackage(String pkg);\n    void removeVisibleOutsidePackage(String pkg);\n    boolean isOutsidePackageVisible(String pkg);\n    InstalledAppInfo getInstalledAppInfo(String pkg, int flags);\n    InstallResult installPackage(String path, int flags);\n    boolean isPackageLaunched(int userId, String packageName);\n    void setPackageHidden(int userId, String packageName, boolean hidden);\n    boolean installPackageAsUser(int userId, String packageName);\n    boolean uninstallPackageAsUser(String packageName, int userId);\n    boolean uninstallPackage(String packageName);\n    List<InstalledAppInfo> getInstalledApps(int flags);\n    List<InstalledAppInfo> getInstalledAppsAsUser(int userId, int flags);\n    int getInstalledAppCount();\n    boolean isAppInstalled(String packageName);\n    boolean isAppInstalledAsUser(int userId, String packageName);\n\n    void registerObserver(IPackageObserver observer);\n    void unregisterObserver(IPackageObserver observer);\n\n    void setAppRequestListener(IAppRequestListener listener);\n    void clearAppRequestListener();\n    IAppRequestListener getAppRequestListener();\n\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IBinderDelegateService.aidl",
    "content": "// IBinderDelegateService.aidl\npackage com.lody.virtual.server;\n\nimport android.content.ComponentName;\n\ninterface IBinderDelegateService {\n\n   ComponentName getComponent();\n\n   IBinder getService();\n\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IDeviceInfoManager.aidl",
    "content": "// IDeviceInfoManager.aidl\npackage com.lody.virtual.server;\n\nimport com.lody.virtual.remote.VDeviceInfo;\n\ninterface IDeviceInfoManager {\n\n    VDeviceInfo getDeviceInfo(int userId);\n\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IJobScheduler.aidl",
    "content": "package com.lody.virtual.server;\n\nimport android.app.job.JobInfo;\n\n /**\n  * IPC interface that supports the app-facing {@link #JobScheduler} api.\n  */\ninterface IJobScheduler {\n    int schedule(in JobInfo job);\n    void cancel(int jobId);\n    void cancelAll();\n    List<JobInfo> getAllPendingJobs();\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/INotificationManager.aidl",
    "content": "// INotificationManager.aidl\npackage com.lody.virtual.server;\n\n// Declare any non-default types here with import statements\nimport android.app.Notification;\n\ninterface INotificationManager {\n    int dealNotificationId(int id, String packageName, String tag, int userId);\n    String dealNotificationTag(int id, String packageName, String tag, int userId);\n    boolean areNotificationsEnabledForPackage(String packageName, int userId);\n    void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId);\n    void addNotification(int id, String tag, String packageName, int userId);\n    void cancelAllNotification(String packageName, int userId);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IPackageInstaller.aidl",
    "content": "package com.lody.virtual.server;\n\nimport android.content.pm.IPackageDeleteObserver2;\nimport android.content.pm.IPackageInstallerCallback;\nimport android.content.pm.IPackageInstallerSession;\nimport android.content.IntentSender;\nimport android.graphics.Bitmap;\n\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.pm.installer.SessionParams;\nimport com.lody.virtual.server.pm.installer.SessionInfo;\n\ninterface IPackageInstaller {\n    int createSession(in SessionParams params, String installerPackageName, int userId);\n\n    void updateSessionAppIcon(int sessionId, in Bitmap appIcon);\n    void updateSessionAppLabel(int sessionId, String appLabel);\n\n    void abandonSession(int sessionId);\n\n    IPackageInstallerSession openSession(int sessionId);\n\n    SessionInfo getSessionInfo(int sessionId);\n\n    VParceledListSlice getAllSessions(int userId);\n    VParceledListSlice getMySessions(String installerPackageName, int userId);\n\n    void registerCallback(IPackageInstallerCallback callback, int userId);\n    void unregisterCallback(IPackageInstallerCallback callback);\n\n    void uninstall(String packageName, String callerPackageName, int flags,\n            in IntentSender statusReceiver, int userId);\n\n    void setPermissionsResult(int sessionId, boolean accepted);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IPackageInstallerSession.aidl",
    "content": "package com.lody.virtual.server;\n\nimport android.content.IntentSender;\nimport android.os.ParcelFileDescriptor;\n\ninterface IPackageInstallerSession {\n    void setClientProgress(float progress);\n    void addClientProgress(float progress);\n\n    String[] getNames();\n    ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);\n    ParcelFileDescriptor openRead(String name);\n    void close();\n    void commit(in IntentSender statusReceiver);\n    void abandon();\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IPackageManager.aidl",
    "content": "// IPackageManager.aidl\npackage com.lody.virtual.server;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.IntentFilter;\nimport android.content.pm.PermissionInfo;\nimport android.content.pm.PermissionGroupInfo;\n\nimport com.lody.virtual.remote.ReceiverInfo;\nimport com.lody.virtual.remote.VParceledListSlice;\n\nimport com.lody.virtual.server.IPackageInstaller;\n\ninterface IPackageManager {\n\n        int getPackageUid(String packageName, int userId);\n\n        String[] getPackagesForUid(int vuid);\n\n        List<String> getSharedLibraries(String pkgName);\n\n        int checkPermission(String permName, String pkgName, int userId);\n\n        PackageInfo getPackageInfo(String packageName, int flags, int userId);\n\n        ActivityInfo getActivityInfo(in ComponentName componentName, int flags, int userId);\n\n        boolean activitySupportsIntent(in ComponentName component, in Intent intent,\n                                           \t\t\t\t\t\t\t\t\t\t  in String resolvedType);\n         ActivityInfo getReceiverInfo(in ComponentName componentName, int flags, int userId);\n\n         ServiceInfo getServiceInfo(in ComponentName componentName, int flags, int userId);\n\n         ProviderInfo getProviderInfo(in ComponentName componentName, int flags, int userId);\n\n         ResolveInfo resolveIntent(in Intent intent, in String resolvedType, int flags, int userId);\n\n         List<ResolveInfo> queryIntentActivities(in Intent intent,in  String resolvedType, int flags, int userId);\n\n         List<ResolveInfo> queryIntentReceivers(in Intent intent, String resolvedType, int flags, int userId);\n\n         ResolveInfo resolveService(in Intent intent, String resolvedType, int flags, int userId);\n\n         List<ResolveInfo> queryIntentServices(in Intent intent, String resolvedType, int flags, int userId);\n\n         List<ResolveInfo> queryIntentContentProviders(in Intent intent, String resolvedType, int flags, int userId);\n\n         VParceledListSlice getInstalledPackages(int flags, int userId);\n\n         VParceledListSlice getInstalledApplications(int flags, int userId);\n\n         PermissionInfo getPermissionInfo(in String name, int flags);\n\n         List<PermissionInfo> queryPermissionsByGroup(in String group, int flags);\n\n         PermissionGroupInfo getPermissionGroupInfo(in String name, int flags);\n\n         List<PermissionGroupInfo> getAllPermissionGroups(int flags);\n\n         ProviderInfo resolveContentProvider(in String name, int flags, int userId);\n\n         ApplicationInfo getApplicationInfo(in String packageName, int flags, int userId);\n\n         VParceledListSlice queryContentProviders(in String processName, int vuid, int flags);\n\n         List<String> querySharedPackages(in String packageName);\n\n         String getNameForUid(int uid);\n\n         IPackageInstaller getPackageInstaller();\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IUserManager.aidl",
    "content": "package com.lody.virtual.server;\n\nimport android.os.ParcelFileDescriptor;\nimport com.lody.virtual.os.VUserInfo;\nimport android.graphics.Bitmap;\n\n/**\n*\n */\ninterface IUserManager {\n    VUserInfo createUser(in String name, int flags);\n    boolean removeUser(int userHandle);\n    void setUserName(int userHandle, String name);\n    void setUserIcon(int userHandle, in Bitmap icon);\n    Bitmap getUserIcon(int userHandle);\n    List<VUserInfo> getUsers(boolean excludeDying);\n    VUserInfo getUserInfo(int userHandle);\n    void setGuestEnabled(boolean enable);\n    boolean isGuestEnabled();\n    void wipeUser(int userHandle);\n    int getUserSerialNumber(int userHandle);\n    int getUserHandle(int userSerialNumber);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/IVirtualStorageService.aidl",
    "content": "// IVirtualStorageService.aidl\npackage com.lody.virtual.server;\n\n\ninterface IVirtualStorageService {\n\n   void setVirtualStorage(in String packageName, in int userId, in String vsPath);\n\n   String getVirtualStorage(in String packageName, in int userId);\n\n   void setVirtualStorageState(in String packageName, in int userId, in boolean enable);\n\n   boolean isVirtualStorageEnable(in String packageName, in int userId);\n\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/interfaces/IAppRequestListener.aidl",
    "content": "// IAppRequestListener.aidl\npackage com.lody.virtual.server.interfaces;\n\ninterface IAppRequestListener {\n    void onRequestInstall(in String path);\n    void onRequestUninstall(in String pkg);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/interfaces/IIntentFilterObserver.aidl",
    "content": "// IIntentFilterObserver.aidl\npackage com.lody.virtual.server.interfaces;\n\n// Declare any non-default types here with import statements\n\ninterface IIntentFilterObserver {\n    /**\n     * Demonstrates some basic types that you can use as parameters\n     * and return values in AIDL.\n     */\n\n     Intent filter(in Intent intent);\n     void setCallBack(IBinder callBack);\n     IBinder getCallBack();\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/interfaces/IPackageObserver.aidl",
    "content": "// IPackageObserver.aidl\npackage com.lody.virtual.server.interfaces;\n\ninterface IPackageObserver {\n    void onPackageInstalled(in String packageName);\n    void onPackageUninstalled(in String packageName);\n    void onPackageInstalledAsUser(in int userId, in String packageName);\n    void onPackageUninstalledAsUser(in int userId, in String packageName);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/interfaces/IProcessObserver.aidl",
    "content": "// IProcessObserver.aidl\npackage com.lody.virtual.server.interfaces;\n\ninterface IProcessObserver {\n    void onProcessCreated(in String pkg, in String processName);\n\n    void onProcessDied(in String pkg, in String processName);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/interfaces/IServiceFetcher.aidl",
    "content": "// IServiceFetcher.aidl\npackage com.lody.virtual.server.interfaces;\n\ninterface IServiceFetcher {\n    IBinder getService(String name);\n    void addService(String name,in IBinder service);\n    void removeService(String name);\n}"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/interfaces/IUiCallback.aidl",
    "content": "// IUiCallback.aidl\npackage com.lody.virtual.server.interfaces;\n\ninterface IUiCallback {\n    void onAppOpened(in String packageName, in int userId);\n}\n"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/pm/installer/SessionInfo.aidl",
    "content": "// SessionInfo.aidl\npackage com.lody.virtual.server.pm.installer;\n\nparcelable SessionInfo;"
  },
  {
    "path": "lib/src/main/aidl/com/lody/virtual/server/pm/installer/SessionParams.aidl",
    "content": "// SessionParams.aidl\npackage com.lody.virtual.server.pm.installer;\n\nparcelable SessionParams;"
  },
  {
    "path": "lib/src/main/java/android/content/SyncStatusInfo.java",
    "content": "package android.content;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.Log;\n\nimport java.util.ArrayList;\n\npublic class SyncStatusInfo implements Parcelable {\n    static final int VERSION = 2;\n\n    public final int authorityId;\n    public long totalElapsedTime;\n    public int numSyncs;\n    public int numSourcePoll;\n    public int numSourceServer;\n    public int numSourceLocal;\n    public int numSourceUser;\n    public int numSourcePeriodic;\n    public long lastSuccessTime;\n    public int lastSuccessSource;\n    public long lastFailureTime;\n    public int lastFailureSource;\n    public String lastFailureMesg;\n    public long initialFailureTime;\n    public boolean pending;\n    public boolean initialize;\n    \n  // Warning: It is up to the external caller to ensure there are\n  // no race conditions when accessing this list\n  private ArrayList<Long> periodicSyncTimes;\n\n    private static final String TAG = \"Sync\";\n\n    public SyncStatusInfo(int authorityId) {\n        this.authorityId = authorityId;\n    }\n\n    public int getLastFailureMesgAsInt(int def) {\n        return 0;\n    }\n\n    public int describeContents() {\n        return 0;\n    }\n\n    public void writeToParcel(Parcel parcel, int flags) {\n        parcel.writeInt(VERSION);\n        parcel.writeInt(authorityId);\n        parcel.writeLong(totalElapsedTime);\n        parcel.writeInt(numSyncs);\n        parcel.writeInt(numSourcePoll);\n        parcel.writeInt(numSourceServer);\n        parcel.writeInt(numSourceLocal);\n        parcel.writeInt(numSourceUser);\n        parcel.writeLong(lastSuccessTime);\n        parcel.writeInt(lastSuccessSource);\n        parcel.writeLong(lastFailureTime);\n        parcel.writeInt(lastFailureSource);\n        parcel.writeString(lastFailureMesg);\n        parcel.writeLong(initialFailureTime);\n        parcel.writeInt(pending ? 1 : 0);\n        parcel.writeInt(initialize ? 1 : 0);\n        if (periodicSyncTimes != null) {\n            parcel.writeInt(periodicSyncTimes.size());\n            for (long periodicSyncTime : periodicSyncTimes) {\n                parcel.writeLong(periodicSyncTime);\n            }\n        } else {\n            parcel.writeInt(-1);\n        }\n    }\n\n    public SyncStatusInfo(Parcel parcel) {\n        int version = parcel.readInt();\n        if (version != VERSION && version != 1) {\n            Log.w(\"SyncStatusInfo\", \"Unknown version: \" + version);\n        }\n        authorityId = parcel.readInt();\n        totalElapsedTime = parcel.readLong();\n        numSyncs = parcel.readInt();\n        numSourcePoll = parcel.readInt();\n        numSourceServer = parcel.readInt();\n        numSourceLocal = parcel.readInt();\n        numSourceUser = parcel.readInt();\n        lastSuccessTime = parcel.readLong();\n        lastSuccessSource = parcel.readInt();\n        lastFailureTime = parcel.readLong();\n        lastFailureSource = parcel.readInt();\n        lastFailureMesg = parcel.readString();\n        initialFailureTime = parcel.readLong();\n        pending = parcel.readInt() != 0;\n        initialize = parcel.readInt() != 0;\n        if (version == 1) {\n            periodicSyncTimes = null;\n        } else {\n            int N = parcel.readInt();\n            if (N < 0) {\n                periodicSyncTimes = null;\n            } else {\n                periodicSyncTimes = new ArrayList<Long>();\n                for (int i=0; i<N; i++) {\n                    periodicSyncTimes.add(parcel.readLong());\n                }\n            }\n        }\n    }\n\n    public SyncStatusInfo(SyncStatusInfo other) {\n        authorityId = other.authorityId;\n        totalElapsedTime = other.totalElapsedTime;\n        numSyncs = other.numSyncs;\n        numSourcePoll = other.numSourcePoll;\n        numSourceServer = other.numSourceServer;\n        numSourceLocal = other.numSourceLocal;\n        numSourceUser = other.numSourceUser;\n        numSourcePeriodic = other.numSourcePeriodic;\n        lastSuccessTime = other.lastSuccessTime;\n        lastSuccessSource = other.lastSuccessSource;\n        lastFailureTime = other.lastFailureTime;\n        lastFailureSource = other.lastFailureSource;\n        lastFailureMesg = other.lastFailureMesg;\n        initialFailureTime = other.initialFailureTime;\n        pending = other.pending;\n        initialize = other.initialize;\n        if (other.periodicSyncTimes != null) {\n            periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);\n        }\n    }\n\n    public void setPeriodicSyncTime(int index, long when) {\n        // The list is initialized lazily when scheduling occurs so we need to make sure\n        // we initialize elements < index to zero (zero is ignore for scheduling purposes)\n        ensurePeriodicSyncTimeSize(index);\n        periodicSyncTimes.set(index, when);\n    }\n\n    public long getPeriodicSyncTime(int index) {\n        if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {\n            return periodicSyncTimes.get(index);\n        } else {\n            return 0;\n        }\n    }\n\n    public void removePeriodicSyncTime(int index) {\n        if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {\n            periodicSyncTimes.remove(index);\n        }\n    }\n\n    public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {\n        public SyncStatusInfo createFromParcel(Parcel in) {\n            return new SyncStatusInfo(in);\n        }\n\n        public SyncStatusInfo[] newArray(int size) {\n            return new SyncStatusInfo[size];\n        }\n    };\n\n    private void ensurePeriodicSyncTimeSize(int index) {\n        if (periodicSyncTimes == null) {\n            periodicSyncTimes = new ArrayList<>(0);\n        }\n\n        final int requiredSize = index + 1;\n        if (periodicSyncTimes.size() < requiredSize) {\n            for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {\n                periodicSyncTimes.add((long) 0);\n            }\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/android/content/pm/PackageParser.java",
    "content": "package android.content.pm;\n\nimport android.content.ComponentName;\nimport android.content.IntentFilter;\nimport android.os.Bundle;\n\nimport java.util.ArrayList;\n\n/**\n * @author Lody\n */\npublic class PackageParser {\n\n    public static final int PARSE_IS_SYSTEM = 1;\n\n    public static class IntentInfo extends IntentFilter {\n        public boolean hasDefault;\n        public int labelRes;\n        public CharSequence nonLocalizedLabel;\n        public int icon;\n        public int logo;\n        public int banner;\n    }\n\n    public static class Component<II extends IntentInfo> {\n        public Package owner;\n        public ArrayList<II> intents;\n        public String className;\n        public Bundle metaData;\n\n        public ComponentName getComponentName() {\n            return null;\n        }\n    }\n\n    public final static class Activity extends Component<ActivityIntentInfo> {\n        public ActivityInfo info;\n    }\n\n    // 中间包组件结构\n    public class Package {\n        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);\n        // Receiver 结构与 Activity 类似\n        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);\n        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);\n        public final ArrayList<Service> services = new ArrayList<Service>(0);\n        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);\n        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);\n        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);\n        public final ArrayList<String> requestedPermissions = new ArrayList<String>();\n        public Signature[] mSignatures;\n        public Bundle mAppMetaData;\n        public Object mExtras;\n        public String packageName;\n        public int mPreferredOrder;\n        public String mSharedUserId;\n        public ArrayList<String> usesLibraries;\n        public int mVersionCode;\n        public ApplicationInfo applicationInfo;\n        public String mVersionName;\n\n        // Applications hardware preferences\n        public ArrayList<ConfigurationInfo> configPreferences = null;\n\n        // Applications requested features\n        public ArrayList<FeatureInfo> reqFeatures = null;\n        public int mSharedUserLabel;\n    }\n\n    public final class Service extends Component<ServiceIntentInfo> {\n        public ServiceInfo info;\n    }\n\n    public final class Provider extends Component<ProviderIntentInfo> {\n        public ProviderInfo info;\n    }\n\n    public final class Instrumentation extends Component<IntentInfo> {\n        public InstrumentationInfo info;\n    }\n\n    public final class Permission extends Component<IntentInfo> {\n        public PermissionInfo info;\n    }\n\n    public final class PermissionGroup extends Component<IntentInfo> {\n        public PermissionGroupInfo info;\n    }\n\n    public class ActivityIntentInfo extends IntentInfo {\n        public Activity activity;\n    }\n\n\n    public class ServiceIntentInfo extends IntentInfo {\n        public Service service;\n    }\n\n    public class ProviderIntentInfo extends IntentInfo {\n        public Provider provider;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/Build.java",
    "content": "package com.lody.virtual;\n\n/**\n *\n * Version info of VirtualApp project.\n *\n * @author Lody\n *\n */\n\npublic class Build {\n\n    public static final String VERSION_NAME = \"Build-823-01\";\n\n    public static final int VERSION_CODE = 8230001;\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/GmsSupport.java",
    "content": "package com.lody.virtual;\n\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\n\nimport com.lody.virtual.client.core.InstallStrategy;\nimport com.lody.virtual.client.core.VirtualCore;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic class GmsSupport {\n\n    private static final List<String> GOOGLE_APP = Arrays.asList(\n            \"com.android.vending\",\n            \"com.google.android.play.games\",\n            \"com.google.android.wearable.app\",\n            \"com.google.android.wearable.app.cn\"\n    );\n\n    private static final List<String> GOOGLE_SERVICE = Arrays.asList(\n            \"com.google.android.gsf\",\n            \"com.google.android.gms\",\n            \"com.google.android.gsf.login\",\n            \"com.google.android.backuptransport\",\n            \"com.google.android.backup\",\n            \"com.google.android.configupdater\",\n            \"com.google.android.syncadapters.contacts\",\n            \"com.google.android.feedback\",\n            \"com.google.android.onetimeinitializer\",\n            \"com.google.android.partnersetup\",\n            \"com.google.android.setupwizard\",\n            \"com.google.android.syncadapters.calendar\"\n    );\n\n    public static boolean isGmsFamilyPackage(String packageName) {\n        return packageName.equals(\"com.android.vending\")\n                || packageName.equals(\"com.google.android.gms\");\n    }\n\n    public static boolean isGoogleFrameworkInstalled() {\n        return VirtualCore.get().isAppInstalled(\"com.google.android.gms\");\n    }\n\n    public static boolean isOutsideGoogleFrameworkExist() {\n        return VirtualCore.get().isOutsideInstalled(\"com.google.android.gms\");\n    }\n\n    private static void installPackages(List<String> list, int userId) {\n        VirtualCore core = VirtualCore.get();\n        for (String packageName : list) {\n            if (core.isAppInstalledAsUser(userId, packageName)) {\n                continue;\n            }\n            ApplicationInfo info = null;\n            try {\n                info = VirtualCore.get().getUnHookPackageManager().getApplicationInfo(packageName, 0);\n            } catch (PackageManager.NameNotFoundException e) {\n                // Ignore\n            }\n            if (info == null || info.sourceDir == null) {\n                continue;\n            }\n            if (userId == 0) {\n                core.installPackage(info.sourceDir, InstallStrategy.DEPEND_SYSTEM_IF_EXIST);\n            } else {\n                core.installPackageAsUser(userId, packageName);\n            }\n        }\n    }\n\n    public static void installGApps(int userId) {\n        installPackages(GOOGLE_SERVICE, userId);\n        installPackages(GOOGLE_APP, userId);\n    }\n\n    public static void installGoogleService(int userId) {\n        installPackages(GOOGLE_SERVICE, userId);\n    }\n\n    public static void installGoogleApp(int userId) {\n        installPackages(GOOGLE_APP, userId);\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/NativeEngine.java",
    "content": "package com.lody.virtual.client;\n\nimport android.annotation.SuppressLint;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Process;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.client.natives.NativeMethods;\nimport com.lody.virtual.helper.compat.BuildCompat;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.InstalledAppInfo;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * VirtualApp Native Project\n */\npublic class NativeEngine {\n\n    private static final String TAG = NativeEngine.class.getSimpleName();\n\n    private static Map<String, InstalledAppInfo> sDexOverrideMap;\n\n    private static boolean sFlag = false;\n\n    static {\n        try {\n            System.loadLibrary(\"va-native\");\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    static {\n        NativeMethods.init();\n    }\n\n\n    public static void startDexOverride() {\n        List<InstalledAppInfo> installedAppInfos = VirtualCore.get().getInstalledApps(0);\n        sDexOverrideMap = new HashMap<>(installedAppInfos.size());\n        for (InstalledAppInfo info : installedAppInfos) {\n            try {\n                sDexOverrideMap.put(new File(info.apkPath).getCanonicalPath(), info);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public static String getRedirectedPath(String origPath) {\n        try {\n            return nativeGetRedirectedPath(origPath);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n        return origPath;\n    }\n\n    public static String restoreRedirectedPath(String origPath) {\n        try {\n            return nativeRestoreRedirectedPath(origPath);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n        return origPath;\n    }\n\n    public static void redirectDirectory(String origPath, String newPath) {\n        if (!origPath.endsWith(\"/\")) {\n            origPath = origPath + \"/\";\n        }\n        if (!newPath.endsWith(\"/\")) {\n            newPath = newPath + \"/\";\n        }\n        try {\n            nativeRedirect(origPath, newPath);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    public static void redirectFile(String origPath, String newPath) {\n        if (origPath.endsWith(\"/\")) {\n            origPath = origPath.substring(0, origPath.length() - 1);\n        }\n        if (newPath.endsWith(\"/\")) {\n            newPath = newPath.substring(0, newPath.length() - 1);\n        }\n\n        try {\n            nativeRedirect(origPath, newPath);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    public static void readOnly(String path) {\n        try {\n            nativeReadOnly(path);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    public static void hook() {\n        try {\n            String soPath = String.format(\"/data/data/%s/lib/libva-native.so\", VirtualCore.get().getHostPkg());\n            if (!new File(soPath).exists()) {\n                throw new RuntimeException(\"Unable to find the so.\");\n            }\n            nativeStartUniformer(soPath, Build.VERSION.SDK_INT, BuildCompat.getPreviewSDKInt());\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    // hook native 函数，目前 openDexFile，Camera，AudioRecoder\n    static void hookNative() {\n        if (sFlag) {\n            return;\n        }\n        Method[] methods = {NativeMethods.gOpenDexFileNative, NativeMethods.gCameraNativeSetup, NativeMethods.gAudioRecordNativeCheckPermission};\n        try {\n            nativeHookNative(methods, VirtualCore.get().getHostPkg(), VirtualRuntime.isArt(), Build.VERSION.SDK_INT, NativeMethods.gCameraMethodType);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n        sFlag = true;\n    }\n\n    public static void onKillProcess(int pid, int signal) {\n        VLog.e(TAG, \"killProcess: pid = %d, signal = %d.\", pid, signal);\n        if (pid == android.os.Process.myPid()) {\n            VLog.e(TAG, VLog.getStackTraceString(new Throwable()));\n        }\n    }\n\n    public static int onGetCallingUid(int originUid) {\n        int callingPid = Binder.getCallingPid();\n        if (callingPid == Process.myPid()) {\n            return VClientImpl.get().getBaseVUid();\n        }\n        if (callingPid == VirtualCore.get().getSystemPid()) {\n            return Process.SYSTEM_UID;\n        }\n        int vuid = VActivityManager.get().getUidByPid(callingPid);\n        if (vuid != -1) {\n            return VUserHandle.getAppId(vuid);\n        }\n        VLog.d(TAG, \"Unknown uid: \" + callingPid);\n        return VClientImpl.get().getBaseVUid();\n    }\n\n    // hook 的 openDexFile 函数，native 层重定向到 java 层\n    public static void onOpenDexFileNative(String[] params) {\n        String dexOrJarPath = params[0];\n        String outputPath = params[1];\n        VLog.d(TAG, \"DexOrJarPath = %s, OutputPath = %s.\", dexOrJarPath, outputPath);\n        try {\n            String canonical = new File(dexOrJarPath).getCanonicalPath();\n            InstalledAppInfo info = sDexOverrideMap.get(canonical);\n            if (info != null && !info.dependSystem) {\n                outputPath = info.getOdexFile().getPath();\n                params[1] = outputPath;\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n\n    private static native void nativeHookNative(Object method, String hostPackageName, boolean isArt, int apiLevel, int cameraMethodType);\n\n    private static native void nativeMark();\n\n    private static native String nativeRestoreRedirectedPath(String redirectedPath);\n\n    private static native String nativeGetRedirectedPath(String orgPath);\n\n    private static native void nativeRedirect(String origPath, String newPath);\n\n    private static native void nativeReadOnly(String path);\n\n    private static native void nativeStartUniformer(String selfSoPath, int apiLevel, int previewApiLevel);\n\n    public static int onGetUid(int uid) {\n        return VClientImpl.get().getBaseVUid();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/VClientImpl.java",
    "content": "package com.lody.virtual.client;\n\nimport android.annotation.SuppressLint;\nimport android.app.Application;\nimport android.app.Instrumentation;\nimport android.content.BroadcastReceiver;\nimport android.content.ComponentName;\nimport android.content.ContentProviderClient;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ProviderInfo;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.ConditionVariable;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.os.StrictMode;\nimport android.util.Log;\n\nimport com.lody.virtual.client.core.CrashHandler;\nimport com.lody.virtual.client.core.InvocationStubManager;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.SpecialComponentList;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.fixer.ContextFixer;\nimport com.lody.virtual.client.hook.delegate.AppInstrumentation;\nimport com.lody.virtual.client.hook.providers.ProviderHook;\nimport com.lody.virtual.client.hook.proxies.am.HCallbackStub;\nimport com.lody.virtual.client.hook.secondary.ProxyServiceFactory;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.client.ipc.VDeviceManager;\nimport com.lody.virtual.client.ipc.VPackageManager;\nimport com.lody.virtual.client.ipc.VirtualStorageManager;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.compat.BuildCompat;\nimport com.lody.virtual.helper.compat.StorageManagerCompat;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.InstalledAppInfo;\nimport com.lody.virtual.remote.PendingResultData;\nimport com.lody.virtual.remote.VDeviceInfo;\nimport com.taobao.android.dex.interpret.ARTUtils;\nimport com.taobao.android.runtime.DalvikUtils;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport mirror.android.app.ActivityThread;\nimport mirror.android.app.ActivityThreadNMR1;\nimport mirror.android.app.ContextImpl;\nimport mirror.android.app.IActivityManager;\nimport mirror.android.app.LoadedApk;\nimport mirror.android.content.ContentProviderHolderOreo;\nimport mirror.android.providers.Settings;\nimport mirror.android.renderscript.RenderScriptCacheDir;\nimport mirror.android.view.HardwareRenderer;\nimport mirror.android.view.RenderScript;\nimport mirror.android.view.ThreadedRenderer;\nimport mirror.com.android.internal.content.ReferrerIntent;\nimport mirror.dalvik.system.VMRuntime;\nimport mirror.java.lang.ThreadGroupN;\n\nimport static com.lody.virtual.os.VUserHandle.getUserId;\n\n/**\n * @author Lody\n */\n\npublic final class VClientImpl extends IVClient.Stub {\n\n    private static final int NEW_INTENT = 11;\n    private static final int RECEIVER = 12;\n\n    private static final String TAG = VClientImpl.class.getSimpleName();\n    @SuppressLint(\"StaticFieldLeak\")\n    private static final VClientImpl gClient = new VClientImpl();\n    private final H mH = new H();\n    private ConditionVariable mTempLock;\n    private Instrumentation mInstrumentation = AppInstrumentation.getDefault();\n    private IBinder token;\n    private int vuid;\n    private VDeviceInfo deviceInfo;\n    private AppBindData mBoundApplication;\n    private Application mInitialApplication;\n    private CrashHandler crashHandler;\n\n    public static VClientImpl get() {\n        return gClient;\n    }\n\n    public boolean isBound() {\n        return mBoundApplication != null;\n    }\n\n    public VDeviceInfo getDeviceInfo() {\n        return deviceInfo;\n    }\n\n    public Application getCurrentApplication() {\n        return mInitialApplication;\n    }\n\n    public String getCurrentPackage() {\n        return mBoundApplication != null ? mBoundApplication.appInfo.packageName : null;\n    }\n\n    public ApplicationInfo getCurrentApplicationInfo() {\n        return mInitialApplication != null ? mInitialApplication.getApplicationInfo() : null;\n    }\n\n    public CrashHandler getCrashHandler() {\n        return crashHandler;\n    }\n\n    public void setCrashHandler(CrashHandler crashHandler) {\n        this.crashHandler = crashHandler;\n    }\n\n    public int getVUid() {\n        return vuid;\n    }\n\n    public int getBaseVUid() {\n        return VUserHandle.getAppId(vuid);\n    }\n\n    public ClassLoader getClassLoader(ApplicationInfo appInfo) {\n        Context context = createPackageContext(appInfo.packageName);\n        return context.getClassLoader();\n    }\n\n    private void sendMessage(int what, Object obj) {\n        Message msg = Message.obtain();\n        msg.what = what;\n        msg.obj = obj;\n        mH.sendMessage(msg);\n    }\n\n    @Override\n    public IBinder getAppThread() {\n        return ActivityThread.getApplicationThread.call(VirtualCore.mainThread());\n    }\n\n    @Override\n    public IBinder getToken() {\n        return token;\n    }\n\n    public void initProcess(IBinder token, int vuid) {\n        this.token = token;\n        this.vuid = vuid;\n        this.deviceInfo = VDeviceManager.get().getDeviceInfo(getUserId(vuid));\n    }\n\n    private void handleNewIntent(NewIntentData data) {\n        Intent intent;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {\n            intent = ReferrerIntent.ctor.newInstance(data.intent, data.creator);\n        } else {\n            intent = data.intent;\n        }\n        if (ActivityThread.performNewIntents != null) {\n            ActivityThread.performNewIntents.call(\n                    VirtualCore.mainThread(),\n                    data.token,\n                    Collections.singletonList(intent)\n            );\n        } else {\n            ActivityThreadNMR1.performNewIntents.call(\n                    VirtualCore.mainThread(),\n                    data.token,\n                    Collections.singletonList(intent),\n                    true);\n        }\n    }\n\n    public void bindApplication(final String packageName, final String processName) {\n        if (Looper.getMainLooper() == Looper.myLooper()) {\n            bindApplicationNoCheck(packageName, processName, new ConditionVariable());\n        } else {\n            final ConditionVariable lock = new ConditionVariable();\n            VirtualRuntime.getUIHandler().post(new Runnable() {\n                @Override\n                public void run() {\n                    bindApplicationNoCheck(packageName, processName, lock);\n                    lock.open();\n                }\n            });\n            lock.block();\n        }\n    }\n\n\n    // 该逻辑参考 framework 的 ActivityThread.handleBindApplication\n    private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) {\n        mTempLock = lock;\n        try {\n            // 设置未捕获异常的 Callback\n            setupUncaughtHandler();\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        try {\n            // 修复 Provider 信息\n            fixInstalledProviders();\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        mirror.android.os.Build.SERIAL.set(deviceInfo.serial);\n        mirror.android.os.Build.DEVICE.set(Build.DEVICE.replace(\" \", \"_\"));\n        ActivityThread.mInitialApplication.set(\n                VirtualCore.mainThread(),\n                null\n        );\n        // 从 VPMS 获取 apk 信息\n        AppBindData data = new AppBindData();\n        InstalledAppInfo info = VirtualCore.get().getInstalledAppInfo(packageName, 0);\n        if (info == null) {\n            new Exception(\"App not exist!\").printStackTrace();\n            Process.killProcess(0);\n            System.exit(0);\n        }\n        // dex 优化的开关，dalvik 和 art 处理不同\n        if (!info.dependSystem && info.skipDexOpt) {\n            VLog.d(TAG, \"Dex opt skipped.\");\n            if (VirtualRuntime.isArt()) {\n                ARTUtils.init(VirtualCore.get().getContext());\n                ARTUtils.setIsDex2oatEnabled(false);\n            } else {\n                DalvikUtils.init();\n                DalvikUtils.setDexOptMode(DalvikUtils.OPTIMIZE_MODE_NONE);\n            }\n        }\n        data.appInfo = VPackageManager.get().getApplicationInfo(packageName, 0, getUserId(vuid));\n        data.processName = processName;\n        data.providers = VPackageManager.get().queryContentProviders(processName, getVUid(), PackageManager.GET_META_DATA);\n        Log.i(TAG, \"Binding application \" + data.appInfo.packageName + \" (\" + data.processName + \")\");\n        mBoundApplication = data;\n        // 主要设置进程的名字\n        VirtualRuntime.setupRuntime(data.processName, data.appInfo);\n        int targetSdkVersion = data.appInfo.targetSdkVersion;\n        if (targetSdkVersion < Build.VERSION_CODES.GINGERBREAD) {\n            StrictMode.ThreadPolicy newPolicy = new StrictMode.ThreadPolicy.Builder(StrictMode.getThreadPolicy()).permitNetwork().build();\n            StrictMode.setThreadPolicy(newPolicy);\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            if (mirror.android.os.StrictMode.sVmPolicyMask != null) {\n                mirror.android.os.StrictMode.sVmPolicyMask.set(0);\n            }\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {\n            mirror.android.os.Message.updateCheckRecycle.call(targetSdkVersion);\n        }\n        if (VASettings.ENABLE_IO_REDIRECT) {\n            // IO 重定向\n            startIOUniformer();\n        }\n        // hook native 函数\n        NativeEngine.hookNative();\n        Object mainThread = VirtualCore.mainThread();\n        // 准备 dex 列表\n        NativeEngine.startDexOverride();\n        // 获得子 pkg 的 Context 前提是必须在系统中安装的（疑问？）\n        Context context = createPackageContext(data.appInfo.packageName);\n        // 设置虚拟机系统环境 临时文件夹 codeCacheDir\n        System.setProperty(\"java.io.tmpdir\", context.getCacheDir().getAbsolutePath());\n        // oat 的 cache 目录\n        File codeCacheDir;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            codeCacheDir = context.getCodeCacheDir();\n        } else {\n            codeCacheDir = context.getCacheDir();\n        }\n        // 硬件加速的 cache 目录\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            if (HardwareRenderer.setupDiskCache != null) {\n                HardwareRenderer.setupDiskCache.call(codeCacheDir);\n            }\n        } else {\n            if (ThreadedRenderer.setupDiskCache != null) {\n                ThreadedRenderer.setupDiskCache.call(codeCacheDir);\n            }\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            if (RenderScriptCacheDir.setupDiskCache != null) {\n                RenderScriptCacheDir.setupDiskCache.call(codeCacheDir);\n            }\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            if (RenderScript.setupDiskCache != null) {\n                RenderScript.setupDiskCache.call(codeCacheDir);\n            }\n        }\n\n        // 修复子 App 中 ActivityThread.AppBind信息erData 的参数，因为之前用的是在 Host 程序中注册的 Stub 的信息\n        Object boundApp = fixBoundApp(mBoundApplication);\n        mBoundApplication.info = ContextImpl.mPackageInfo.get(context);\n        mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info);\n\n        // 同样修复 targetSdkVersion 原来也是可 Host 程序一样的\n        VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion);\n\n        boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName);\n        if (!conflict) {\n            InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);\n        }\n\n        // 开始构建子程序包的 Application 对象，并且替换原来通过 Host Stub 生成的 mInitialApplication\n        mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null);\n        mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication);\n        ContextFixer.fixContext(mInitialApplication);\n        if (data.providers != null) {\n            // 注册 Providers\n            installContentProviders(mInitialApplication, data.providers);\n        }\n        // 初始化锁开，异步调用的初始化函数可以返回了\n        if (lock != null) {\n            lock.open();\n            mTempLock = null;\n        }\n        try {\n            // 调用 Application.onCreate\n            mInstrumentation.callApplicationOnCreate(mInitialApplication);\n            InvocationStubManager.getInstance().checkEnv(HCallbackStub.class);\n            if (conflict) {\n                InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);\n            }\n            Application createdApp = ActivityThread.mInitialApplication.get(mainThread);\n            if (createdApp != null) {\n                mInitialApplication = createdApp;\n            }\n        } catch (Exception e) {\n            if (!mInstrumentation.onException(mInitialApplication, e)) {\n                throw new RuntimeException(\n                        \"Unable to create application \" + mInitialApplication.getClass().getName()\n                                + \": \" + e.toString(), e);\n            }\n        }\n        VActivityManager.get().appDoneExecuting();\n    }\n\n    private void setupUncaughtHandler() {\n        ThreadGroup root = Thread.currentThread().getThreadGroup();\n        while (root.getParent() != null) {\n            root = root.getParent();\n        }\n        ThreadGroup newRoot = new RootThreadGroup(root);\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            final List<ThreadGroup> groups = mirror.java.lang.ThreadGroup.groups.get(root);\n            //noinspection SynchronizationOnLocalVariableOrMethodParameter\n            synchronized (groups) {\n                List<ThreadGroup> newGroups = new ArrayList<>(groups);\n                newGroups.remove(newRoot);\n                mirror.java.lang.ThreadGroup.groups.set(newRoot, newGroups);\n                groups.clear();\n                groups.add(newRoot);\n                mirror.java.lang.ThreadGroup.groups.set(root, groups);\n                for (ThreadGroup group : newGroups) {\n                    mirror.java.lang.ThreadGroup.parent.set(group, newRoot);\n                }\n            }\n        } else {\n            final ThreadGroup[] groups = ThreadGroupN.groups.get(root);\n            //noinspection SynchronizationOnLocalVariableOrMethodParameter\n            synchronized (groups) {\n                ThreadGroup[] newGroups = groups.clone();\n                ThreadGroupN.groups.set(newRoot, newGroups);\n                ThreadGroupN.groups.set(root, new ThreadGroup[]{newRoot});\n                for (Object group : newGroups) {\n                    ThreadGroupN.parent.set(group, newRoot);\n                }\n                ThreadGroupN.ngroups.set(root, 1);\n            }\n        }\n    }\n\n    // IO 重定向\n    @SuppressLint(\"SdCardPath\")\n    private void startIOUniformer() {\n        ApplicationInfo info = mBoundApplication.appInfo;\n        int userId = VUserHandle.myUserId();\n        String wifiMacAddressFile = deviceInfo.getWifiFile(userId).getPath();\n        NativeEngine.redirectDirectory(\"/sys/class/net/wlan0/address\", wifiMacAddressFile);\n        NativeEngine.redirectDirectory(\"/sys/class/net/eth0/address\", wifiMacAddressFile);\n        NativeEngine.redirectDirectory(\"/sys/class/net/wifi/address\", wifiMacAddressFile);\n        NativeEngine.redirectDirectory(\"/data/data/\" + info.packageName, info.dataDir);\n        NativeEngine.redirectDirectory(\"/data/user/0/\" + info.packageName, info.dataDir);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            NativeEngine.redirectDirectory(\"/data/user_de/0/\" + info.packageName, info.dataDir);\n        }\n        String libPath = new File(VEnvironment.getDataAppPackageDirectory(info.packageName), \"lib\").getAbsolutePath();\n        String userLibPath = new File(VEnvironment.getUserSystemDirectory(userId), \"lib\").getAbsolutePath();\n        NativeEngine.redirectDirectory(userLibPath, libPath);\n        NativeEngine.redirectDirectory(\"/data/data/\" + info.packageName + \"/lib/\", libPath);\n        NativeEngine.redirectDirectory(\"/data/user/0/\" + info.packageName + \"/lib/\", libPath);\n\n        NativeEngine.readOnly(VEnvironment.getDataAppDirectory().getPath());\n        VirtualStorageManager vsManager = VirtualStorageManager.get();\n        String vsPath = vsManager.getVirtualStorage(info.packageName, userId);\n        boolean enable = vsManager.isVirtualStorageEnable(info.packageName, userId);\n        if (enable && vsPath != null) {\n            File vsDirectory = new File(vsPath);\n            if (vsDirectory.exists() || vsDirectory.mkdirs()) {\n                HashSet<String> mountPoints = getMountPoints();\n                for (String mountPoint : mountPoints) {\n                    NativeEngine.redirectDirectory(mountPoint, vsPath);\n                }\n            }\n        }\n        NativeEngine.hook();\n    }\n\n    @SuppressLint(\"SdCardPath\")\n    private HashSet<String> getMountPoints() {\n        HashSet<String> mountPoints = new HashSet<>(3);\n        mountPoints.add(\"/mnt/sdcard/\");\n        mountPoints.add(\"/sdcard/\");\n        String[] points = StorageManagerCompat.getAllPoints(VirtualCore.get().getContext());\n        if (points != null) {\n            Collections.addAll(mountPoints, points);\n        }\n        return mountPoints;\n\n    }\n\n    private Context createPackageContext(String packageName) {\n        try {\n            Log.e(\"gy\", \"create pkg context for pkg: \" + packageName);\n            Context hostContext = VirtualCore.get().getContext();\n            return hostContext.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);\n        } catch (PackageManager.NameNotFoundException e) {\n            VirtualRuntime.crash(new RemoteException());\n        }\n        throw new RuntimeException();\n    }\n\n    private Object fixBoundApp(AppBindData data) {\n        Object thread = VirtualCore.mainThread();\n        Object boundApp = mirror.android.app.ActivityThread.mBoundApplication.get(thread);\n        mirror.android.app.ActivityThread.AppBindData.appInfo.set(boundApp, data.appInfo);\n        mirror.android.app.ActivityThread.AppBindData.processName.set(boundApp, data.processName);\n        mirror.android.app.ActivityThread.AppBindData.instrumentationName.set(\n                boundApp,\n                new ComponentName(data.appInfo.packageName, Instrumentation.class.getName())\n        );\n        ActivityThread.AppBindData.providers.set(boundApp, data.providers);\n        return boundApp;\n    }\n\n    private void installContentProviders(Context app, List<ProviderInfo> providers) {\n        long origId = Binder.clearCallingIdentity();\n        Object mainThread = VirtualCore.mainThread();\n        try {\n            for (ProviderInfo cpi : providers) {\n                if (cpi.enabled) {\n                    ActivityThread.installProvider(mainThread, app, cpi, null);\n                }\n            }\n        } finally {\n            Binder.restoreCallingIdentity(origId);\n        }\n    }\n\n    @Override\n    public IBinder acquireProviderClient(ProviderInfo info) {\n        if (mTempLock != null) {\n            mTempLock.block();\n        }\n        // 这里检查 Application 是否启动，注意注册 Provider 的逻辑也在里面\n        if (!isBound()) {\n            VClientImpl.get().bindApplication(info.packageName, info.processName);\n        }\n        // 准备 ContentProviderClient\n        IInterface provider = null;\n        String[] authorities = info.authority.split(\";\");\n        String authority = authorities.length == 0 ? info.authority : authorities[0];\n        ContentResolver resolver = VirtualCore.get().getContext().getContentResolver();\n        ContentProviderClient client = null;\n        try {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                client = resolver.acquireUnstableContentProviderClient(authority);\n            } else {\n                client = resolver.acquireContentProviderClient(authority);\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        if (client != null) {\n            // 反射获取 provider\n            provider = mirror.android.content.ContentProviderClient.mContentProvider.get(client);\n            client.release();\n        }\n        return provider != null ? provider.asBinder() : null;\n    }\n\n    private void fixInstalledProviders() {\n        clearSettingProvider();\n        Map clientMap = ActivityThread.mProviderMap.get(VirtualCore.mainThread());\n        for (Object clientRecord : clientMap.values()) {\n            if (BuildCompat.isOreo()) {\n                IInterface provider = ActivityThread.ProviderClientRecordJB.mProvider.get(clientRecord);\n                Object holder = ActivityThread.ProviderClientRecordJB.mHolder.get(clientRecord);\n                if (holder == null) {\n                    continue;\n                }\n                ProviderInfo info = ContentProviderHolderOreo.info.get(holder);\n                if (!info.authority.startsWith(VASettings.STUB_CP_AUTHORITY)) {\n                    provider = ProviderHook.createProxy(true, info.authority, provider);\n                    ActivityThread.ProviderClientRecordJB.mProvider.set(clientRecord, provider);\n                    ContentProviderHolderOreo.provider.set(holder, provider);\n                }\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                IInterface provider = ActivityThread.ProviderClientRecordJB.mProvider.get(clientRecord);\n                Object holder = ActivityThread.ProviderClientRecordJB.mHolder.get(clientRecord);\n                if (holder == null) {\n                    continue;\n                }\n                ProviderInfo info = IActivityManager.ContentProviderHolder.info.get(holder);\n                if (!info.authority.startsWith(VASettings.STUB_CP_AUTHORITY)) {\n                    provider = ProviderHook.createProxy(true, info.authority, provider);\n                    ActivityThread.ProviderClientRecordJB.mProvider.set(clientRecord, provider);\n                    IActivityManager.ContentProviderHolder.provider.set(holder, provider);\n                }\n            } else {\n                String authority = ActivityThread.ProviderClientRecord.mName.get(clientRecord);\n                IInterface provider = ActivityThread.ProviderClientRecord.mProvider.get(clientRecord);\n                if (provider != null && !authority.startsWith(VASettings.STUB_CP_AUTHORITY)) {\n                    provider = ProviderHook.createProxy(true, authority, provider);\n                    ActivityThread.ProviderClientRecord.mProvider.set(clientRecord, provider);\n                }\n            }\n        }\n\n    }\n\n    private void clearSettingProvider() {\n        Object cache;\n        cache = Settings.System.sNameValueCache.get();\n        if (cache != null) {\n            clearContentProvider(cache);\n        }\n        cache = Settings.Secure.sNameValueCache.get();\n        if (cache != null) {\n            clearContentProvider(cache);\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && Settings.Global.TYPE != null) {\n            cache = Settings.Global.sNameValueCache.get();\n            if (cache != null) {\n                clearContentProvider(cache);\n            }\n        }\n    }\n\n    private static void clearContentProvider(Object cache) {\n        if (BuildCompat.isOreo()) {\n            Object holder = Settings.NameValueCacheOreo.mProviderHolder.get(cache);\n            if (holder != null) {\n                Settings.ContentProviderHolder.mContentProvider.set(holder, null);\n            }\n        } else {\n            Settings.NameValueCache.mContentProvider.set(cache, null);\n        }\n    }\n\n    @Override\n    public void finishActivity(IBinder token) {\n        VActivityManager.get().finishActivity(token);\n    }\n\n\n    // 将被 ActivityStack 远程调用\n    @Override\n    public void scheduleNewIntent(String creator, IBinder token, Intent intent) {\n        NewIntentData data = new NewIntentData();\n        data.creator = creator;\n        data.token = token;\n        data.intent = intent;\n        sendMessage(NEW_INTENT, data);\n    }\n\n    @Override\n    public void scheduleReceiver(String processName, ComponentName component, Intent intent, PendingResultData resultData) {\n        ReceiverData receiverData = new ReceiverData();\n        receiverData.resultData = resultData;\n        receiverData.intent = intent;\n        receiverData.component = component;\n        receiverData.processName = processName;\n        sendMessage(RECEIVER, receiverData);\n    }\n\n    private void handleReceiver(ReceiverData data) {\n        BroadcastReceiver.PendingResult result = data.resultData.build();\n        try {\n            // 依然是检测 Application 是否初始化，没有则初始化\n            if (!isBound()) {\n                bindApplication(data.component.getPackageName(), data.processName);\n            }\n            // 获取 Receiver 的 Context，这个context是一个ReceiverRestrictedContext实例，它有两个主要函数被禁掉：registerReceiver()和 bindService()。这两个函数在BroadcastReceiver.onReceive()不允许调用。每次Receiver处理一个广播，传递进来的context都是一个新的实例。\n            Context context = mInitialApplication.getBaseContext();\n            Context receiverContext = ContextImpl.getReceiverRestrictedContext.call(context);\n            String className = data.component.getClassName();\n            Log.e(\"gy\", \"handler Receiver: \" + className);\n            // 实例化目标 Receiver\n            BroadcastReceiver receiver = (BroadcastReceiver) context.getClassLoader().loadClass(className).newInstance();\n            mirror.android.content.BroadcastReceiver.setPendingResult.call(receiver, result);\n            data.intent.setExtrasClassLoader(context.getClassLoader());\n            // 手动调用 onCreate\n            receiver.onReceive(receiverContext, data.intent);\n            // 通知 Pending 结束\n            if (mirror.android.content.BroadcastReceiver.getPendingResult.call(receiver) != null) {\n                result.finish();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            throw new RuntimeException(\n                    \"Unable to start receiver \" + data.component\n                            + \": \" + e.toString(), e);\n        }\n        // 这里需要远程通知 VAService 广播已送到\n        VActivityManager.get().broadcastFinish(data.resultData);\n    }\n\n    @Override\n    public IBinder createProxyService(ComponentName component, IBinder binder) {\n        return ProxyServiceFactory.getProxyService(getCurrentApplication(), component, binder);\n    }\n\n    @Override\n    public String getDebugInfo() {\n        return \"process : \" + VirtualRuntime.getProcessName() + \"\\n\" +\n                \"initialPkg : \" + VirtualRuntime.getInitialPackageName() + \"\\n\" +\n                \"vuid : \" + vuid;\n    }\n\n    private static class RootThreadGroup extends ThreadGroup {\n\n        RootThreadGroup(ThreadGroup parent) {\n            super(parent, \"VA-Root\");\n        }\n\n        @Override\n        public void uncaughtException(Thread t, Throwable e) {\n            CrashHandler handler = VClientImpl.gClient.crashHandler;\n            if (handler != null) {\n                handler.handleUncaughtException(t, e);\n            } else {\n                VLog.e(\"uncaught\", e);\n                System.exit(0);\n            }\n        }\n    }\n\n    private final class NewIntentData {\n        String creator;\n        IBinder token;\n        Intent intent;\n    }\n\n    private final class AppBindData {\n        String processName;\n        ApplicationInfo appInfo;\n        List<ProviderInfo> providers;\n        Object info;\n    }\n\n    private final class ReceiverData {\n        PendingResultData resultData;\n        Intent intent;\n        ComponentName component;\n        String processName;\n    }\n\n    private class H extends Handler {\n\n        private H() {\n            super(Looper.getMainLooper());\n        }\n\n        @Override\n        public void handleMessage(Message msg) {\n            switch (msg.what) {\n                case NEW_INTENT: {\n                    handleNewIntent((NewIntentData) msg.obj);\n                }\n                break;\n                case RECEIVER: {\n                    handleReceiver((ReceiverData) msg.obj);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/core/CrashHandler.java",
    "content": "package com.lody.virtual.client.core;\n\n/**\n * @author Lody\n */\n\npublic interface CrashHandler {\n\n    void handleUncaughtException(Thread t, Throwable e);\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/core/InstallStrategy.java",
    "content": "package com.lody.virtual.client.core;\n\n/**\n * @author Lody\n *\n *\n */\npublic interface InstallStrategy {\n\tint TERMINATE_IF_EXIST = 0x01 << 1;\n\tint UPDATE_IF_EXIST = 0x01 << 2;\n\tint COMPARE_VERSION = 0X01 << 3;\n\tint IGNORE_NEW_VERSION = 0x01 << 4;\n\tint DEPEND_SYSTEM_IF_EXIST = 0x01 << 5;\n\tint SKIP_DEX_OPT = 0x01 << 6;\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java",
    "content": "package com.lody.virtual.client.core;\n\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.delegate.AppInstrumentation;\nimport com.lody.virtual.client.hook.proxies.account.AccountManagerStub;\nimport com.lody.virtual.client.hook.proxies.alarm.AlarmManagerStub;\nimport com.lody.virtual.client.hook.proxies.am.ActivityManagerStub;\nimport com.lody.virtual.client.hook.proxies.am.HCallbackStub;\nimport com.lody.virtual.client.hook.proxies.appops.AppOpsManagerStub;\nimport com.lody.virtual.client.hook.proxies.appwidget.AppWidgetManagerStub;\nimport com.lody.virtual.client.hook.proxies.audio.AudioManagerStub;\nimport com.lody.virtual.client.hook.proxies.backup.BackupManagerStub;\nimport com.lody.virtual.client.hook.proxies.bluetooth.BluetoothStub;\nimport com.lody.virtual.client.hook.proxies.clipboard.ClipBoardStub;\nimport com.lody.virtual.client.hook.proxies.connectivity.ConnectivityStub;\nimport com.lody.virtual.client.hook.proxies.content.ContentServiceStub;\nimport com.lody.virtual.client.hook.proxies.context_hub.ContextHubServiceStub;\nimport com.lody.virtual.client.hook.proxies.display.DisplayStub;\nimport com.lody.virtual.client.hook.proxies.dropbox.DropBoxManagerStub;\nimport com.lody.virtual.client.hook.proxies.graphics.GraphicsStatsStub;\nimport com.lody.virtual.client.hook.proxies.imms.MmsStub;\nimport com.lody.virtual.client.hook.proxies.input.InputMethodManagerStub;\nimport com.lody.virtual.client.hook.proxies.isms.ISmsStub;\nimport com.lody.virtual.client.hook.proxies.isub.ISubStub;\nimport com.lody.virtual.client.hook.proxies.job.JobServiceStub;\nimport com.lody.virtual.client.hook.proxies.libcore.LibCoreStub;\nimport com.lody.virtual.client.hook.proxies.location.LocationManagerStub;\nimport com.lody.virtual.client.hook.proxies.media.router.MediaRouterServiceStub;\nimport com.lody.virtual.client.hook.proxies.media.session.SessionManagerStub;\nimport com.lody.virtual.client.hook.proxies.mount.MountServiceStub;\nimport com.lody.virtual.client.hook.proxies.network.NetworkManagementStub;\nimport com.lody.virtual.client.hook.proxies.notification.NotificationManagerStub;\nimport com.lody.virtual.client.hook.proxies.persistent_data_block.PersistentDataBlockServiceStub;\nimport com.lody.virtual.client.hook.proxies.phonesubinfo.PhoneSubInfoStub;\nimport com.lody.virtual.client.hook.proxies.pm.PackageManagerStub;\nimport com.lody.virtual.client.hook.proxies.power.PowerManagerStub;\nimport com.lody.virtual.client.hook.proxies.restriction.RestrictionStub;\nimport com.lody.virtual.client.hook.proxies.search.SearchManagerStub;\nimport com.lody.virtual.client.hook.proxies.shortcut.ShortcutServiceStub;\nimport com.lody.virtual.client.hook.proxies.telephony.TelephonyRegistryStub;\nimport com.lody.virtual.client.hook.proxies.telephony.TelephonyStub;\nimport com.lody.virtual.client.hook.proxies.user.UserManagerStub;\nimport com.lody.virtual.client.hook.proxies.vibrator.VibratorStub;\nimport com.lody.virtual.client.hook.proxies.wifi.WifiManagerStub;\nimport com.lody.virtual.client.hook.proxies.wifi_scanner.WifiScannerStub;\nimport com.lody.virtual.client.hook.proxies.window.WindowManagerStub;\nimport com.lody.virtual.client.interfaces.IInjector;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;\nimport static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;\nimport static android.os.Build.VERSION_CODES.KITKAT;\nimport static android.os.Build.VERSION_CODES.LOLLIPOP;\nimport static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;\nimport static android.os.Build.VERSION_CODES.M;\nimport static android.os.Build.VERSION_CODES.N;\n\n/**\n * @author Lody\n *\n */\npublic final class InvocationStubManager {\n\n    private static InvocationStubManager sInstance = new InvocationStubManager();\n    private static boolean sInit;\n\n\tprivate Map<Class<?>, IInjector> mInjectors = new HashMap<>(13);\n\n\tprivate InvocationStubManager() {\n\t}\n\n\tpublic static InvocationStubManager getInstance() {\n\t\treturn sInstance;\n\t}\n\n\tvoid injectAll() throws Throwable {\n\t\tfor (IInjector injector : mInjectors.values()) {\n\t\t\tinjector.inject();\n\t\t}\n\t\t// XXX: Lazy inject the Instrumentation,\n\t\taddInjector(AppInstrumentation.getDefault());\n\t}\n\n    /**\n\t * @return if the InvocationStubManager has been initialized.\n\t */\n\tpublic boolean isInit() {\n\t\treturn sInit;\n\t}\n\n\n\tpublic void init() throws Throwable {\n\t\tif (isInit()) {\n\t\t\tthrow new IllegalStateException(\"InvocationStubManager Has been initialized.\");\n\t\t}\n\t\tinjectInternal();\n\t\tsInit = true;\n\n\t}\n\n\tprivate void injectInternal() throws Throwable {\n\t\t// VA 自身的 App 进程不需要 Hook\n\t\tif (VirtualCore.get().isMainProcess()) {\n\t\t\treturn;\n\t\t}\n\t\t// VAService 需要 Hook AMS 和 PMS\n\t\tif (VirtualCore.get().isServerProcess()) {\n\t\t\taddInjector(new ActivityManagerStub());\n\t\t\taddInjector(new PackageManagerStub());\n\t\t\treturn;\n\t\t}\n\t\t// Client APP 需要 Hook 整个 framework，来使其调用到 VA framework\n\t\tif (VirtualCore.get().isVAppProcess()) {\n\t\t\taddInjector(new LibCoreStub());\n\t\t\taddInjector(new ActivityManagerStub());\n\t\t\taddInjector(new PackageManagerStub());\n\t\t\taddInjector(HCallbackStub.getDefault());\n\t\t\taddInjector(new ISmsStub());\n\t\t\taddInjector(new ISubStub());\n\t\t\taddInjector(new DropBoxManagerStub());\n\t\t\taddInjector(new NotificationManagerStub());\n\t\t\taddInjector(new LocationManagerStub());\n\t\t\taddInjector(new WindowManagerStub());\n\t\t\taddInjector(new ClipBoardStub());\n\t\t\taddInjector(new MountServiceStub());\n\t\t\taddInjector(new BackupManagerStub());\n\t\t\taddInjector(new TelephonyStub());\n\t\t\taddInjector(new TelephonyRegistryStub());\n\t\t\taddInjector(new PhoneSubInfoStub());\n\t\t\taddInjector(new PowerManagerStub());\n\t\t\taddInjector(new AppWidgetManagerStub());\n\t\t\taddInjector(new AccountManagerStub());\n\t\t\taddInjector(new AudioManagerStub());\n\t\t\taddInjector(new SearchManagerStub());\n\t\t\taddInjector(new ContentServiceStub());\n\t\t\taddInjector(new ConnectivityStub());\n\n\t\t\tif (Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {\n\t\t\t\taddInjector(new VibratorStub());\n\t\t\t\taddInjector(new WifiManagerStub());\n\t\t\t\taddInjector(new BluetoothStub());\n\t\t\t\taddInjector(new ContextHubServiceStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {\n\t\t\t\taddInjector(new UserManagerStub());\n\t\t\t}\n\n\t\t\tif (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {\n\t\t\t\taddInjector(new DisplayStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= LOLLIPOP) {\n\t\t\t\taddInjector(new PersistentDataBlockServiceStub());\n\t\t\t\taddInjector(new InputMethodManagerStub());\n\t\t\t\taddInjector(new MmsStub());\n\t\t\t\taddInjector(new SessionManagerStub());\n\t\t\t\taddInjector(new JobServiceStub());\n\t\t\t\taddInjector(new RestrictionStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= KITKAT) {\n\t\t\t\taddInjector(new AlarmManagerStub());\n\t\t\t\taddInjector(new AppOpsManagerStub());\n\t\t\t\taddInjector(new MediaRouterServiceStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= LOLLIPOP_MR1) {\n\t\t\t\taddInjector(new GraphicsStatsStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= M) {\n\t\t\t\taddInjector(new NetworkManagementStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= N) {\n                addInjector(new WifiScannerStub());\n                addInjector(new ShortcutServiceStub());\n            }\n\t\t}\n\t}\n\n\tprivate void addInjector(IInjector IInjector) {\n\t\tmInjectors.put(IInjector.getClass(), IInjector);\n\t}\n\n\tpublic <T extends IInjector> T findInjector(Class<T> clazz) {\n\t\t// noinspection unchecked\n\t\treturn (T) mInjectors.get(clazz);\n\t}\n\n\tpublic <T extends IInjector> void checkEnv(Class<T> clazz) {\n\t\tIInjector IInjector = findInjector(clazz);\n\t\tif (IInjector != null && IInjector.isEnvBad()) {\n\t\t\ttry {\n\t\t\t\tIInjector.inject();\n\t\t\t} catch (Throwable e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic <T extends IInjector, H extends MethodInvocationStub> H getInvocationStub(Class<T> injectorClass) {\n\t\tT injector = findInjector(injectorClass);\n\t\tif (injector != null && injector instanceof MethodInvocationProxy) {\n\t\t\t// noinspection unchecked\n\t\t\treturn (H) ((MethodInvocationProxy) injector).getInvocationStub();\n\t\t}\n\t\treturn null;\n\t}\n\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java",
    "content": "package com.lody.virtual.client.core;\n\nimport android.annotation.SuppressLint;\nimport android.app.ActivityManager;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.res.AssetManager;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.os.Bundle;\nimport android.os.ConditionVariable;\nimport android.os.Looper;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.env.Constants;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.fixer.ContextFixer;\nimport com.lody.virtual.client.hook.delegate.ComponentDelegate;\nimport com.lody.virtual.client.hook.delegate.PhoneInfoDelegate;\nimport com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;\nimport com.lody.virtual.client.ipc.LocalProxyUtils;\nimport com.lody.virtual.client.ipc.ServiceManagerNative;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.client.ipc.VPackageManager;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.compat.BundleCompat;\nimport com.lody.virtual.helper.utils.BitmapUtils;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.InstallResult;\nimport com.lody.virtual.remote.InstalledAppInfo;\nimport com.lody.virtual.server.IAppManager;\nimport com.lody.virtual.server.interfaces.IAppRequestListener;\nimport com.lody.virtual.server.interfaces.IPackageObserver;\nimport com.lody.virtual.server.interfaces.IUiCallback;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport dalvik.system.DexFile;\nimport mirror.android.app.ActivityThread;\n\n/**\n * @author Lody\n * @version 3.5\n */\npublic final class VirtualCore {\n\n    public static final int GET_HIDDEN_APP = 0x00000001;\n\n    @SuppressLint(\"StaticFieldLeak\")\n    private static VirtualCore gCore = new VirtualCore();\n    private final int myUid = Process.myUid();\n    /**\n     * Client Package Manager\n     */\n    private PackageManager unHookPackageManager;\n    /**\n     * Host package name\n     */\n    private String hostPkgName;\n    /**\n     * ActivityThread instance\n     */\n    private Object mainThread;\n    private Context context;\n    /**\n     * Main ProcessName\n     */\n    private String mainProcessName;\n    /**\n     * Real Process Name\n     */\n    private String processName;\n    private ProcessType processType;\n    private IAppManager mService;\n    private boolean isStartUp;\n    private PackageInfo hostPkgInfo;\n    private int systemPid;\n    private ConditionVariable initLock = new ConditionVariable();\n    private PhoneInfoDelegate phoneInfoDelegate;\n    private ComponentDelegate componentDelegate;\n    private TaskDescriptionDelegate taskDescriptionDelegate;\n\n    private VirtualCore() {\n    }\n\n    public static VirtualCore get() {\n        return gCore;\n    }\n\n    public static PackageManager getPM() {\n        return get().getPackageManager();\n    }\n\n    public static Object mainThread() {\n        return get().mainThread;\n    }\n\n    public ConditionVariable getInitLock() {\n        return initLock;\n    }\n\n    public int myUid() {\n        return myUid;\n    }\n\n    public int myUserId() {\n        return VUserHandle.getUserId(myUid);\n    }\n\n    public ComponentDelegate getComponentDelegate() {\n        return componentDelegate == null ? ComponentDelegate.EMPTY : componentDelegate;\n    }\n\n    public void setComponentDelegate(ComponentDelegate delegate) {\n        this.componentDelegate = delegate;\n    }\n\n    public PhoneInfoDelegate getPhoneInfoDelegate() {\n        return phoneInfoDelegate;\n    }\n\n    public void setPhoneInfoDelegate(PhoneInfoDelegate phoneInfoDelegate) {\n        this.phoneInfoDelegate = phoneInfoDelegate;\n    }\n\n    public void setCrashHandler(CrashHandler handler) {\n        VClientImpl.get().setCrashHandler(handler);\n    }\n\n    public TaskDescriptionDelegate getTaskDescriptionDelegate() {\n        return taskDescriptionDelegate;\n    }\n\n    public void setTaskDescriptionDelegate(TaskDescriptionDelegate taskDescriptionDelegate) {\n        this.taskDescriptionDelegate = taskDescriptionDelegate;\n    }\n\n    public int[] getGids() {\n        return hostPkgInfo.gids;\n    }\n\n    public Context getContext() {\n        return context;\n    }\n\n    public PackageManager getPackageManager() {\n        return context.getPackageManager();\n    }\n\n    public String getHostPkg() {\n        return hostPkgName;\n    }\n\n    public PackageManager getUnHookPackageManager() {\n        return unHookPackageManager;\n    }\n\n\n    public void startup(Context context) throws Throwable {\n        if (!isStartUp) {\n            // 确保 MainThread\n            if (Looper.myLooper() != Looper.getMainLooper()) {\n                throw new IllegalStateException(\"VirtualCore.startup() must called in main thread.\");\n            }\n            VASettings.STUB_CP_AUTHORITY = context.getPackageName() + \".\" + VASettings.STUB_DEF_AUTHORITY;\n            ServiceManagerNative.SERVICE_CP_AUTH = context.getPackageName() + \".\" + ServiceManagerNative.SERVICE_DEF_AUTH;\n            this.context = context;\n            // 获取 ActivityThread 实例\n            mainThread = ActivityThread.currentActivityThread.call();\n            unHookPackageManager = context.getPackageManager();\n            hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);\n            detectProcessType();\n            // hook 系统类\n            InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();\n            invocationStubManager.init();\n            invocationStubManager.injectAll();\n            // 修复权限管理\n            ContextFixer.fixContext(context);\n            isStartUp = true;\n            if (initLock != null) {\n                initLock.open();\n                initLock = null;\n            }\n        }\n    }\n\n    public void waitForEngine() {\n        ServiceManagerNative.ensureServerStarted();\n    }\n\n    public boolean isEngineLaunched() {\n        String engineProcessName = getEngineProcessName();\n        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        for (ActivityManager.RunningAppProcessInfo info : am.getRunningAppProcesses()) {\n            if (info.processName.endsWith(engineProcessName)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public String getEngineProcessName() {\n        return context.getString(R.string.engine_process_name);\n    }\n\n    public void initialize(VirtualInitializer initializer) {\n        if (initializer == null) {\n            throw new IllegalStateException(\"Initializer = NULL\");\n        }\n        switch (processType) {\n            case Main:\n                initializer.onMainProcess();\n                break;\n            case VAppClient:\n                initializer.onVirtualProcess();\n                break;\n            case Server:\n                initializer.onServerProcess();\n                break;\n            case CHILD:\n                initializer.onChildProcess();\n                break;\n        }\n    }\n\n    private void detectProcessType() {\n        // Host package name\n        hostPkgName = context.getApplicationInfo().packageName;\n        // Main process name\n        mainProcessName = context.getApplicationInfo().processName;\n        // Current process name\n        processName = ActivityThread.getProcessName.call(mainThread);\n        if (processName.equals(mainProcessName)) {\n            processType = ProcessType.Main;\n        } else if (processName.endsWith(Constants.SERVER_PROCESS_NAME)) {\n            processType = ProcessType.Server;\n        } else if (VActivityManager.get().isAppProcess(processName)) {\n            processType = ProcessType.VAppClient;\n        } else {\n            processType = ProcessType.CHILD;\n        }\n        if (isVAppProcess()) {\n            systemPid = VActivityManager.get().getSystemPid();\n        }\n    }\n\n    private IAppManager getService() {\n        if (mService == null\n                || (!VirtualCore.get().isVAppProcess() && !mService.asBinder().isBinderAlive())) {\n            synchronized (this) {\n                Object remote = getStubInterface();\n                mService = LocalProxyUtils.genProxy(IAppManager.class, remote);\n            }\n        }\n        return mService;\n    }\n\n    private Object getStubInterface() {\n        return IAppManager.Stub\n                .asInterface(ServiceManagerNative.getService(ServiceManagerNative.APP));\n    }\n\n    /**\n     * @return If the current process is used to VA.\n     */\n    public boolean isVAppProcess() {\n        return ProcessType.VAppClient == processType;\n    }\n\n    /**\n     * @return If the current process is the main.\n     */\n    public boolean isMainProcess() {\n        return ProcessType.Main == processType;\n    }\n\n    /**\n     * @return If the current process is the child.\n     */\n    public boolean isChildProcess() {\n        return ProcessType.CHILD == processType;\n    }\n\n    /**\n     * @return If the current process is the server.\n     */\n    public boolean isServerProcess() {\n        return ProcessType.Server == processType;\n    }\n\n    /**\n     * @return the <em>actual</em> process name\n     */\n    public String getProcessName() {\n        return processName;\n    }\n\n    /**\n     * @return the <em>Main</em> process name\n     */\n    public String getMainProcessName() {\n        return mainProcessName;\n    }\n\n    /**\n     * Optimize the Dalvik-Cache for the specified package.\n     *\n     * @param pkg package name\n     * @throws IOException\n     */\n    public void preOpt(String pkg) throws IOException {\n        InstalledAppInfo info = getInstalledAppInfo(pkg, 0);\n        if (info != null && !info.dependSystem && !info.skipDexOpt) {\n            DexFile.loadDex(info.apkPath, info.getOdexFile().getPath(), 0).close();\n        }\n    }\n\n    /**\n     * Is the specified app running in foreground / background?\n     *\n     * @param packageName package name\n     * @param userId      user id\n     * @return if the specified app running in foreground / background.\n     */\n    public boolean isAppRunning(String packageName, int userId) {\n        return VActivityManager.get().isAppRunning(packageName, userId);\n    }\n\n    public InstallResult installPackage(String apkPath, int flags) {\n        try {\n            // 调用远程 VAService\n            return getService().installPackage(apkPath, flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void addVisibleOutsidePackage(String pkg) {\n        try {\n            getService().addVisibleOutsidePackage(pkg);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void removeVisibleOutsidePackage(String pkg) {\n        try {\n            getService().removeVisibleOutsidePackage(pkg);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isOutsidePackageVisible(String pkg) {\n        try {\n            return getService().isOutsidePackageVisible(pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isAppInstalled(String pkg) {\n        try {\n            return getService().isAppInstalled(pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isPackageLaunchable(String packageName) {\n        InstalledAppInfo info = getInstalledAppInfo(packageName, 0);\n        return info != null\n                && getLaunchIntent(packageName, info.getInstalledUsers()[0]) != null;\n    }\n\n    public Intent getLaunchIntent(String packageName, int userId) {\n        VPackageManager pm = VPackageManager.get();\n        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);\n        intentToResolve.addCategory(Intent.CATEGORY_INFO);\n        intentToResolve.setPackage(packageName);\n        List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, intentToResolve.resolveType(context), 0, userId);\n\n        // Otherwise, try to find a main launcher activity.\n        if (ris == null || ris.size() <= 0) {\n            // reuse the intent instance\n            intentToResolve.removeCategory(Intent.CATEGORY_INFO);\n            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);\n            intentToResolve.setPackage(packageName);\n            ris = pm.queryIntentActivities(intentToResolve, intentToResolve.resolveType(context), 0, userId);\n        }\n        if (ris == null || ris.size() <= 0) {\n            return null;\n        }\n        Intent intent = new Intent(intentToResolve);\n        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        intent.setClassName(ris.get(0).activityInfo.packageName,\n                ris.get(0).activityInfo.name);\n        return intent;\n    }\n\n    public boolean createShortcut(int userId, String packageName, OnEmitShortcutListener listener) {\n        return createShortcut(userId, packageName, null, listener);\n    }\n\n    public boolean createShortcut(int userId, String packageName, Intent splash, OnEmitShortcutListener listener) {\n        InstalledAppInfo setting = getInstalledAppInfo(packageName, 0);\n        if (setting == null) {\n            return false;\n        }\n        ApplicationInfo appInfo = setting.getApplicationInfo(userId);\n        PackageManager pm = context.getPackageManager();\n        String name;\n        Bitmap icon;\n        try {\n            CharSequence sequence = appInfo.loadLabel(pm);\n            name = sequence.toString();\n            icon = BitmapUtils.drawableToBitmap(appInfo.loadIcon(pm));\n        } catch (Throwable e) {\n            return false;\n        }\n        if (listener != null) {\n            String newName = listener.getName(name);\n            if (newName != null) {\n                name = newName;\n            }\n            Bitmap newIcon = listener.getIcon(icon);\n            if (newIcon != null) {\n                icon = newIcon;\n            }\n        }\n        Intent targetIntent = getLaunchIntent(packageName, userId);\n        if (targetIntent == null) {\n            return false;\n        }\n        Intent shortcutIntent = new Intent();\n        shortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);\n        shortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);\n        if (splash != null) {\n            shortcutIntent.putExtra(\"_VA_|_splash_\", splash.toUri(0));\n        }\n        shortcutIntent.putExtra(\"_VA_|_intent_\", targetIntent);\n        shortcutIntent.putExtra(\"_VA_|_uri_\", targetIntent.toUri(0));\n        shortcutIntent.putExtra(\"_VA_|_user_id_\", VUserHandle.myUserId());\n\n        Intent addIntent = new Intent();\n        addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);\n        addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);\n        addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);\n        addIntent.setAction(\"com.android.launcher.action.INSTALL_SHORTCUT\");\n        context.sendBroadcast(addIntent);\n        return true;\n    }\n\n    public boolean removeShortcut(int userId, String packageName, Intent splash, OnEmitShortcutListener listener) {\n        InstalledAppInfo setting = getInstalledAppInfo(packageName, 0);\n        if (setting == null) {\n            return false;\n        }\n        ApplicationInfo appInfo = setting.getApplicationInfo(userId);\n        PackageManager pm = context.getPackageManager();\n        String name;\n        try {\n            CharSequence sequence = appInfo.loadLabel(pm);\n            name = sequence.toString();\n        } catch (Throwable e) {\n            return false;\n        }\n        if (listener != null) {\n            String newName = listener.getName(name);\n            if (newName != null) {\n                name = newName;\n            }\n        }\n        Intent targetIntent = getLaunchIntent(packageName, userId);\n        if (targetIntent == null) {\n            return false;\n        }\n        Intent shortcutIntent = new Intent();\n        shortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);\n        shortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);\n        if (splash != null) {\n            shortcutIntent.putExtra(\"_VA_|_splash_\", splash.toUri(0));\n        }\n        shortcutIntent.putExtra(\"_VA_|_intent_\", targetIntent);\n        shortcutIntent.putExtra(\"_VA_|_uri_\", targetIntent.toUri(0));\n        shortcutIntent.putExtra(\"_VA_|_user_id_\", VUserHandle.myUserId());\n\n        Intent addIntent = new Intent();\n        addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);\n        addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);\n        addIntent.setAction(\"com.android.launcher.action.UNINSTALL_SHORTCUT\");\n        context.sendBroadcast(addIntent);\n        return true;\n    }\n\n    public abstract static class UiCallback extends IUiCallback.Stub {\n    }\n\n    public void setUiCallback(Intent intent, IUiCallback callback) {\n        if (callback != null) {\n            Bundle bundle = new Bundle();\n            BundleCompat.putBinder(bundle, \"_VA_|_ui_callback_\", callback.asBinder());\n            intent.putExtra(\"_VA_|_sender_\", bundle);\n        }\n    }\n\n    public InstalledAppInfo getInstalledAppInfo(String pkg, int flags) {\n        try {\n            return getService().getInstalledAppInfo(pkg, flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int getInstalledAppCount() {\n        try {\n            return getService().getInstalledAppCount();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isStartup() {\n        return isStartUp;\n    }\n\n    public boolean uninstallPackageAsUser(String pkgName, int userId) {\n        try {\n            return getService().uninstallPackageAsUser(pkgName, userId);\n        } catch (RemoteException e) {\n            // Ignore\n        }\n        return false;\n    }\n\n    public boolean uninstallPackage(String pkgName) {\n        try {\n            return getService().uninstallPackage(pkgName);\n        } catch (RemoteException e) {\n            // Ignore\n        }\n        return false;\n    }\n\n    public Resources getResources(String pkg) throws Resources.NotFoundException {\n        InstalledAppInfo installedAppInfo = getInstalledAppInfo(pkg, 0);\n        if (installedAppInfo != null) {\n            AssetManager assets = mirror.android.content.res.AssetManager.ctor.newInstance();\n            mirror.android.content.res.AssetManager.addAssetPath.call(assets, installedAppInfo.apkPath);\n            Resources hostRes = context.getResources();\n            return new Resources(assets, hostRes.getDisplayMetrics(), hostRes.getConfiguration());\n        }\n        throw new Resources.NotFoundException(pkg);\n    }\n\n    public synchronized ActivityInfo resolveActivityInfo(Intent intent, int userId) {\n        ActivityInfo activityInfo = null;\n        if (intent.getComponent() == null) {\n            ResolveInfo resolveInfo = VPackageManager.get().resolveIntent(intent, intent.getType(), 0, userId);\n            if (resolveInfo != null && resolveInfo.activityInfo != null) {\n                activityInfo = resolveInfo.activityInfo;\n                intent.setClassName(activityInfo.packageName, activityInfo.name);\n            }\n        } else {\n            activityInfo = resolveActivityInfo(intent.getComponent(), userId);\n        }\n        if (activityInfo != null) {\n            if (activityInfo.targetActivity != null) {\n                ComponentName componentName = new ComponentName(activityInfo.packageName, activityInfo.targetActivity);\n                activityInfo = VPackageManager.get().getActivityInfo(componentName, 0, userId);\n                intent.setComponent(componentName);\n            }\n        }\n        return activityInfo;\n    }\n\n    public ActivityInfo resolveActivityInfo(ComponentName componentName, int userId) {\n        return VPackageManager.get().getActivityInfo(componentName, 0, userId);\n    }\n\n    public ServiceInfo resolveServiceInfo(Intent intent, int userId) {\n        ServiceInfo serviceInfo = null;\n        ResolveInfo resolveInfo = VPackageManager.get().resolveService(intent, intent.getType(), 0, userId);\n        if (resolveInfo != null) {\n            serviceInfo = resolveInfo.serviceInfo;\n        }\n        return serviceInfo;\n    }\n\n    public void killApp(String pkg, int userId) {\n        VActivityManager.get().killAppByPkg(pkg, userId);\n    }\n\n    public void killAllApps() {\n        VActivityManager.get().killAllApps();\n    }\n\n    public List<InstalledAppInfo> getInstalledApps(int flags) {\n        try {\n            return getService().getInstalledApps(flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<InstalledAppInfo> getInstalledAppsAsUser(int userId, int flags) {\n        try {\n            return getService().getInstalledAppsAsUser(userId, flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void clearAppRequestListener() {\n        try {\n            getService().clearAppRequestListener();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void scanApps() {\n        try {\n            getService().scanApps();\n        } catch (RemoteException e) {\n            // Ignore\n        }\n    }\n\n    public IAppRequestListener getAppRequestListener() {\n        try {\n            return getService().getAppRequestListener();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setAppRequestListener(final AppRequestListener listener) {\n        IAppRequestListener inner = new IAppRequestListener.Stub() {\n            @Override\n            public void onRequestInstall(final String path) {\n                VirtualRuntime.getUIHandler().post(new Runnable() {\n                    @Override\n                    public void run() {\n                        listener.onRequestInstall(path);\n                    }\n                });\n            }\n\n            @Override\n            public void onRequestUninstall(final String pkg) {\n                VirtualRuntime.getUIHandler().post(new Runnable() {\n                    @Override\n                    public void run() {\n                        listener.onRequestUninstall(pkg);\n                    }\n                });\n            }\n        };\n        try {\n            getService().setAppRequestListener(inner);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public boolean isPackageLaunched(int userId, String packageName) {\n        try {\n            return getService().isPackageLaunched(userId, packageName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setPackageHidden(int userId, String packageName, boolean hidden) {\n        try {\n            getService().setPackageHidden(userId, packageName, hidden);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public boolean installPackageAsUser(int userId, String packageName) {\n        try {\n            return getService().installPackageAsUser(userId, packageName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isAppInstalledAsUser(int userId, String packageName) {\n        try {\n            return getService().isAppInstalledAsUser(userId, packageName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int[] getPackageInstalledUsers(String packageName) {\n        try {\n            return getService().getPackageInstalledUsers(packageName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public abstract static class PackageObserver extends IPackageObserver.Stub {\n    }\n\n    public void registerObserver(IPackageObserver observer) {\n        try {\n            getService().registerObserver(observer);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void unregisterObserver(IPackageObserver observer) {\n        try {\n            getService().unregisterObserver(observer);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    // 是否在系统中安装\n    public boolean isOutsideInstalled(String packageName) {\n        try {\n            return unHookPackageManager.getApplicationInfo(packageName, 0) != null;\n        } catch (PackageManager.NameNotFoundException e) {\n            // Ignore\n        }\n        return false;\n    }\n\n\n    public int getSystemPid() {\n        return systemPid;\n    }\n\n    /**\n     * Process type\n     */\n    private enum ProcessType {\n        /**\n         * Server process\n         */\n        Server,\n        /**\n         * Virtual app process\n         */\n        VAppClient,\n        /**\n         * Main process\n         */\n        Main,\n        /**\n         * Child process\n         */\n        CHILD\n    }\n\n    public interface AppRequestListener {\n        void onRequestInstall(String path);\n\n        void onRequestUninstall(String pkg);\n    }\n\n    public interface OnEmitShortcutListener {\n        Bitmap getIcon(Bitmap originIcon);\n\n        String getName(String originName);\n    }\n\n    public static abstract class VirtualInitializer {\n        public void onMainProcess() {\n        }\n\n        public void onVirtualProcess() {\n        }\n\n        public void onServerProcess() {\n        }\n\n        public void onChildProcess() {\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/env/Constants.java",
    "content": "package com.lody.virtual.client.env;\n\nimport android.app.PendingIntent;\nimport android.content.Intent;\n\nimport com.lody.virtual.client.stub.ShortcutHandleActivity;\n\n/**\n * @author Lody\n *\n */\npublic class Constants {\n\n\tpublic static final String EXTRA_USER_HANDLE = \"android.intent.extra.user_handle\";\n\t/**\n\t * If an apk declared the \"fake-signature\" attribute on its Application TAG,\n\t * we will use its signature instead of the real signature.\n\t *\n\t * For more detail, please see :\n\t * https://github.com/microg/android_packages_apps_GmsCore/blob/master/\n\t * patches/android_frameworks_base-M.patch.\n\t */\n\tpublic static final String FEATURE_FAKE_SIGNATURE = \"fake-signature\";\n\tpublic static final String ACTION_PACKAGE_ADDED = \"virtual.\" + Intent.ACTION_PACKAGE_ADDED;\n\tpublic static final String ACTION_PACKAGE_REMOVED = \"virtual.\" + Intent.ACTION_PACKAGE_REMOVED;\n\tpublic static final String ACTION_PACKAGE_CHANGED = \"virtual.\" + Intent.ACTION_PACKAGE_CHANGED;\n\tpublic static final String ACTION_USER_ADDED = \"virtual.\" + \"android.intent.action.USER_ADDED\";\n\tpublic static final String ACTION_USER_REMOVED = \"virtual.\" + \"android.intent.action.USER_REMOVED\";\n\tpublic static final String ACTION_USER_INFO_CHANGED = \"virtual.\" + \"android.intent.action.USER_CHANGED\";\n\tpublic static final String ACTION_USER_STARTED = \"Virtual.\" + \"android.intent.action.USER_STARTED\";\n\tpublic static String META_KEY_IDENTITY = \"X-Identity\";\n\tpublic static String META_VALUE_STUB = \"Stub-User\";\n\t/**\n\t * Server process name of VA\n\t */\n\tpublic static String SERVER_PROCESS_NAME = \":x\";\n\t/**\n\t * The activity who handle the shortcut.\n\t */\n\tpublic static String SHORTCUT_PROXY_ACTIVITY_NAME = ShortcutHandleActivity.class.getName();\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/env/DeadServerException.java",
    "content": "package com.lody.virtual.client.env;\n\n/**\n * @author Lody\n */\n\npublic class DeadServerException extends RuntimeException {\n\n    public DeadServerException() {\n    }\n\n    public DeadServerException(String message) {\n        super(message);\n    }\n\n    public DeadServerException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public DeadServerException(Throwable cause) {\n        super(cause);\n    }\n\n    public DeadServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n        super(message, cause, enableSuppression, writableStackTrace);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java",
    "content": "package com.lody.virtual.client.env;\n\nimport android.Manifest;\nimport android.app.DownloadManager;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.Build;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Map;\nimport java.util.Set;\n\nimport mirror.android.webkit.IWebViewUpdateService;\nimport mirror.android.webkit.WebViewFactory;\n\n/**\n * @author Lody\n */\npublic final class SpecialComponentList {\n\n    private static final List<String> ACTION_BLACK_LIST = new ArrayList<String>(1);\n    private static final Map<String, String> PROTECTED_ACTION_MAP = new HashMap<>(5);\n    private static final HashSet<String> WHITE_PERMISSION = new HashSet<>(3);\n    // 加密\n    private static final HashSet<String> INSTRUMENTATION_CONFLICTING = new HashSet<>(2);\n    private static final HashSet<String> SPEC_SYSTEM_APP_LIST = new HashSet<>(3);\n    private static final Set<String> SYSTEM_BROADCAST_ACTION = new HashSet<>(7);\n    private static String PROTECT_ACTION_PREFIX = \"_VA_protected_\";\n\n    static {\n        SYSTEM_BROADCAST_ACTION.add(DownloadManager.ACTION_DOWNLOAD_COMPLETE);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_SCREEN_ON);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_SCREEN_OFF);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_NEW_OUTGOING_CALL);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_TIME_TICK);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_TIME_CHANGED);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_TIMEZONE_CHANGED);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_CHANGED);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_LOW);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_OKAY);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_POWER_CONNECTED);\n        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_POWER_DISCONNECTED);\n        SYSTEM_BROADCAST_ACTION.add(\"android.provider.Telephony.SMS_RECEIVED\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.provider.Telephony.SMS_DELIVER\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.net.wifi.STATE_CHANGE\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.net.wifi.SCAN_RESULTS\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.net.wifi.WIFI_STATE_CHANGED\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.net.conn.CONNECTIVITY_CHANGE\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.intent.action.ANY_DATA_STATE\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.intent.action.SIM_STATE_CHANGED\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.location.PROVIDERS_CHANGED\");\n        SYSTEM_BROADCAST_ACTION.add(\"android.location.MODE_CHANGED\");\n\n        ACTION_BLACK_LIST.add(\"android.appwidget.action.APPWIDGET_UPDATE\");\n\n        WHITE_PERMISSION.add(\"com.google.android.gms.settings.SECURITY_SETTINGS\");\n        WHITE_PERMISSION.add(\"com.google.android.apps.plus.PRIVACY_SETTINGS\");\n        WHITE_PERMISSION.add(Manifest.permission.ACCOUNT_MANAGER);\n\n        PROTECTED_ACTION_MAP.put(Intent.ACTION_PACKAGE_ADDED, Constants.ACTION_PACKAGE_ADDED);\n        PROTECTED_ACTION_MAP.put(Intent.ACTION_PACKAGE_REMOVED, Constants.ACTION_PACKAGE_REMOVED);\n        PROTECTED_ACTION_MAP.put(Intent.ACTION_PACKAGE_CHANGED, Constants.ACTION_PACKAGE_CHANGED);\n        PROTECTED_ACTION_MAP.put(\"android.intent.action.USER_ADDED\", Constants.ACTION_USER_ADDED);\n        PROTECTED_ACTION_MAP.put(\"android.intent.action.USER_REMOVED\", Constants.ACTION_USER_REMOVED);\n        PROTECTED_ACTION_MAP.put(\"com.gy.test\",\"com.gy.test\");\n\n        INSTRUMENTATION_CONFLICTING.add(\"com.qihoo.magic\");\n        INSTRUMENTATION_CONFLICTING.add(\"com.qihoo.magic_mutiple\");\n        INSTRUMENTATION_CONFLICTING.add(\"com.facebook.katana\");\n\n        SPEC_SYSTEM_APP_LIST.add(\"android\");\n        SPEC_SYSTEM_APP_LIST.add(\"com.google.android.webview\");\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            try {\n                String webViewPkgN = IWebViewUpdateService.getCurrentWebViewPackageName.call(WebViewFactory.getUpdateService.call());\n                if (webViewPkgN != null) {\n                    SPEC_SYSTEM_APP_LIST.add(webViewPkgN);\n                }\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public static boolean isSpecSystemPackage(String pkg) {\n        return SPEC_SYSTEM_APP_LIST.contains(pkg);\n    }\n\n    public static boolean isConflictingInstrumentation(String packageName) {\n        return INSTRUMENTATION_CONFLICTING.contains(packageName);\n    }\n\n    /**\n     * Check if the action in the BlackList.\n     *\n     * @param action Action\n     */\n    public static boolean isActionInBlackList(String action) {\n        return ACTION_BLACK_LIST.contains(action);\n    }\n\n    /**\n     * Add an action to the BlackList.\n     *\n     * @param action action\n     */\n    public static void addBlackAction(String action) {\n        ACTION_BLACK_LIST.add(action);\n    }\n\n    public static void protectIntentFilter(IntentFilter filter) {\n        if (filter != null) {\n            List<String> actions = mirror.android.content.IntentFilter.mActions.get(filter);\n            ListIterator<String> iterator = actions.listIterator();\n            while (iterator.hasNext()) {\n                String action = iterator.next();\n                if (SpecialComponentList.isActionInBlackList(action)) {\n                    iterator.remove();\n                    continue;\n                }\n                if (SYSTEM_BROADCAST_ACTION.contains(action)) {\n                    continue;\n                }\n                String newAction = SpecialComponentList.protectAction(action);\n                if (newAction != null) {\n                    iterator.set(newAction);\n                }\n            }\n        }\n    }\n\n    public static void protectIntent(Intent intent) {\n        String protectAction = protectAction(intent.getAction());\n        if (protectAction != null) {\n            intent.setAction(protectAction);\n        }\n    }\n\n    public static void unprotectIntent(Intent intent) {\n        String unprotectAction = unprotectAction(intent.getAction());\n        if (unprotectAction != null) {\n            intent.setAction(unprotectAction);\n        }\n    }\n\n    public static String protectAction(String originAction) {\n        if (originAction == null) {\n            return null;\n        }\n        if (originAction.startsWith(\"_VA_\")) {\n            return originAction;\n        }\n        String newAction = PROTECTED_ACTION_MAP.get(originAction);\n        if (newAction == null) {\n            newAction = PROTECT_ACTION_PREFIX + originAction;\n        }\n        return newAction;\n    }\n\n    public static String unprotectAction(String action) {\n        if (action == null) {\n            return null;\n        }\n        if (action.startsWith(PROTECT_ACTION_PREFIX)) {\n            return action.substring(PROTECT_ACTION_PREFIX.length());\n        }\n        for (Map.Entry<String, String> next : PROTECTED_ACTION_MAP.entrySet()) {\n            String modifiedAction = next.getValue();\n            if (modifiedAction.equals(action)) {\n                return next.getKey();\n            }\n        }\n        return null;\n    }\n\n    public static boolean isWhitePermission(String permission) {\n        return WHITE_PERMISSION.contains(permission);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/env/VirtualRuntime.java",
    "content": "package com.lody.virtual.client.env;\n\nimport android.content.pm.ApplicationInfo;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Process;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.compat.ApplicationThreadCompat;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport mirror.android.ddm.DdmHandleAppName;\nimport mirror.android.ddm.DdmHandleAppNameJBMR1;\n\n/**\n * @author Lody\n *         <p>\n *         <p/>\n *         Runtime Environment for App.\n */\npublic class VirtualRuntime {\n\n    private static final Handler sUIHandler = new Handler(Looper.getMainLooper());\n\n    private static String sInitialPackageName;\n    private static String sProcessName;\n\n    public static Handler getUIHandler() {\n        return sUIHandler;\n    }\n\n    public static String getProcessName() {\n        return sProcessName;\n    }\n\n    public static String getInitialPackageName() {\n        return sInitialPackageName;\n    }\n\n    public static void setupRuntime(String processName, ApplicationInfo appInfo) {\n        if (sProcessName != null) {\n            return;\n        }\n        sInitialPackageName = appInfo.packageName;\n        sProcessName = processName;\n        // 设置 process 的名字\n        mirror.android.os.Process.setArgV0.call(processName);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            DdmHandleAppNameJBMR1.setAppName.call(processName, 0);\n        } else {\n            DdmHandleAppName.setAppName.call(processName);\n        }\n    }\n\n    public static <T> T crash(RemoteException e) throws RuntimeException {\n        e.printStackTrace();\n        if (VirtualCore.get().isVAppProcess()) {\n            Process.killProcess(Process.myPid());\n            System.exit(0);\n        }\n        throw new DeadServerException(e);\n    }\n\n\n    public static boolean isArt() {\n        return System.getProperty(\"java.vm.version\").startsWith(\"2\");\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/fixer/ActivityFixer.java",
    "content": "package com.lody.virtual.client.fixer;\n\nimport android.app.Activity;\nimport android.app.ActivityManager;\nimport android.app.WallpaperManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.TypedArray;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\n\nimport mirror.com.android.internal.R_Hide;\n\n/**\n * @author Lody\n *\n */\npublic final class ActivityFixer {\n\n\tprivate ActivityFixer() {\n\t}\n\n\tpublic static void fixActivity(Activity activity) {\n\t\tContext baseContext = activity.getBaseContext();\n\t\ttry {\n\t\t\tTypedArray typedArray = activity.obtainStyledAttributes((R_Hide.styleable.Window.get()));\n\t\t\tif (typedArray != null) {\n\t\t\t\tboolean showWallpaper = typedArray.getBoolean(R_Hide.styleable.Window_windowShowWallpaper.get(),\n\t\t\t\t\t\tfalse);\n\t\t\t\tif (showWallpaper) {\n\t\t\t\t\tactivity.getWindow().setBackgroundDrawable(WallpaperManager.getInstance(activity).getDrawable());\n\t\t\t\t}\n\t\t\t\ttypedArray.recycle();\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\tIntent intent = activity.getIntent();\n\t\t\tApplicationInfo applicationInfo = baseContext.getApplicationInfo();\n\t\t\tPackageManager pm = activity.getPackageManager();\n\t\t\tif (intent != null && activity.isTaskRoot()) {\n\t\t\t\ttry {\n\t\t\t\t\tString label = applicationInfo.loadLabel(pm) + \"\";\n\t\t\t\t\tBitmap icon = null;\n\t\t\t\t\tDrawable drawable = applicationInfo.loadIcon(pm);\n\t\t\t\t\tif (drawable instanceof BitmapDrawable) {\n\t\t\t\t\t\ticon = ((BitmapDrawable) drawable).getBitmap();\n\t\t\t\t\t}\n\t\t\t\t\tactivity.setTaskDescription(new ActivityManager.TaskDescription(label, icon));\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/fixer/ComponentFixer.java",
    "content": "package com.lody.virtual.client.fixer;\n\nimport android.content.pm.ComponentInfo;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.server.pm.PackageSetting;\n\n/**\n * @author Lody\n */\n\npublic class ComponentFixer {\n\n    public static String fixComponentClassName(String pkgName, String className) {\n        if (className != null) {\n            if (className.charAt(0) == '.') {\n                return pkgName + className;\n            }\n            return className;\n        }\n        return null;\n    }\n\n    public static void fixComponentInfo(PackageSetting setting, ComponentInfo info, int userId) {\n        if (info != null) {\n            if (TextUtils.isEmpty(info.processName)) {\n                info.processName = info.packageName;\n            }\n            info.name = fixComponentClassName(info.packageName, info.name);\n            if (info.processName == null) {\n                info.processName = info.applicationInfo.processName;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/fixer/ContextFixer.java",
    "content": "package com.lody.virtual.client.fixer;\n\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.os.Build;\nimport android.os.DropBoxManager;\n\nimport com.lody.virtual.client.core.InvocationStubManager;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationStub;\nimport com.lody.virtual.client.hook.proxies.dropbox.DropBoxManagerStub;\nimport com.lody.virtual.client.hook.proxies.graphics.GraphicsStatsStub;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.ReflectException;\n\nimport mirror.android.app.ContextImpl;\nimport mirror.android.app.ContextImplKitkat;\nimport mirror.android.content.ContentResolverJBMR2;\n\n/**\n * @author Lody\n */\npublic class ContextFixer {\n\n    private static final String TAG = ContextFixer.class.getSimpleName();\n\n    /**\n     * Fuck AppOps\n     *\n     * @param context Context\n     */\n    public static void fixContext(Context context) {\n        try {\n            context.getPackageName();\n        } catch (Throwable e) {\n            return;\n        }\n        InvocationStubManager.getInstance().checkEnv(GraphicsStatsStub.class);\n        int deep = 0;\n        while (context instanceof ContextWrapper) {\n            context = ((ContextWrapper) context).getBaseContext();\n            deep++;\n            if (deep >= 10) {\n                return;\n            }\n        }\n        ContextImpl.mPackageManager.set(context, null);\n        try {\n            context.getPackageManager();\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        if (!VirtualCore.get().isVAppProcess()) {\n            return;\n        }\n        DropBoxManager dm = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);\n        BinderInvocationStub boxBinder = InvocationStubManager.getInstance().getInvocationStub(DropBoxManagerStub.class);\n        if (boxBinder != null) {\n            try {\n                Reflect.on(dm).set(\"mService\", boxBinder.getProxyInterface());\n            } catch (ReflectException e) {\n                e.printStackTrace();\n            }\n        }\n        String hostPkg = VirtualCore.get().getHostPkg();\n        ContextImpl.mBasePackageName.set(context, hostPkg);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            ContextImplKitkat.mOpPackageName.set(context, hostPkg);\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            ContentResolverJBMR2.mPackageName.set(context.getContentResolver(), hostPkg);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/BinderInvocationProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefStaticMethod;\nimport mirror.android.os.ServiceManager;\n\n/**\n * @author Paulo Costa\n *\n * @see MethodInvocationProxy\n */\npublic abstract class BinderInvocationProxy extends MethodInvocationProxy<BinderInvocationStub> {\n\n\tprotected String mServiceName;\n\n\tpublic BinderInvocationProxy(IInterface stub, String serviceName) {\n\t\tthis(new BinderInvocationStub(stub), serviceName);\n\t}\n\n\tpublic BinderInvocationProxy(RefStaticMethod<IInterface> asInterfaceMethod, String serviceName) {\n\t\tthis(new BinderInvocationStub(asInterfaceMethod, ServiceManager.getService.call(serviceName)), serviceName);\n\t}\n\n\tpublic BinderInvocationProxy(Class<?> stubClass, String serviceName) {\n\t\tthis(new BinderInvocationStub(stubClass, ServiceManager.getService.call(serviceName)), serviceName);\n\t}\n\n\tpublic BinderInvocationProxy(BinderInvocationStub hookDelegate, String serviceName) {\n\t\tsuper(hookDelegate);\n\t\tthis.mServiceName = serviceName;\n\t}\n\n\t@Override\n\tpublic void inject() throws Throwable {\n\t\tgetInvocationStub().replaceService(mServiceName);\n\t}\n\n\t@Override\n\tpublic boolean isEnvBad() {\n\t\tIBinder binder = ServiceManager.getService.call(mServiceName);\n\t\treturn binder != null && getInvocationStub() != binder;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/BinderInvocationStub.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.Parcel;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport com.lody.virtual.client.core.VirtualCore;\n\nimport java.io.FileDescriptor;\nimport java.lang.reflect.Method;\n\nimport mirror.RefStaticMethod;\nimport mirror.android.os.ServiceManager;\n\n/**\n * @author Lody\n */\n// IBinder hook asInterface 接口\n@SuppressWarnings(\"unchecked\")\npublic class BinderInvocationStub extends MethodInvocationStub<IInterface> implements IBinder {\n\n    private static final String TAG = BinderInvocationStub.class.getSimpleName();\n    private IBinder mBaseBinder;\n\n    public BinderInvocationStub(RefStaticMethod<IInterface> asInterfaceMethod, IBinder binder) {\n        this(asInterface(asInterfaceMethod, binder));\n    }\n\n    public BinderInvocationStub(Class<?> stubClass, IBinder binder) {\n        this(asInterface(stubClass, binder));\n    }\n\n\n    public BinderInvocationStub(IInterface mBaseInterface) {\n        super(mBaseInterface);\n        mBaseBinder = getBaseInterface() != null ? getBaseInterface().asBinder() : null;\n        addMethodProxy(new AsBinder());\n    }\n\n    private static IInterface asInterface(RefStaticMethod<IInterface> asInterfaceMethod, IBinder binder) {\n        if (asInterfaceMethod == null || binder == null) {\n            return null;\n        }\n        return asInterfaceMethod.call(binder);\n    }\n\n    private static IInterface asInterface(Class<?> stubClass, IBinder binder) {\n        try {\n            if (stubClass == null || binder == null) {\n                return null;\n            }\n            Method asInterface = stubClass.getMethod(\"asInterface\", IBinder.class);\n            return (IInterface) asInterface.invoke(null, binder);\n        } catch (Exception e) {\n            Log.d(TAG, \"Could not create stub \" + stubClass.getName() + \". Cause: \" + e);\n            return null;\n        }\n    }\n\n    public void replaceService(String name) {\n        if (mBaseBinder != null) {\n            ServiceManager.sCache.get().put(name, this);\n        }\n    }\n\n    private final class AsBinder extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"asBinder\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return BinderInvocationStub.this;\n        }\n    }\n\n\n    @Override\n    public String getInterfaceDescriptor() throws RemoteException {\n        return mBaseBinder.getInterfaceDescriptor();\n    }\n\n    public Context getContext() {\n        return VirtualCore.get().getContext();\n    }\n\n    @Override\n    public boolean pingBinder() {\n        return mBaseBinder.pingBinder();\n    }\n\n    @Override\n    public boolean isBinderAlive() {\n        return mBaseBinder.isBinderAlive();\n    }\n\n    @Override\n    public IInterface queryLocalInterface(String descriptor) {\n        return getProxyInterface();\n    }\n\n    @Override\n    public void dump(FileDescriptor fd, String[] args) throws RemoteException {\n        mBaseBinder.dump(fd, args);\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)\n    @Override\n    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {\n        mBaseBinder.dumpAsync(fd, args);\n    }\n\n    @Override\n    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        return mBaseBinder.transact(code, data, reply, flags);\n    }\n\n    @Override\n    public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {\n        mBaseBinder.linkToDeath(recipient, flags);\n    }\n\n    @Override\n    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {\n        return mBaseBinder.unlinkToDeath(recipient, flags);\n    }\n\n    public IBinder getBaseBinder() {\n        return mBaseBinder;\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/Inject.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Lody\n *\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Inject {\n\tClass<?> value();\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/LogInvocation.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.util.Log;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Add this annotation to a {@link MethodProxy} or a {@link MethodInvocationStub} to\n * log all the calls and their arguments.\n *\n * Obviously, this is only useful for debugging.\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface LogInvocation {\n    public Condition value() default Condition.ALWAYS;\n\n    static enum Condition {\n        /** Never logs anything */\n        NEVER {\n            public int getLogLevel(boolean isHooked, boolean isError) {\n                return -1;\n            }\n        },\n        /**\n         * Logs every call.\n         * Mostly useful for debugging.\n         */\n        ALWAYS {\n            public int getLogLevel(boolean isHooked, boolean isError) {\n                return isError ? Log.WARN : Log.INFO;\n            }\n        },\n        /**\n         * Logs only calls that exited with error\n         * A reasonable tradeoff between noise and getting relevant information\n         */\n        ON_ERROR {\n            public int getLogLevel(boolean isHooked, boolean isError) {\n                return isError ? Log.WARN : -1;\n            }\n        },\n\n        /**\n         *  Log only calls that haven't been hooked\n         *  It only makes sense on a MethodInvocationProxy, and is useful to pinpoint missing methods\n         */\n        NOT_HOOKED {\n            public int getLogLevel(boolean isHooked, boolean isError) {\n                return isHooked ? -1 : isError ? Log.WARN : Log.INFO;\n            }\n        };\n\n        public abstract int getLogLevel(boolean isHooked, boolean isError);\n    };\n};\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/MethodBox.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\n@SuppressWarnings(\"unchecked\")\npublic class MethodBox {\n    public final Method method;\n    public final Object who;\n    public final Object[] args;\n\n    public MethodBox(Method method, Object who, Object[] args) {\n        this.method = method;\n        this.who = who;\n        this.args = args;\n    }\n\n    public <T> T call() throws InvocationTargetException {\n        try {\n            return (T) method.invoke(who, args);\n        } catch (IllegalAccessException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public <T> T callSafe() {\n        try {\n            return (T) method.invoke(who, args);\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/MethodInvocationProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.core.InvocationStubManager;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.interfaces.IInjector;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Modifier;\n\n/**\n * @author Lody\n *         <p>\n *         This class is responsible with:\n *         - Instantiating a {@link MethodInvocationStub.HookInvocationHandler} on {@link #getInvocationStub()} ()}\n *         - Install a bunch of {@link MethodProxy}s, either with a @{@link Inject} annotation or manually\n *         calling {@link #addMethodProxy(MethodProxy)} from {@link #onBindMethods()}\n *         - Install the hooked object on the Runtime via {@link #inject()}\n *         <p>\n *         All {@link MethodInvocationProxy}s (plus a couple of other @{@link IInjector}s are installed by\n *         {@link InvocationStubManager}\n * @see Inject\n */\n//Method Hook 通过动态代理\npublic abstract class MethodInvocationProxy<T extends MethodInvocationStub> implements IInjector {\n\n    protected T mInvocationStub;\n\n    public MethodInvocationProxy(T invocationStub) {\n        this.mInvocationStub = invocationStub;\n        onBindMethods();\n        afterHookApply(invocationStub);\n\n        LogInvocation loggingAnnotation = getClass().getAnnotation(LogInvocation.class);\n        if (loggingAnnotation != null) {\n            invocationStub.setInvocationLoggingCondition(loggingAnnotation.value());\n        }\n    }\n\n    protected void onBindMethods() {\n\n        if (mInvocationStub == null) {\n            return;\n        }\n        Class<? extends MethodInvocationProxy> clazz = getClass();\n        Inject inject = clazz.getAnnotation(Inject.class);\n        if (inject != null) {\n            Class<?> proxiesClass = inject.value();\n            Class<?>[] innerClasses = proxiesClass.getDeclaredClasses();\n            // 遍历内部类\n            for (Class<?> innerClass : innerClasses) {\n                if (!Modifier.isAbstract(innerClass.getModifiers())\n                        && MethodProxy.class.isAssignableFrom(innerClass)\n                        && innerClass.getAnnotation(SkipInject.class) == null) {\n                    addMethodProxy(innerClass);\n                }\n            }\n\n        }\n    }\n\n    private void addMethodProxy(Class<?> hookType) {\n        try {\n            Constructor<?> constructor = hookType.getDeclaredConstructors()[0];\n            if (!constructor.isAccessible()) {\n                constructor.setAccessible(true);\n            }\n            MethodProxy methodProxy;\n            if (constructor.getParameterTypes().length == 0) {\n                methodProxy = (MethodProxy) constructor.newInstance();\n            } else {\n                methodProxy = (MethodProxy) constructor.newInstance(this);\n            }\n            mInvocationStub.addMethodProxy(methodProxy);\n        } catch (Throwable e) {\n            throw new RuntimeException(\"Unable to instance Hook : \" + hookType + \" : \" + e.getMessage());\n        }\n    }\n\n    public MethodProxy addMethodProxy(MethodProxy methodProxy) {\n        return mInvocationStub.addMethodProxy(methodProxy);\n    }\n\n    protected void afterHookApply(T delegate) {\n    }\n\n    @Override\n    public abstract void inject() throws Throwable;\n\n    public Context getContext() {\n        return VirtualCore.get().getContext();\n    }\n\n    public T getInvocationStub() {\n        return mInvocationStub;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/MethodInvocationStub.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n *         <p>\n *         HookHandler uses Java's {@link Proxy} to create a wrapper for existing services.\n *         <p>\n *         When any method is called on the wrapper, it checks if there is any {@link MethodProxy} registered\n *         and enabled for that method. If so, it calls the startUniformer instead of the wrapped implementation.\n *         <p>\n *         The whole thing is managed by a {@link MethodInvocationProxy} subclass\n */\n@SuppressWarnings(\"unchecked\")\npublic class MethodInvocationStub<T> {\n\n    private static final String TAG = MethodInvocationStub.class.getSimpleName();\n\n    private Map<String, MethodProxy> mInternalMethodProxies = new HashMap<>();\n    private T mBaseInterface;\n    private T mProxyInterface;\n    private String mIdentityName;\n    private LogInvocation.Condition mInvocationLoggingCondition = LogInvocation.Condition.NEVER;\n\n\n    public Map<String, MethodProxy> getAllHooks() {\n        return mInternalMethodProxies;\n    }\n\n\n    public MethodInvocationStub(T baseInterface, Class<?>... proxyInterfaces) {\n        this.mBaseInterface = baseInterface;\n        if (baseInterface != null) {\n            if (proxyInterfaces == null) {\n                proxyInterfaces = MethodParameterUtils.getAllInterface(baseInterface.getClass());\n            }\n            mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookInvocationHandler());\n        } else {\n            VLog.d(TAG, \"Unable to build HookDelegate: %s.\", getIdentityName());\n        }\n    }\n\n    public LogInvocation.Condition getInvocationLoggingCondition() {\n        return mInvocationLoggingCondition;\n    }\n\n    public void setInvocationLoggingCondition(LogInvocation.Condition invocationLoggingCondition) {\n        mInvocationLoggingCondition = invocationLoggingCondition;\n    }\n\n    public void setIdentityName(String identityName) {\n        this.mIdentityName = identityName;\n    }\n\n    public String getIdentityName() {\n        if (mIdentityName != null) {\n            return mIdentityName;\n        }\n        return getClass().getSimpleName();\n    }\n\n    public MethodInvocationStub(T baseInterface) {\n        this(baseInterface, (Class[]) null);\n    }\n\n    /**\n     * Copy all proxies from the input HookDelegate.\n     *\n     * @param from the HookDelegate we copy from.\n     */\n    public void copyMethodProxies(MethodInvocationStub from) {\n        this.mInternalMethodProxies.putAll(from.getAllHooks());\n    }\n\n    /**\n     * Add a method proxy.\n     *\n     * @param methodProxy proxy\n     */\n    public MethodProxy addMethodProxy(MethodProxy methodProxy) {\n        if (methodProxy != null && !TextUtils.isEmpty(methodProxy.getMethodName())) {\n            if (mInternalMethodProxies.containsKey(methodProxy.getMethodName())) {\n                VLog.w(TAG, \"The Hook(%s, %s) you added has been in existence.\", methodProxy.getMethodName(),\n                        methodProxy.getClass().getName());\n                return methodProxy;\n            }\n            mInternalMethodProxies.put(methodProxy.getMethodName(), methodProxy);\n        }\n        return methodProxy;\n    }\n\n    /**\n     * Remove a method proxy.\n     *\n     * @param hookName proxy\n     * @return The proxy you removed\n     */\n    public MethodProxy removeMethodProxy(String hookName) {\n        return mInternalMethodProxies.remove(hookName);\n    }\n\n    /**\n     * Remove a method proxy.\n     *\n     * @param methodProxy target proxy\n     */\n    public void removeMethodProxy(MethodProxy methodProxy) {\n        if (methodProxy != null) {\n            removeMethodProxy(methodProxy.getMethodName());\n        }\n    }\n\n    /**\n     * Remove all method proxies.\n     */\n    public void removeAllMethodProxies() {\n        mInternalMethodProxies.clear();\n    }\n\n    /**\n     * Get the startUniformer by its name.\n     *\n     * @param name name of the Hook\n     * @param <H>  Type of the Hook\n     * @return target startUniformer\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <H extends MethodProxy> H getMethodProxy(String name) {\n        return (H) mInternalMethodProxies.get(name);\n    }\n\n    /**\n     * @return Proxy interface\n     */\n    public T getProxyInterface() {\n        return mProxyInterface;\n    }\n\n    /**\n     * @return Origin Interface\n     */\n    public T getBaseInterface() {\n        return mBaseInterface;\n    }\n\n    /**\n     * @return count of the hooks\n     */\n    public int getMethodProxiesCount() {\n        return mInternalMethodProxies.size();\n    }\n\n    private class HookInvocationHandler implements InvocationHandler {\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n            MethodProxy methodProxy = getMethodProxy(method.getName());\n            boolean useProxy = (methodProxy != null && methodProxy.isEnable());\n            boolean mightLog = (mInvocationLoggingCondition != LogInvocation.Condition.NEVER) ||\n                    (methodProxy != null && methodProxy.getInvocationLoggingCondition() != LogInvocation.Condition.NEVER);\n\n            String argStr = null;\n            Object res = null;\n            Throwable exception = null;\n            if (mightLog) {\n                // Arguments to string is done before the method is called because the method might actually change it\n                argStr = Arrays.toString(args);\n                argStr = argStr.substring(1, argStr.length()-1);\n            }\n\n\n            try {\n                if (useProxy && methodProxy.beforeCall(mBaseInterface, method, args)) {\n                    res = methodProxy.call(mBaseInterface, method, args);\n                    res = methodProxy.afterCall(mBaseInterface, method, args, res);\n                } else {\n                    res = method.invoke(mBaseInterface, args);\n                }\n                return res;\n\n            } catch (Throwable t) {\n                exception = t;\n                if (exception instanceof InvocationTargetException && ((InvocationTargetException) exception).getTargetException() != null) {\n                    exception = ((InvocationTargetException) exception).getTargetException();\n                }\n                throw exception;\n\n            } finally {\n                if (mightLog) {\n                    int logPriority = mInvocationLoggingCondition.getLogLevel(useProxy, exception != null);\n                    if (methodProxy != null) {\n                        logPriority = Math.max(logPriority, methodProxy.getInvocationLoggingCondition().getLogLevel(useProxy, exception != null));\n                    }\n                    if (logPriority >= 0) {\n                        String retString;\n                        if (exception != null) {\n                            retString = exception.toString();\n                        } else if (method.getReturnType().equals(void.class)) {\n                            retString = \"void\";\n                        } else {\n                            retString = String.valueOf(res);\n                        }\n\n                        Log.println(logPriority, TAG, method.getDeclaringClass().getSimpleName() + \".\" + method.getName() + \"(\" + argStr + \") => \" + retString);\n                    }\n                }\n            }\n        }\n    }\n\n    private void dumpMethodProxies() {\n        StringBuilder sb = new StringBuilder(50);\n        sb.append(\"*********************\");\n        for (MethodProxy proxy : mInternalMethodProxies.values()) {\n            sb.append(proxy.getMethodName()).append(\"\\n\");\n        }\n        sb.append(\"*********************\");\n        VLog.e(TAG, sb.toString());\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/MethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.VDeviceInfo;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n// 方法 Hook 基类，使用动态代理\npublic abstract class MethodProxy {\n\n    private boolean enable = true;\n    private LogInvocation.Condition mInvocationLoggingCondition = LogInvocation.Condition.NEVER; // Inherit\n\n    public MethodProxy() {\n        LogInvocation loggingAnnotation = getClass().getAnnotation(LogInvocation.class);\n        if (loggingAnnotation != null) {\n            this.mInvocationLoggingCondition = loggingAnnotation.value();\n        }\n    }\n\n    public static String getHostPkg() {\n        return VirtualCore.get().getHostPkg();\n    }\n\n    protected static Context getHostContext() {\n        return VirtualCore.get().getContext();\n    }\n\n    protected static boolean isAppProcess() {\n        return VirtualCore.get().isVAppProcess();\n    }\n\n    protected static boolean isServerProcess() {\n        return VirtualCore.get().isServerProcess();\n    }\n\n    protected static boolean isMainProcess() {\n        return VirtualCore.get().isMainProcess();\n    }\n\n    protected static int getVUid() {\n        return VClientImpl.get().getVUid();\n    }\n\n    protected static int getAppUserId() {\n        return VUserHandle.getUserId(getVUid());\n    }\n\n    protected static int getBaseVUid() {\n        return VClientImpl.get().getBaseVUid();\n    }\n\n    protected static int getRealUid() {\n        return VirtualCore.get().myUid();\n    }\n\n    protected static VDeviceInfo getDeviceInfo() {\n        return VClientImpl.get().getDeviceInfo();\n    }\n\n    public static boolean isVisiblePackage(ApplicationInfo info) {\n        return getHostPkg().equals(info.packageName)\n                || ComponentUtils.isSystemApp(info)\n                || VirtualCore.get().isOutsidePackageVisible(info.packageName);\n    }\n\n    public abstract String getMethodName();\n\n    public boolean beforeCall(Object who, Method method, Object... args) {\n        return true;\n    }\n\n    public Object call(Object who, Method method, Object... args) throws Throwable {\n        return method.invoke(who, args);\n    }\n\n    public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n        return result;\n    }\n\n    public boolean isEnable() {\n        return enable;\n    }\n\n    public void setEnable(boolean enable) {\n        this.enable = enable;\n    }\n\n    public LogInvocation.Condition getInvocationLoggingCondition() {\n        return mInvocationLoggingCondition;\n    }\n\n    public void setInvocationloggingCondition(LogInvocation.Condition invocationLoggingCondition) {\n        mInvocationLoggingCondition = invocationLoggingCondition;\n    }\n\n    public boolean isAppPkg(String pkg) {\n        return VirtualCore.get().isAppInstalled(pkg);\n    }\n\n    protected PackageManager getPM() {\n        return VirtualCore.getPM();\n    }\n\n    @Override\n    public String toString() {\n        return \"Method : \" + getMethodName();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ReplaceCallingPkgMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.Method;\n\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\n\n/**\n * @author Lody\n */\n\npublic class ReplaceCallingPkgMethodProxy extends StaticMethodProxy {\n\n\tpublic ReplaceCallingPkgMethodProxy(String name) {\n\t\tsuper(name);\n\t}\n\n\t@Override\n\tpublic boolean beforeCall(Object who, Method method, Object... args) {\n\t\tMethodParameterUtils.replaceFirstAppPkg(args);\n\t\treturn super.beforeCall(who, method, args);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ReplaceLastPkgMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.Method;\n\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\n\n/**\n * @author Lody\n */\n// 一个 Hook for 替换最新的 Pkg ？\npublic class ReplaceLastPkgMethodProxy extends StaticMethodProxy {\n\n\tpublic ReplaceLastPkgMethodProxy(String name) {\n\t\tsuper(name);\n\t}\n\n\t@Override\n\tpublic boolean beforeCall(Object who, Method method, Object... args) {\n\t\tMethodParameterUtils.replaceLastAppPkg(args);\n\t\treturn super.beforeCall(who, method, args);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ReplaceLastUidMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport android.os.Process;\n\nimport com.lody.virtual.helper.utils.ArrayUtils;\n\nimport java.lang.reflect.Method;\n\npublic class ReplaceLastUidMethodProxy extends StaticMethodProxy {\n\n    public ReplaceLastUidMethodProxy(String name) {\n        super(name);\n    }\n\n    @Override\n    public boolean beforeCall(Object who, Method method, Object... args) {\n        int index = ArrayUtils.indexOfLast(args, Integer.class);\n        if (index != -1) {\n            int uid = (int) args[index];\n            if (uid == Process.myUid()) {\n                args[index] = getRealUid();\n            }\n        }\n        return super.beforeCall(who, method, args);\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ReplaceSequencePkgMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.Method;\n\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\n\n/**\n * @author Lody\n */\n\npublic class ReplaceSequencePkgMethodProxy extends StaticMethodProxy {\n\n\tprivate int sequence;\n\n\tpublic ReplaceSequencePkgMethodProxy(String name, int sequence) {\n\t\tsuper(name);\n\t\tthis.sequence = sequence;\n\t}\n\n\t@Override\n\tpublic boolean beforeCall(Object who, Method method, Object... args) {\n\t\tMethodParameterUtils.replaceSequenceAppPkg(args, sequence);\n\t\treturn super.beforeCall(who, method, args);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ReplaceSpecPkgMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\npublic class ReplaceSpecPkgMethodProxy extends StaticMethodProxy {\n\n\tprivate int index;\n\n\tpublic ReplaceSpecPkgMethodProxy(String name, int index) {\n\t\tsuper(name);\n\t\tthis.index = index;\n\t}\n\n\t@Override\n\tpublic boolean beforeCall(Object who, Method method, Object... args) {\n\t\tif (args != null) {\n\t\t\tint i = index;\n\t\t\tif (i < 0) {\n\t\t\t\ti += args.length;\n\t\t\t}\n\t\t\tif (i >= 0 && i < args.length && args[i] instanceof String) {\n\t\t\t\targs[i] = getHostPkg();\n\t\t\t}\n\t\t}\n\t\treturn super.beforeCall(who, method, args);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ReplaceUidMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.Method;\n\npublic class ReplaceUidMethodProxy extends StaticMethodProxy {\n\n        private final int index;\n        public ReplaceUidMethodProxy(String name, int index) {\n            super(name);\n            this.index = index;\n        }\n\n    @Override\n    public boolean beforeCall(Object who, Method method, Object... args) {\n        int uid = (int) args[index];\n        if (uid == getVUid() || uid == getBaseVUid()) {\n            args[index] = getRealUid();\n        }\n        return super.beforeCall(who, method, args);\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/ResultStaticMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\npublic class ResultStaticMethodProxy extends StaticMethodProxy {\n\n\tObject mResult;\n\n\tpublic ResultStaticMethodProxy(String name, Object result) {\n\t\tsuper(name);\n\t\tmResult = result;\n\t}\n\n\tpublic Object getResult() {\n\t\treturn mResult;\n\t}\n\n\t@Override\n\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\treturn mResult;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/SkipInject.java",
    "content": "package com.lody.virtual.client.hook.base;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author Lody\n *\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface SkipInject {\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/base/StaticMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.base;\n\n/**\n * @author Lody\n */\n\npublic class StaticMethodProxy extends MethodProxy {\n\n\tprivate String mName;\n\n\tpublic StaticMethodProxy(String name) {\n\t\tthis.mName = name;\n\t}\n\n\t@Override\n\tpublic String getMethodName() {\n\t\treturn mName;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/delegate/AppInstrumentation.java",
    "content": "package com.lody.virtual.client.hook.delegate;\n\nimport android.app.Activity;\nimport android.app.Application;\nimport android.app.Instrumentation;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.fixer.ActivityFixer;\nimport com.lody.virtual.client.fixer.ContextFixer;\nimport com.lody.virtual.client.interfaces.IInjector;\nimport com.lody.virtual.client.ipc.ActivityClientRecord;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.helper.compat.BundleCompat;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.server.interfaces.IUiCallback;\n\nimport mirror.android.app.ActivityThread;\n\n/**\n * @author Lody\n */\npublic final class AppInstrumentation extends InstrumentationDelegate implements IInjector {\n\n    private static final String TAG = AppInstrumentation.class.getSimpleName();\n\n    private static AppInstrumentation gDefault;\n\n    private AppInstrumentation(Instrumentation base) {\n        super(base);\n    }\n\n    public static AppInstrumentation getDefault() {\n        if (gDefault == null) {\n            synchronized (AppInstrumentation.class) {\n                if (gDefault == null) {\n                    gDefault = create();\n                }\n            }\n        }\n        return gDefault;\n    }\n\n    private static AppInstrumentation create() {\n        Instrumentation instrumentation = ActivityThread.mInstrumentation.get(VirtualCore.mainThread());\n        if (instrumentation instanceof AppInstrumentation) {\n            return (AppInstrumentation) instrumentation;\n        }\n        return new AppInstrumentation(instrumentation);\n    }\n\n\n    @Override\n    public void inject() throws Throwable {\n        base = ActivityThread.mInstrumentation.get(VirtualCore.mainThread());\n        ActivityThread.mInstrumentation.set(VirtualCore.mainThread(), this);\n    }\n\n    @Override\n    public boolean isEnvBad() {\n        return !(ActivityThread.mInstrumentation.get(VirtualCore.mainThread()) instanceof AppInstrumentation);\n    }\n\n    @Override\n    public void callActivityOnCreate(Activity activity, Bundle icicle) {\n        VirtualCore.get().getComponentDelegate().beforeActivityCreate(activity);\n        IBinder token = mirror.android.app.Activity.mToken.get(activity);\n        ActivityClientRecord r = VActivityManager.get().getActivityRecord(token);\n        // 替换 Activity 对象\n        if (r != null) {\n            r.activity = activity;\n        }\n        ContextFixer.fixContext(activity);\n        ActivityFixer.fixActivity(activity);\n        ActivityInfo info = null;\n        if (r != null) {\n            info = r.info;\n        }\n        // 设置主题和屏幕纵横控制\n        if (info != null) {\n            if (info.theme != 0) {\n                activity.setTheme(info.theme);\n            }\n            if (activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED\n                    && info.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {\n                activity.setRequestedOrientation(info.screenOrientation);\n            }\n        }\n        super.callActivityOnCreate(activity, icicle);\n        VirtualCore.get().getComponentDelegate().afterActivityCreate(activity);\n    }\n\n    @Override\n    public void callActivityOnResume(Activity activity) {\n        VirtualCore.get().getComponentDelegate().beforeActivityResume(activity);\n        VActivityManager.get().onActivityResumed(activity);\n        super.callActivityOnResume(activity);\n        VirtualCore.get().getComponentDelegate().afterActivityResume(activity);\n        Intent intent = activity.getIntent();\n        if (intent != null) {\n            Bundle bundle = intent.getBundleExtra(\"_VA_|_sender_\");\n            if (bundle != null) {\n                IBinder callbackToken = BundleCompat.getBinder(bundle, \"_VA_|_ui_callback_\");\n                IUiCallback callback = IUiCallback.Stub.asInterface(callbackToken);\n                if (callback != null) {\n                    try {\n                        callback.onAppOpened(VClientImpl.get().getCurrentPackage(), VUserHandle.myUserId());\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n    }\n\n\n    @Override\n    public void callActivityOnDestroy(Activity activity) {\n        VirtualCore.get().getComponentDelegate().beforeActivityDestroy(activity);\n        super.callActivityOnDestroy(activity);\n        VirtualCore.get().getComponentDelegate().afterActivityDestroy(activity);\n    }\n\n    @Override\n    public void callActivityOnPause(Activity activity) {\n        VirtualCore.get().getComponentDelegate().beforeActivityPause(activity);\n        super.callActivityOnPause(activity);\n        VirtualCore.get().getComponentDelegate().afterActivityPause(activity);\n    }\n\n\n    @Override\n    public void callApplicationOnCreate(Application app) {\n        super.callApplicationOnCreate(app);\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/delegate/ComponentDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\n\n\nimport android.content.Intent;\n\nimport android.app.Activity;\n\npublic interface ComponentDelegate {\n\n    ComponentDelegate EMPTY = new ComponentDelegate() {\n\n        @Override\n        public void beforeActivityCreate(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void beforeActivityResume(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void beforeActivityPause(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void beforeActivityDestroy(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void afterActivityCreate(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void afterActivityResume(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void afterActivityPause(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void afterActivityDestroy(Activity activity) {\n            // Empty\n        }\n\n        @Override\n        public void onSendBroadcast(Intent intent) {\n            // Empty\n        }\n    };\n\n    void beforeActivityCreate(Activity activity);\n\n    void beforeActivityResume(Activity activity);\n\n    void beforeActivityPause(Activity activity);\n\n    void beforeActivityDestroy(Activity activity);\n\n    void afterActivityCreate(Activity activity);\n\n    void afterActivityResume(Activity activity);\n\n    void afterActivityPause(Activity activity);\n\n    void afterActivityDestroy(Activity activity);\n\n    void onSendBroadcast(Intent intent);\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/delegate/InstrumentationDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\n\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.Application;\nimport android.app.Instrumentation;\nimport android.app.UiAutomation;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ActivityInfo;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.PersistableBundle;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\n\n/**\n * @author Lody\n */\npublic class InstrumentationDelegate extends Instrumentation {\n\n\tprotected Instrumentation base;\n\n\tpublic InstrumentationDelegate(Instrumentation base) {\n\t\tthis.base = base;\n\t}\n\n\tpublic static Application newApplication(Class<?> clazz, Context context)\n\t\t\tthrows InstantiationException, IllegalAccessException, ClassNotFoundException {\n\t\treturn Instrumentation.newApplication(clazz, context);\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle arguments) {\n\t\tbase.onCreate(arguments);\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tbase.start();\n\t}\n\n\t@Override\n\tpublic void onStart() {\n\t\tbase.onStart();\n\t}\n\n\t@Override\n\tpublic boolean onException(Object obj, Throwable e) {\n\t\treturn base.onException(obj, e);\n\t}\n\n\t@Override\n\tpublic void sendStatus(int resultCode, Bundle results) {\n\t\tbase.sendStatus(resultCode, results);\n\t}\n\n\t@Override\n\tpublic void finish(int resultCode, Bundle results) {\n\t\tbase.finish(resultCode, results);\n\t}\n\n\t@Override\n\tpublic void setAutomaticPerformanceSnapshots() {\n\t\tbase.setAutomaticPerformanceSnapshots();\n\t}\n\n\t@Override\n\tpublic void startPerformanceSnapshot() {\n\t\tbase.startPerformanceSnapshot();\n\t}\n\n\t@Override\n\tpublic void endPerformanceSnapshot() {\n\t\tbase.endPerformanceSnapshot();\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tbase.onDestroy();\n\t}\n\n\t@Override\n\tpublic Context getContext() {\n\t\treturn base.getContext();\n\t}\n\n\t@Override\n\tpublic ComponentName getComponentName() {\n\t\treturn base.getComponentName();\n\t}\n\n\t@Override\n\tpublic Context getTargetContext() {\n\t\treturn base.getTargetContext();\n\t}\n\n\t@Override\n\tpublic boolean isProfiling() {\n\t\treturn base.isProfiling();\n\t}\n\n\t@Override\n\tpublic void startProfiling() {\n\t\tbase.startProfiling();\n\t}\n\n\t@Override\n\tpublic void stopProfiling() {\n\t\tbase.stopProfiling();\n\t}\n\n\t@Override\n\tpublic void setInTouchMode(boolean inTouch) {\n\t\tbase.setInTouchMode(inTouch);\n\t}\n\n\t@Override\n\tpublic void waitForIdle(Runnable recipient) {\n\t\tbase.waitForIdle(recipient);\n\t}\n\n\t@Override\n\tpublic void waitForIdleSync() {\n\t\tbase.waitForIdleSync();\n\t}\n\n\t@Override\n\tpublic void runOnMainSync(Runnable runner) {\n\t\tbase.runOnMainSync(runner);\n\t}\n\n\t@Override\n\tpublic Activity startActivitySync(Intent intent) {\n\t\treturn base.startActivitySync(intent);\n\t}\n\n\t@Override\n\tpublic void addMonitor(ActivityMonitor monitor) {\n\t\tbase.addMonitor(monitor);\n\t}\n\n\t@Override\n\tpublic ActivityMonitor addMonitor(IntentFilter filter, ActivityResult result, boolean block) {\n\t\treturn base.addMonitor(filter, result, block);\n\t}\n\n\t@Override\n\tpublic ActivityMonitor addMonitor(String cls, ActivityResult result, boolean block) {\n\t\treturn base.addMonitor(cls, result, block);\n\t}\n\n\t@Override\n\tpublic boolean checkMonitorHit(ActivityMonitor monitor, int minHits) {\n\t\treturn base.checkMonitorHit(monitor, minHits);\n\t}\n\n\t@Override\n\tpublic Activity waitForMonitor(ActivityMonitor monitor) {\n\t\treturn base.waitForMonitor(monitor);\n\t}\n\n\t@Override\n\tpublic Activity waitForMonitorWithTimeout(ActivityMonitor monitor, long timeOut) {\n\t\treturn base.waitForMonitorWithTimeout(monitor, timeOut);\n\t}\n\n\t@Override\n\tpublic void removeMonitor(ActivityMonitor monitor) {\n\t\tbase.removeMonitor(monitor);\n\t}\n\n\t@Override\n\tpublic boolean invokeMenuActionSync(Activity targetActivity, int id, int flag) {\n\t\treturn base.invokeMenuActionSync(targetActivity, id, flag);\n\t}\n\n\t@Override\n\tpublic boolean invokeContextMenuAction(Activity targetActivity, int id, int flag) {\n\t\treturn base.invokeContextMenuAction(targetActivity, id, flag);\n\t}\n\n\t@Override\n\tpublic void sendStringSync(String text) {\n\t\tbase.sendStringSync(text);\n\t}\n\n\t@Override\n\tpublic void sendKeySync(KeyEvent event) {\n\t\tbase.sendKeySync(event);\n\t}\n\n\t@Override\n\tpublic void sendKeyDownUpSync(int key) {\n\t\tbase.sendKeyDownUpSync(key);\n\t}\n\n\t@Override\n\tpublic void sendCharacterSync(int keyCode) {\n\t\tbase.sendCharacterSync(keyCode);\n\t}\n\n\t@Override\n\tpublic void sendPointerSync(MotionEvent event) {\n\t\tbase.sendPointerSync(event);\n\t}\n\n\t@Override\n\tpublic void sendTrackballEventSync(MotionEvent event) {\n\t\tbase.sendTrackballEventSync(event);\n\t}\n\n\t@Override\n\tpublic Application newApplication(ClassLoader cl, String className, Context context)\n\t\t\tthrows InstantiationException, IllegalAccessException, ClassNotFoundException {\n\t\treturn base.newApplication(cl, className, context);\n\t}\n\n\t@Override\n\tpublic void callApplicationOnCreate(Application app) {\n\t\tbase.callApplicationOnCreate(app);\n\t}\n\n\t@Override\n\tpublic Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent,\n\t\t\tActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance)\n\t\t\tthrows InstantiationException, IllegalAccessException {\n\t\treturn base.newActivity(clazz, context, token, application, intent, info, title, parent, id,\n\t\t\t\tlastNonConfigurationInstance);\n\t}\n\n\t@Override\n\tpublic Activity newActivity(ClassLoader cl, String className, Intent intent)\n\t\t\tthrows InstantiationException, IllegalAccessException, ClassNotFoundException {\n\t\treturn base.newActivity(cl, className, intent);\n\t}\n\n\t@Override\n\tpublic void callActivityOnCreate(Activity activity, Bundle icicle) {\n\t\tbase.callActivityOnCreate(activity, icicle);\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\t@Override\n\tpublic void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {\n\t\tbase.callActivityOnCreate(activity, icicle, persistentState);\n\t}\n\n\t@Override\n\tpublic void callActivityOnDestroy(Activity activity) {\n\t\tbase.callActivityOnDestroy(activity);\n\t}\n\n\t@Override\n\tpublic void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {\n\t\tbase.callActivityOnRestoreInstanceState(activity, savedInstanceState);\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\t@Override\n\tpublic void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,\n\t\t\tPersistableBundle persistentState) {\n\t\tbase.callActivityOnRestoreInstanceState(activity, savedInstanceState, persistentState);\n\t}\n\n\t@Override\n\tpublic void callActivityOnPostCreate(Activity activity, Bundle icicle) {\n\t\tbase.callActivityOnPostCreate(activity, icicle);\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\t@Override\n\tpublic void callActivityOnPostCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {\n\t\tbase.callActivityOnPostCreate(activity, icicle, persistentState);\n\t}\n\n\t@Override\n\tpublic void callActivityOnNewIntent(Activity activity, Intent intent) {\n\t\tbase.callActivityOnNewIntent(activity, intent);\n\t}\n\n\n\t@Override\n\tpublic void callActivityOnStart(Activity activity) {\n\t\tbase.callActivityOnStart(activity);\n\t}\n\n\t@Override\n\tpublic void callActivityOnRestart(Activity activity) {\n\t\tbase.callActivityOnRestart(activity);\n\t}\n\n\t@Override\n\tpublic void callActivityOnResume(Activity activity) {\n\t\tbase.callActivityOnResume(activity);\n\t}\n\n\t@Override\n\tpublic void callActivityOnStop(Activity activity) {\n\t\tbase.callActivityOnStop(activity);\n\t}\n\n\t@Override\n\tpublic void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {\n\t\tbase.callActivityOnSaveInstanceState(activity, outState);\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\t@Override\n\tpublic void callActivityOnSaveInstanceState(Activity activity, Bundle outState,\n\t\t\tPersistableBundle outPersistentState) {\n\t\tbase.callActivityOnSaveInstanceState(activity, outState, outPersistentState);\n\t}\n\n\t@Override\n\tpublic void callActivityOnPause(Activity activity) {\n\t\tbase.callActivityOnPause(activity);\n\t}\n\n\t@Override\n\tpublic void callActivityOnUserLeaving(Activity activity) {\n\t\tbase.callActivityOnUserLeaving(activity);\n\t}\n\n\t@Override\n\tpublic Bundle getAllocCounts() {\n\t\treturn base.getAllocCounts();\n\t}\n\n\t@Override\n\tpublic Bundle getBinderCounts() {\n\t\treturn base.getBinderCounts();\n\t}\n\n\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)\n\t@Override\n\tpublic UiAutomation getUiAutomation() {\n\t\treturn base.getUiAutomation();\n\t}\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/delegate/PhoneInfoDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\n\npublic interface PhoneInfoDelegate {\n\n    /***\n     * Fake the Device ID.\n     *\n     * @param oldDeviceId old DeviceId\n     * @param userId      user\n     */\n    String getDeviceId(String oldDeviceId, int userId);\n\n    /***\n     * Fake the BluetoothAddress\n     *\n     * @param oldBluetoothAddress old BluetoothAddress\n     * @param userId              user\n     */\n    String getBluetoothAddress(String oldBluetoothAddress, int userId);\n\n    /***\n     * Fake the macAddress\n     *\n     * @param oldMacAddress old MacAddress\n     * @param userId        user\n     */\n    String getMacAddress(String oldMacAddress, int userId);\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/delegate/TaskDescriptionDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.ActivityManager;\nimport android.os.Build;\n\npublic interface TaskDescriptionDelegate {\n    public ActivityManager.TaskDescription getTaskDescription(ActivityManager.TaskDescription oldTaskDescription);\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/providers/DownloadProviderHook.java",
    "content": "package com.lody.virtual.client.hook.providers;\n\nimport android.content.ContentValues;\nimport android.net.Uri;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.MethodBox;\n\nimport java.lang.reflect.InvocationTargetException;\n\n/**\n * @author Lody\n */\n\nclass DownloadProviderHook extends ExternalProviderHook {\n\n    private static final String TAG = DownloadProviderHook.class.getSimpleName();\n\n    private static final String COLUMN_NOTIFICATION_PACKAGE = \"notificationpackage\";\n    private static final String COLUMN_IS_PUBLIC_API = \"is_public_api\";\n    private static final String COLUMN_OTHER_UID = \"otheruid\";\n    private static final String COLUMN_COOKIE_DATA = \"cookiedata\";\n    private static final String COLUMN_NOTIFICATION_CLASS = \"notificationclass\";\n    private static final String INSERT_KEY_PREFIX = \"http_header_\";\n\n\n    private static final String[] ENFORCE_REMOVE_COLUMNS = {\n            COLUMN_OTHER_UID,\n            COLUMN_NOTIFICATION_CLASS\n    };\n\n    DownloadProviderHook(Object base) {\n        super(base);\n    }\n\n    @Override\n    public Uri insert(MethodBox methodBox, Uri url, ContentValues initialValues) throws InvocationTargetException {\n        if (initialValues.containsKey(COLUMN_NOTIFICATION_PACKAGE)) {\n            initialValues.put(COLUMN_NOTIFICATION_PACKAGE, VirtualCore.get().getHostPkg());\n        }\n        if (initialValues.containsKey(COLUMN_COOKIE_DATA)) {\n            String cookie = initialValues.getAsString(COLUMN_COOKIE_DATA);\n            initialValues.remove(COLUMN_COOKIE_DATA);\n            // retrieve the next free INSERT_KEY_PREFIX\n            int headerIndex = 0;\n            while (initialValues.containsKey(INSERT_KEY_PREFIX + headerIndex)) {\n                headerIndex++;\n            }\n            // add the cookie\n            initialValues.put(INSERT_KEY_PREFIX + headerIndex, \"Cookie\" + \": \" + cookie);\n        }\n        if (!initialValues.containsKey(COLUMN_IS_PUBLIC_API)) {\n            initialValues.put(COLUMN_IS_PUBLIC_API, true);\n        }\n        for (String column : ENFORCE_REMOVE_COLUMNS) {\n            if (initialValues.containsKey(column)) {\n                initialValues.remove(column);\n            }\n        }\n        return super.insert(methodBox, url, initialValues);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/providers/ExternalProviderHook.java",
    "content": "package com.lody.virtual.client.hook.providers;\n\nimport com.lody.virtual.client.core.VirtualCore;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\npublic class ExternalProviderHook extends ProviderHook {\n\n    public ExternalProviderHook(Object base) {\n        super(base);\n    }\n\n    @Override\n    protected void processArgs(Method method, Object... args) {\n        if (args != null && args.length > 0 && args[0] instanceof String) {\n            String pkg = (String) args[0];\n            if (VirtualCore.get().isAppInstalled(pkg)) {\n                args[0] = VirtualCore.get().getHostPkg();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/providers/InternalProviderHook.java",
    "content": "package com.lody.virtual.client.hook.providers;\n\n/**\n * @author Lody\n */\n\npublic class InternalProviderHook extends ProviderHook {\n\n    public InternalProviderHook(Object base) {\n        super(base);\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/providers/ProviderHook.java",
    "content": "package com.lody.virtual.client.hook.providers;\n\nimport android.content.ContentValues;\nimport android.content.res.AssetFileDescriptor;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IInterface;\nimport android.os.ParcelFileDescriptor;\n\nimport com.lody.virtual.client.hook.base.MethodBox;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport mirror.android.content.IContentProvider;\n\n/**\n * @author Lody\n */\n\npublic class ProviderHook implements InvocationHandler {\n\n    private static final Map<String, HookFetcher> PROVIDER_MAP = new HashMap<>();\n\n    static {\n        PROVIDER_MAP.put(\"settings\", new HookFetcher() {\n            @Override\n            public ProviderHook fetch(boolean external, IInterface provider) {\n                return new SettingsProviderHook(provider);\n            }\n        });\n        PROVIDER_MAP.put(\"downloads\", new HookFetcher() {\n            @Override\n            public ProviderHook fetch(boolean external, IInterface provider) {\n                return new DownloadProviderHook(provider);\n            }\n        });\n    }\n\n    protected final Object mBase;\n\n    public ProviderHook(Object base) {\n        this.mBase = base;\n    }\n\n    private static HookFetcher fetchHook(String authority) {\n        HookFetcher fetcher = PROVIDER_MAP.get(authority);\n        if (fetcher == null) {\n            fetcher = new HookFetcher() {\n                @Override\n                public ProviderHook fetch(boolean external, IInterface provider) {\n                    if (external) {\n                        return new ExternalProviderHook(provider);\n                    }\n                    return new InternalProviderHook(provider);\n                }\n            };\n        }\n        return fetcher;\n    }\n\n    private static IInterface createProxy(IInterface provider, ProviderHook hook) {\n        if (provider == null || hook == null) {\n            return null;\n        }\n        return (IInterface) Proxy.newProxyInstance(provider.getClass().getClassLoader(), new Class[]{\n                IContentProvider.TYPE,\n        }, hook);\n    }\n\n    public static IInterface createProxy(boolean external, String authority, IInterface provider) {\n        if (provider instanceof Proxy && Proxy.getInvocationHandler(provider) instanceof ProviderHook) {\n            return provider;\n        }\n        ProviderHook.HookFetcher fetcher = ProviderHook.fetchHook(authority);\n        if (fetcher != null) {\n            ProviderHook hook = fetcher.fetch(external, provider);\n            IInterface proxyProvider = ProviderHook.createProxy(provider, hook);\n            if (proxyProvider != null) {\n                provider = proxyProvider;\n            }\n        }\n        return provider;\n    }\n\n    public Bundle call(MethodBox methodBox, String method, String arg, Bundle extras) throws InvocationTargetException {\n        return methodBox.call();\n    }\n\n    public Uri insert(MethodBox methodBox, Uri url, ContentValues initialValues) throws InvocationTargetException {\n\n        return (Uri) methodBox.call();\n    }\n\n    public Cursor query(MethodBox methodBox, Uri url, String[] projection, String selection,\n                        String[] selectionArgs, String sortOrder) throws InvocationTargetException {\n        return (Cursor) methodBox.call();\n    }\n\n    public String getType(MethodBox methodBox, Uri url) throws InvocationTargetException {\n        return (String) methodBox.call();\n    }\n\n    public int bulkInsert(MethodBox methodBox, Uri url, ContentValues[] initialValues) throws InvocationTargetException {\n        return (int) methodBox.call();\n    }\n\n    public int delete(MethodBox methodBox, Uri url, String selection, String[] selectionArgs) throws InvocationTargetException {\n        return (int) methodBox.call();\n    }\n\n    public int update(MethodBox methodBox, Uri url, ContentValues values, String selection,\n                      String[] selectionArgs) throws InvocationTargetException {\n        return (int) methodBox.call();\n    }\n\n    public ParcelFileDescriptor openFile(MethodBox methodBox, Uri url, String mode) throws InvocationTargetException {\n        return (ParcelFileDescriptor) methodBox.call();\n    }\n\n    public AssetFileDescriptor openAssetFile(MethodBox methodBox, Uri url, String mode) throws InvocationTargetException {\n        return (AssetFileDescriptor) methodBox.call();\n    }\n\n    @Override\n    public Object invoke(Object proxy, Method method, Object... args) throws Throwable {\n        try {\n            processArgs(method, args);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        MethodBox methodBox = new MethodBox(method, mBase, args);\n        int start = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 ? 1 : 0;\n        try {\n            String name = method.getName();\n            if (\"call\".equals(name)) {\n                String methodName = (String) args[start];\n                String arg = (String) args[start + 1];\n                Bundle extras = (Bundle) args[start + 2];\n                return call(methodBox, methodName, arg, extras);\n            } else if (\"insert\".equals(name)) {\n                Uri url = (Uri) args[start];\n                ContentValues initialValues = (ContentValues) args[start + 1];\n                return insert(methodBox, url, initialValues);\n            } else if (\"getType\".equals(name)) {\n                return getType(methodBox, (Uri) args[0]);\n            } else if (\"delete\".equals(name)) {\n                Uri url = (Uri) args[0];\n                String selection = (String) args[1];\n                String[] selectionArgs = (String[]) args[2];\n                return delete(methodBox, url, selection, selectionArgs);\n            } else if (\"bulkInsert\".equals(name)) {\n                Uri url = (Uri) args[start];\n                ContentValues[] initialValues = (ContentValues[]) args[start + 1];\n                return bulkInsert(methodBox, url, initialValues);\n            } else if (\"update\".equals(name)) {\n                Uri url = (Uri) args[start];\n                ContentValues values = (ContentValues) args[start + 1];\n                String selection = (String) args[start + 2];\n                String[] selectionArgs = (String[]) args[start + 3];\n                return update(methodBox, url, values, selection, selectionArgs);\n            } else if (\"openFile\".equals(name)) {\n                Uri url = (Uri) args[start];\n                String mode = (String) args[start + 1];\n                return openFile(methodBox, url, mode);\n            } else if (\"openAssetFile\".equals(name)) {\n                Uri url = (Uri) args[start];\n                String mode = (String) args[start + 1];\n                return openAssetFile(methodBox, url, mode);\n            } else if (\"query\".equals(name)) {\n                Uri url = (Uri) args[start];\n                String[] projection = (String[]) args[start + 1];\n                String selection = (String) args[start + 2];\n                String[] selectionArgs = (String[]) args[start + 3];\n                String sortOrder = (String) args[start + 4];\n                return query(methodBox, url, projection, selection, selectionArgs, sortOrder);\n            }\n            return methodBox.call();\n        } catch (Throwable e) {\n            VLog.d(\"ProviderHook\", \"call: %s (%s) with error\", method.getName(), Arrays.toString(args));\n            if (e instanceof InvocationTargetException) {\n                throw e.getCause();\n            }\n            throw e;\n        }\n    }\n\n    protected void processArgs(Method method, Object... args) {\n\n    }\n\n    public interface HookFetcher {\n        ProviderHook fetch(boolean external, IInterface provider);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/providers/SettingsProviderHook.java",
    "content": "package com.lody.virtual.client.hook.providers;\n\nimport android.os.Build;\nimport android.os.Bundle;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.hook.base.MethodBox;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class SettingsProviderHook extends ExternalProviderHook {\n\n    private static final String TAG = SettingsProviderHook.class.getSimpleName();\n\n    private static final int METHOD_GET = 0;\n    private static final int METHOD_PUT = 1;\n\n    private static final Map<String, String> PRE_SET_VALUES = new HashMap<>();\n\n    static {\n        PRE_SET_VALUES.put(\"user_setup_complete\", \"1\");\n        PRE_SET_VALUES.put(\"install_non_market_apps\", \"0\");\n    }\n\n\n    public SettingsProviderHook(Object base) {\n        super(base);\n    }\n\n    private static int getMethodType(String method) {\n        if (method.startsWith(\"GET_\")) {\n            return METHOD_GET;\n        }\n        if (method.startsWith(\"PUT_\")) {\n            return METHOD_PUT;\n        }\n        return -1;\n    }\n\n    private static boolean isSecureMethod(String method) {\n        return method.endsWith(\"secure\");\n    }\n\n\n    @Override\n    public Bundle call(MethodBox methodBox, String method, String arg, Bundle extras) throws InvocationTargetException {\n        int methodType = getMethodType(method);\n        if (METHOD_GET == methodType) {\n            String presetValue = PRE_SET_VALUES.get(arg);\n            if (presetValue != null) {\n                return wrapBundle(arg, presetValue);\n            }\n            if (\"android_id\".equals(arg)) {\n                return wrapBundle(\"android_id\", VClientImpl.get().getDeviceInfo().androidId);\n            }\n        }\n        if (METHOD_PUT == methodType) {\n            if (isSecureMethod(method)) {\n                return null;\n            }\n        }\n        try {\n            return methodBox.call();\n        } catch (InvocationTargetException e) {\n            if (e.getCause() instanceof SecurityException) {\n                return null;\n            }\n            throw e;\n        }\n    }\n\n    private Bundle wrapBundle(String name, String value) {\n        Bundle bundle = new Bundle();\n        if (Build.VERSION.SDK_INT >= 24) {\n            bundle.putString(\"name\", name);\n            bundle.putString(\"value\", value);\n        } else {\n            bundle.putString(name, value);\n        }\n        return bundle;\n    }\n\n    @Override\n    protected void processArgs(Method method, Object... args) {\n        super.processArgs(method, args);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/account/AccountManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.account;\n\nimport android.accounts.Account;\nimport android.accounts.IAccountManagerResponse;\nimport android.content.Context;\nimport android.os.Bundle;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.ipc.VAccountManager;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.accounts.IAccountManager;\n\n/**\n * @author Lody\n */\npublic class AccountManagerStub extends BinderInvocationProxy {\n\n\tprivate static VAccountManager Mgr = VAccountManager.get();\n\n\tpublic AccountManagerStub() {\n\t\tsuper(IAccountManager.Stub.asInterface, Context.ACCOUNT_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new getPassword());\n\t\taddMethodProxy(new getUserData());\n\t\taddMethodProxy(new getAuthenticatorTypes());\n\t\taddMethodProxy(new getAccounts());\n\t\taddMethodProxy(new getAccountsForPackage());\n\t\taddMethodProxy(new getAccountsByTypeForPackage());\n\t\taddMethodProxy(new getAccountsAsUser());\n\t\taddMethodProxy(new hasFeatures());\n\t\taddMethodProxy(new getAccountsByFeatures());\n\t\taddMethodProxy(new addAccountExplicitly());\n\t\taddMethodProxy(new removeAccount());\n\t\taddMethodProxy(new removeAccountAsUser());\n\t\taddMethodProxy(new removeAccountExplicitly());\n\t\taddMethodProxy(new copyAccountToUser());\n\t\taddMethodProxy(new invalidateAuthToken());\n\t\taddMethodProxy(new peekAuthToken());\n\t\taddMethodProxy(new setAuthToken());\n\t\taddMethodProxy(new setPassword());\n\t\taddMethodProxy(new clearPassword());\n\t\taddMethodProxy(new setUserData());\n\t\taddMethodProxy(new updateAppPermission());\n\t\taddMethodProxy(new getAuthToken());\n\t\taddMethodProxy(new addAccount());\n\t\taddMethodProxy(new addAccountAsUser());\n\t\taddMethodProxy(new updateCredentials());\n\t\taddMethodProxy(new editProperties());\n\t\taddMethodProxy(new confirmCredentialsAsUser());\n\t\taddMethodProxy(new accountAuthenticated());\n\t\taddMethodProxy(new getAuthTokenLabel());\n\t\taddMethodProxy(new addSharedAccountAsUser());\n\t\taddMethodProxy(new getSharedAccountsAsUser());\n\t\taddMethodProxy(new removeSharedAccountAsUser());\n\t\taddMethodProxy(new renameAccount());\n\t\taddMethodProxy(new getPreviousName());\n\t\taddMethodProxy(new renameSharedAccountAsUser());\n\t}\n\n\tprivate static class getPassword extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getPassword\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\treturn Mgr.getPassword(account);\n\t\t}\n\t}\n\n\tprivate static class getUserData extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getUserData\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString key = (String) args[1];\n\t\t\treturn Mgr.getUserData(account, key);\n\t\t}\n\t}\n\n\tprivate static class getAuthenticatorTypes extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAuthenticatorTypes\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\treturn Mgr.getAuthenticatorTypes();\n\t\t}\n\t}\n\n\tprivate static class getAccounts extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAccounts\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tString accountType = (String) args[0];\n\t\t\treturn Mgr.getAccounts(accountType);\n\t\t}\n\t}\n\n\tprivate static class getAccountsForPackage extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAccountsForPackage\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tString packageName = (String) args[0];\n\t\t\treturn Mgr.getAccounts(null);\n\t\t}\n\t}\n\n\tprivate static class getAccountsByTypeForPackage extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAccountsByTypeForPackage\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tString type = (String) args[0];\n\t\t\tString packageName = (String) args[1];\n\t\t\treturn Mgr.getAccounts(type);\n\t\t}\n\t}\n\n\tprivate static class getAccountsAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAccountsAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tString accountType = (String) args[0];\n\t\t\treturn Mgr.getAccounts(accountType);\n\t\t}\n\t}\n\n\tprivate static class hasFeatures extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"hasFeatures\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tString[] features = (String[]) args[2];\n\t\t\tMgr.hasFeatures(response, account, features);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class getAccountsByFeatures extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAccountsByFeatures\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tString accountType = (String) args[1];\n\t\t\tString[] features = (String[]) args[2];\n\t\t\tMgr.getAccountsByFeatures(response, accountType, features);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class addAccountExplicitly extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"addAccountExplicitly\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString password = (String) args[1];\n\t\t\tBundle extras = (Bundle) args[2];\n\t\t\treturn Mgr.addAccountExplicitly(account, password, extras);\n\t\t}\n\t}\n\n\tprivate static class removeAccount extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"removeAccount\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tboolean expectActivityLaunch = (boolean) args[2];\n\t\t\tMgr.removeAccount(response, account, expectActivityLaunch);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class removeAccountAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"removeAccountAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tboolean expectActivityLaunch = (boolean) args[2];\n\t\t\tMgr.removeAccount(response, account, expectActivityLaunch);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class removeAccountExplicitly extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"removeAccountExplicitly\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\treturn Mgr.removeAccountExplicitly(account);\n\t\t}\n\t}\n\n\tprivate static class copyAccountToUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"copyAccountToUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tint userFrom = (int) args[2];\n\t\t\tint userTo = (int) args[3];\n\t\t\tmethod.invoke(who, args);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class invalidateAuthToken extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"invalidateAuthToken\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tString accountType = (String) args[0];\n\t\t\tString authToken = (String) args[1];\n\t\t\tMgr.invalidateAuthToken(accountType, authToken);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class peekAuthToken extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"peekAuthToken\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString authTokenType = (String) args[1];\n\t\t\treturn Mgr.peekAuthToken(account, authTokenType);\n\t\t}\n\t}\n\n\tprivate static class setAuthToken extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"setAuthToken\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString authTokenType = (String) args[1];\n\t\t\tString authToken = (String) args[2];\n\t\t\tMgr.setAuthToken(account, authTokenType, authToken);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class setPassword extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"setPassword\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString password = (String) args[1];\n\t\t\tMgr.setPassword(account, password);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class clearPassword extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"clearPassword\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tMgr.clearPassword(account);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class setUserData extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"setUserData\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString key = (String) args[1];\n\t\t\tString value = (String) args[2];\n\t\t\tMgr.setUserData(account, key, value);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class updateAppPermission extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"updateAppPermission\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tString authTokenType = (String) args[1];\n\t\t\tint uid = (int) args[2];\n\t\t\tboolean val = (boolean) args[3];\n\t\t\tmethod.invoke(who, args);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class getAuthToken extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAuthToken\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tString authTokenType = (String) args[2];\n\t\t\tboolean notifyOnAuthFailure = (boolean) args[3];\n\t\t\tboolean expectActivityLaunch = (boolean) args[4];\n\t\t\tBundle options = (Bundle) args[5];\n\t\t\tMgr.getAuthToken(response, account, authTokenType, notifyOnAuthFailure, expectActivityLaunch, options);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class addAccount extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"addAccount\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tString accountType = (String) args[1];\n\t\t\tString authTokenType = (String) args[2];\n\t\t\tString[] requiredFeatures = (String[]) args[3];\n\t\t\tboolean expectActivityLaunch = (boolean) args[4];\n\t\t\tBundle options = (Bundle) args[5];\n\t\t\tMgr.addAccount(response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, options);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class addAccountAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"addAccountAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tString accountType = (String) args[1];\n\t\t\tString authTokenType = (String) args[2];\n\t\t\tString[] requiredFeatures = (String[]) args[3];\n\t\t\tboolean expectActivityLaunch = (boolean) args[4];\n\t\t\tBundle options = (Bundle) args[5];\n\t\t\tMgr.addAccount(response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, options);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class updateCredentials extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"updateCredentials\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tString authTokenType = (String) args[2];\n\t\t\tboolean expectActivityLaunch = (boolean) args[3];\n\t\t\tBundle options = (Bundle) args[4];\n\t\t\tMgr.updateCredentials(response, account, authTokenType, expectActivityLaunch, options);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class editProperties extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"editProperties\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tString authTokenType = (String) args[1];\n\t\t\tboolean expectActivityLaunch = (boolean) args[2];\n\t\t\tMgr.editProperties(response, authTokenType, expectActivityLaunch);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class confirmCredentialsAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"confirmCredentialsAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount account = (Account) args[1];\n\t\t\tBundle options = (Bundle) args[2];\n\t\t\tboolean expectActivityLaunch = (boolean) args[3];\n\t\t\tMgr.confirmCredentials(response, account, options, expectActivityLaunch);\n\t\t\treturn 0;\n\n\t\t}\n\t}\n\n\tprivate static class accountAuthenticated extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"accountAuthenticated\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\treturn Mgr.accountAuthenticated(account);\n\t\t}\n\t}\n\n\tprivate static class getAuthTokenLabel extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAuthTokenLabel\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tString accountType = (String) args[1];\n\t\t\tString authTokenType = (String) args[2];\n\t\t\tMgr.getAuthTokenLabel(response, accountType, authTokenType);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class addSharedAccountAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"addSharedAccountAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tint userId = (int) args[1];\n\t\t\treturn method.invoke(who, args);\n\t\t}\n\t}\n\n\tprivate static class getSharedAccountsAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getSharedAccountsAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tint userId = (int) args[0];\n\t\t\treturn method.invoke(who, args);\n\t\t}\n\t}\n\n\tprivate static class removeSharedAccountAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"removeSharedAccountAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\tint userId = (int) args[1];\n\t\t\treturn method.invoke(who, args);\n\t\t}\n\t}\n\n\tprivate static class renameAccount extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"renameAccount\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tIAccountManagerResponse response = (IAccountManagerResponse) args[0];\n\t\t\tAccount accountToRename = (Account) args[1];\n\t\t\tString newName = (String) args[2];\n\t\t\tMgr.renameAccount(response, accountToRename, newName);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate static class getPreviousName extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getPreviousName\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount account = (Account) args[0];\n\t\t\treturn Mgr.getPreviousName(account);\n\t\t}\n\t}\n\n\tprivate static class renameSharedAccountAsUser extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"renameSharedAccountAsUser\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tAccount accountToRename = (Account) args[0];\n\t\t\tString newName = (String) args[1];\n\t\t\tint userId = (int) args[2];\n\t\t\treturn method.invoke(who, args);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/alarm/AlarmManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.alarm;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.WorkSource;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.helper.utils.ArrayUtils;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.app.IAlarmManager;\n\n/**\n * @author Lody\n *\n * @see android.app.AlarmManager\n */\npublic class AlarmManagerStub extends BinderInvocationProxy {\n\n\tpublic AlarmManagerStub() {\n\t\tsuper(IAlarmManager.Stub.asInterface, Context.ALARM_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new Set());\n\t\taddMethodProxy(new SetTime());\n\t\taddMethodProxy(new SetTimeZone());\n\t}\n\n\tprivate static class SetTimeZone extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"setTimeZone\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class SetTime extends MethodProxy {\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"setTime\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class Set extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"set\";\n        }\n\n        @Override\n        public boolean beforeCall(Object who, Method method, Object... args) {\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && args[0] instanceof String) {\n\t\t\t\targs[0] = getHostPkg();\n\t\t\t}\n            int index = ArrayUtils.indexOfFirst(args, WorkSource.class);\n            if (index >= 0) {\n                args[index] = null;\n            }\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.am;\n\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationStub;\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.LogInvocation;\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastUidMethodProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.helper.compat.BuildCompat;\nimport com.lody.virtual.helper.compat.ParceledListSliceCompat;\nimport com.lody.virtual.remote.AppTaskInfo;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport mirror.android.app.ActivityManagerNative;\nimport mirror.android.app.ActivityManagerOreo;\nimport mirror.android.app.IActivityManager;\nimport mirror.android.content.pm.ParceledListSlice;\nimport mirror.android.os.ServiceManager;\nimport mirror.android.util.Singleton;\n\n/**\n * @author Lody\n * @see IActivityManager\n * @see android.app.ActivityManager\n */\n\n// Hook 替换 gDefault\n@LogInvocation(LogInvocation.Condition.ALWAYS)\n@Inject(MethodProxies.class)\npublic class ActivityManagerStub extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {\n\n    public ActivityManagerStub() {\n        super(new MethodInvocationStub<>(ActivityManagerNative.getDefault.call()));\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        if (BuildCompat.isOreo()) {\n            //Android Oreo(8.X)\n            Object singleton = ActivityManagerOreo.IActivityManagerSingleton.get();\n            Singleton.mInstance.set(singleton, getInvocationStub().getProxyInterface());\n        } else {\n            if (ActivityManagerNative.gDefault.type() == IActivityManager.TYPE) {\n                ActivityManagerNative.gDefault.set(getInvocationStub().getProxyInterface());\n            } else if (ActivityManagerNative.gDefault.type() == Singleton.TYPE) {\n                Object gDefault = ActivityManagerNative.gDefault.get();\n                Singleton.mInstance.set(gDefault, getInvocationStub().getProxyInterface());\n            }\n        }\n        BinderInvocationStub hookAMBinder = new BinderInvocationStub(getInvocationStub().getBaseInterface());\n        hookAMBinder.copyMethodProxies(getInvocationStub());\n        ServiceManager.sCache.get().put(Context.ACTIVITY_SERVICE, hookAMBinder);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        if (VirtualCore.get().isVAppProcess()) {\n            addMethodProxy(new StaticMethodProxy(\"navigateUpTo\") {\n                @Override\n                public Object call(Object who, Method method, Object... args) throws Throwable {\n                    throw new RuntimeException(\"Call navigateUpTo!!!!\");\n                }\n            });\n            addMethodProxy(new ReplaceLastUidMethodProxy(\"checkPermissionWithToken\"));\n            addMethodProxy(new isUserRunning());\n            addMethodProxy(new ResultStaticMethodProxy(\"updateConfiguration\", 0));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"setAppLockedVerifying\"));\n            addMethodProxy(new StaticMethodProxy(\"checkUriPermission\") {\n                @Override\n                public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n                    return PackageManager.PERMISSION_GRANTED;\n                }\n            });\n            addMethodProxy(new StaticMethodProxy(\"getRecentTasks\") {\n                @Override\n                public Object call(Object who, Method method, Object... args) throws Throwable {\n                    Object _infos = method.invoke(who, args);\n                    //noinspection unchecked\n                    List<ActivityManager.RecentTaskInfo> infos =\n                            ParceledListSliceCompat.isReturnParceledListSlice(method)\n                                    ? ParceledListSlice.getList.call(_infos)\n                                    : (List) _infos;\n                    for (ActivityManager.RecentTaskInfo info : infos) {\n                        AppTaskInfo taskInfo = VActivityManager.get().getTaskInfo(info.id);\n                        if (taskInfo == null) {\n                            continue;\n                        }\n                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                            try {\n                                info.topActivity = taskInfo.topActivity;\n                                info.baseActivity = taskInfo.baseActivity;\n                            } catch (Throwable e) {\n                                // ignore\n                            }\n                        }\n                        try {\n                            info.origActivity = taskInfo.baseActivity;\n                            info.baseIntent = taskInfo.baseIntent;\n                        } catch (Throwable e) {\n                            // ignore\n                        }\n                    }\n                    return _infos;\n                }\n            });\n        }\n    }\n\n    @Override\n    public boolean isEnvBad() {\n        return ActivityManagerNative.getDefault.call() != getInvocationStub().getProxyInterface();\n    }\n\n    private class isUserRunning extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"isUserRunning\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) {\n            int userId = (int) args[0];\n            return userId == 0;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/am/HCallbackStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.am;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ServiceInfo;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Message;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.interfaces.IInjector;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.remote.StubActivityRecord;\n\nimport mirror.android.app.ActivityManagerNative;\nimport mirror.android.app.ActivityThread;\nimport mirror.android.app.IActivityManager;\n\n/**\n     * @author Lody\n     * @see Handler.Callback\n     */\n// 狸猫换太子，取出真正的 Intent\n    public class HCallbackStub implements Handler.Callback, IInjector {\n\n\n        private static final int LAUNCH_ACTIVITY = ActivityThread.H.LAUNCH_ACTIVITY.get();\n        private static final int CREATE_SERVICE = ActivityThread.H.CREATE_SERVICE.get();\n        private static final int SCHEDULE_CRASH =\n                ActivityThread.H.SCHEDULE_CRASH != null ? ActivityThread.H.SCHEDULE_CRASH.get() : -1;\n\n        private static final String TAG = HCallbackStub.class.getSimpleName();\n        private static final HCallbackStub sCallback = new HCallbackStub();\n\n        private boolean mCalling = false;\n\n\n        private Handler.Callback otherCallback;\n\n        private HCallbackStub() {\n        }\n\n        public static HCallbackStub getDefault() {\n            return sCallback;\n        }\n\n        private static Handler getH() {\n            return ActivityThread.mH.get(VirtualCore.mainThread());\n        }\n\n        private static Handler.Callback getHCallback() {\n            try {\n                Handler handler = getH();\n                return mirror.android.os.Handler.mCallback.get(handler);\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n            return null;\n        }\n\n        @Override\n        public boolean handleMessage(Message msg) {\n            if (!mCalling) {\n                mCalling = true;\n                try {\n                    if (LAUNCH_ACTIVITY == msg.what) {\n                        if (!handleLaunchActivity(msg)) {\n                            return true;\n                        }\n                    } else if (CREATE_SERVICE == msg.what) {\n                        if (!VClientImpl.get().isBound()) {\n                            ServiceInfo info = Reflect.on(msg.obj).get(\"info\");\n                            VClientImpl.get().bindApplication(info.packageName, info.processName);\n                        }\n                    } else if (SCHEDULE_CRASH == msg.what) {\n                        // to avoid the exception send from System.\n                        return true;\n                    }\n                    if (otherCallback != null) {\n                        boolean desired = otherCallback.handleMessage(msg);\n                        mCalling = false;\n                        return desired;\n                    } else {\n                        mCalling = false;\n                    }\n                } finally {\n                    mCalling = false;\n                }\n            }\n            return false;\n        }\n\n        private boolean handleLaunchActivity(Message msg) {\n            Object r = msg.obj;\n            Intent stubIntent = ActivityThread.ActivityClientRecord.intent.get(r);\n            // 获取原版 Intent 信息\n            StubActivityRecord saveInstance = new StubActivityRecord(stubIntent);\n            if (saveInstance.intent == null) {\n                return true;\n            }\n            // 原版 Intent\n            Intent intent = saveInstance.intent;\n            ComponentName caller = saveInstance.caller;\n            IBinder token = ActivityThread.ActivityClientRecord.token.get(r);\n            ActivityInfo info = saveInstance.info;\n\n            // 如果 token 还没初始化，代表 App 刚刚启动第一个组件\n            if (VClientImpl.get().getToken() == null) {\n                VActivityManager.get().processRestarted(info.packageName, info.processName, saveInstance.userId);\n                getH().sendMessageAtFrontOfQueue(Message.obtain(msg));\n                return false;\n            }\n            // AppBindData 为空，则 App 信息不明\n            if (!VClientImpl.get().isBound()) {\n                // 初始化并绑定 Application\n                VClientImpl.get().bindApplication(info.packageName, info.processName);\n                getH().sendMessageAtFrontOfQueue(Message.obtain(msg));\n                return false;\n            }\n\n            // 获取 TaskId\n            int taskId = IActivityManager.getTaskForActivity.call(\n                    ActivityManagerNative.getDefault.call(),\n                    token,\n                    false\n            );\n\n            // 1.将 ActivityRecorder 加入 mActivities 2.通知服务端 VAMS Activity 创建完成\n            VActivityManager.get().onActivityCreate(ComponentUtils.toComponentName(info), caller, token, info, intent, ComponentUtils.getTaskAffinity(info), taskId, info.launchMode, info.flags);\n            ClassLoader appClassLoader = VClientImpl.get().getClassLoader(info.applicationInfo);\n            intent.setExtrasClassLoader(appClassLoader);\n            // 将 Host Stub Activity Intent 替换为原版 Intent\n            ActivityThread.ActivityClientRecord.intent.set(r, intent);\n            // 同上\n            ActivityThread.ActivityClientRecord.activityInfo.set(r, info);\n            return true;\n        }\n\n        @Override\n        public void inject() throws Throwable {\n            otherCallback = getHCallback();\n            mirror.android.os.Handler.mCallback.set(getH(), this);\n        }\n\n        @Override\n        public boolean isEnvBad() {\n            Handler.Callback callback = getHCallback();\n            boolean envBad = callback != this;\n            if (callback != null && envBad) {\n                VLog.d(TAG, \"HCallback has bad, other callback = \" + callback);\n            }\n            return envBad;\n        }\n\n    }\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.am;\n\nimport android.annotation.TargetApi;\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.app.IServiceConnection;\nimport android.app.Notification;\nimport android.app.PendingIntent;\nimport android.app.Service;\nimport android.content.ComponentName;\nimport android.content.IIntentReceiver;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ComponentInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.RemoteException;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.util.TypedValue;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.Constants;\nimport com.lody.virtual.client.env.SpecialComponentList;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;\nimport com.lody.virtual.client.hook.providers.ProviderHook;\nimport com.lody.virtual.client.hook.secondary.ServiceConnectionDelegate;\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\nimport com.lody.virtual.client.ipc.ActivityClientRecord;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.client.ipc.VPackageManager;\nimport com.lody.virtual.client.stub.ChooserActivity;\nimport com.lody.virtual.client.stub.StubPendingActivity;\nimport com.lody.virtual.client.stub.StubPendingReceiver;\nimport com.lody.virtual.client.stub.StubPendingService;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.compat.ActivityManagerCompat;\nimport com.lody.virtual.helper.compat.BuildCompat;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.BitmapUtils;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.helper.utils.DrawableUtils;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.os.VUserInfo;\nimport com.lody.virtual.remote.AppTaskInfo;\nimport com.lody.virtual.server.interfaces.IAppRequestListener;\n\nimport java.io.File;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.WeakHashMap;\n\nimport mirror.android.app.IActivityManager;\nimport mirror.android.app.LoadedApk;\nimport mirror.android.content.ContentProviderHolderOreo;\nimport mirror.android.content.IIntentReceiverJB;\nimport mirror.android.content.pm.UserInfo;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"unused\")\nclass MethodProxies {\n\n    // hook 在子程序包中调用的 forceStopPackage，否则 VA 进程全部 GG\n    static class ForceStopPackage extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"forceStopPackage\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            int userId = VUserHandle.myUserId();\n            VActivityManager.get().killAppByPkg(pkg, userId);\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class CrashApplication extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"crashApplication\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class AddPackageDependency extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"addPackageDependency\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class GetPackageForToken extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPackageForToken\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            String pkg = VActivityManager.get().getPackageForToken(token);\n            if (pkg != null) {\n                return pkg;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class UnbindService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"unbindService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IServiceConnection conn = (IServiceConnection) args[0];\n            ServiceConnectionDelegate delegate = ServiceConnectionDelegate.removeDelegate(conn);\n            if (delegate == null) {\n                return method.invoke(who, args);\n            }\n            return VActivityManager.get().unbindService(delegate);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess() || isServerProcess();\n        }\n    }\n\n    static class GetContentProviderExternal extends GetContentProvider {\n\n        @Override\n        public String getMethodName() {\n            return \"getContentProviderExternal\";\n        }\n\n        @Override\n        public int getProviderNameIndex() {\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class StartVoiceActivity extends StartActivity {\n        @Override\n        public String getMethodName() {\n            return \"startVoiceActivity\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class UnstableProviderDied extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"unstableProviderDied\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (args[0] == null) {\n                return 0;\n            }\n            return method.invoke(who, args);\n        }\n    }\n\n\n    static class PeekService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"peekService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceLastAppPkg(args);\n            Intent service = (Intent) args[0];\n            String resolvedType = (String) args[1];\n            return VActivityManager.get().peekService(service, resolvedType);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPackageAskScreenCompat extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPackageAskScreenCompat\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                if (args.length > 0 && args[0] instanceof String) {\n                    args[0] = getHostPkg();\n                }\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetIntentSender extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getIntentSender\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String creator = (String) args[1];\n            String[] resolvedTypes = (String[]) args[6];\n            int type = (int) args[0];\n            int flags = (int) args[7];\n            if (args[5] instanceof Intent[]) {\n                Intent[] intents = (Intent[]) args[5];\n                if (intents.length > 0) {\n                    Intent intent = intents[intents.length - 1];\n                    if (resolvedTypes != null && resolvedTypes.length > 0) {\n                        intent.setDataAndType(intent.getData(), resolvedTypes[resolvedTypes.length - 1]);\n                    }\n                    Intent targetIntent = redirectIntentSender(type, creator, intent);\n                    if (targetIntent != null) {\n                        args[5] = new Intent[]{targetIntent};\n                    }\n                }\n            }\n            args[7] = flags;\n            args[1] = getHostPkg();\n            // Force userId to 0\n            if (args[args.length - 1] instanceof Integer) {\n                args[args.length - 1] = 0;\n            }\n            IInterface sender = (IInterface) method.invoke(who, args);\n            if (sender != null && creator != null) {\n                VActivityManager.get().addPendingIntent(sender.asBinder(), creator);\n            }\n            return sender;\n        }\n\n        private Intent redirectIntentSender(int type, String creator, Intent intent) {\n            Intent newIntent = intent.cloneFilter();\n            switch (type) {\n                case ActivityManagerCompat.INTENT_SENDER_ACTIVITY: {\n                    ComponentInfo info = VirtualCore.get().resolveActivityInfo(intent, VUserHandle.myUserId());\n                    if (info != null) {\n                        newIntent.setClass(getHostContext(), StubPendingActivity.class);\n                        newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                    }\n                }\n                break;\n                case ActivityManagerCompat.INTENT_SENDER_SERVICE: {\n                    ComponentInfo info = VirtualCore.get().resolveServiceInfo(intent, VUserHandle.myUserId());\n                    if (info != null) {\n                        newIntent.setClass(getHostContext(), StubPendingService.class);\n                    }\n                }\n                break;\n                case ActivityManagerCompat.INTENT_SENDER_BROADCAST: {\n                    newIntent.setClass(getHostContext(), StubPendingReceiver.class);\n                }\n                break;\n                default:\n                    return null;\n            }\n            newIntent.putExtra(\"_VA_|_user_id_\", VUserHandle.myUserId());\n            newIntent.putExtra(\"_VA_|_intent_\", intent);\n            newIntent.putExtra(\"_VA_|_creator_\", creator);\n            newIntent.putExtra(\"_VA_|_from_inner_\", true);\n            return newIntent;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n    }\n\n    // Hook startActivity\n    static class StartActivity extends MethodProxy {\n\n        private static final String SCHEME_FILE = \"file\";\n        private static final String SCHEME_PACKAGE = \"package\";\n\n        @Override\n        public String getMethodName() {\n            return \"startActivity\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int intentIndex = ArrayUtils.indexOfObject(args, Intent.class, 1);\n            if (intentIndex < 0) {\n                return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;\n            }\n            int resultToIndex = ArrayUtils.indexOfObject(args, IBinder.class, 2);\n            String resolvedType = (String) args[intentIndex + 1];\n            Intent intent = (Intent) args[intentIndex];\n            intent.setDataAndType(intent.getData(), resolvedType);\n            IBinder resultTo = resultToIndex >= 0 ? (IBinder) args[resultToIndex] : null;\n            int userId = VUserHandle.myUserId();\n\n            if (ComponentUtils.isStubComponent(intent)) {\n                return method.invoke(who, args);\n            }\n\n            // 请求安装和卸载界面\n            if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())\n                    || (Intent.ACTION_VIEW.equals(intent.getAction())\n                    && \"application/vnd.android.package-archive\".equals(intent.getType()))) {\n                if (handleInstallRequest(intent)) {\n                    return 0;\n                }\n            } else if ((Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())\n                    || Intent.ACTION_DELETE.equals(intent.getAction()))\n                    && \"package\".equals(intent.getScheme())) {\n\n                if (handleUninstallRequest(intent)) {\n                    return 0;\n                }\n            }\n\n            String resultWho = null;\n            int requestCode = 0;\n            Bundle options = ArrayUtils.getFirst(args, Bundle.class);\n            if (resultTo != null) {\n                resultWho = (String) args[resultToIndex + 1];\n                requestCode = (int) args[resultToIndex + 2];\n            }\n            // chooser 调用选择界面\n            if (ChooserActivity.check(intent)) {\n                intent.setComponent(new ComponentName(getHostContext(), ChooserActivity.class));\n                intent.putExtra(Constants.EXTRA_USER_HANDLE, userId);\n                intent.putExtra(ChooserActivity.EXTRA_DATA, options);\n                intent.putExtra(ChooserActivity.EXTRA_WHO, resultWho);\n                intent.putExtra(ChooserActivity.EXTRA_REQUEST_CODE, requestCode);\n                return method.invoke(who, args);\n            }\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n                args[intentIndex - 1] = getHostPkg();\n            }\n\n            //解析 ActivityInfo\n            ActivityInfo activityInfo = VirtualCore.get().resolveActivityInfo(intent, userId);\n            if (activityInfo == null) {\n                VLog.e(\"VActivityManager\", \"Unable to resolve activityInfo : \" + intent);\n                if (intent.getPackage() != null && isAppPkg(intent.getPackage())) {\n                    return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;\n                }\n                return method.invoke(who, args);\n            }\n\n            // 调用远程 VAMS.startActivity\n            int res = VActivityManager.get().startActivity(intent, activityInfo, resultTo, options, resultWho, requestCode, VUserHandle.myUserId());\n            if (res != 0 && resultTo != null && requestCode > 0) {\n                VActivityManager.get().sendActivityResult(resultTo, resultWho, requestCode);\n            }\n\n            // 处理 Activity 切换动画，因为此时动画还是 Host 的 Stub Activity 默认动画，需要覆盖成子程序包的动画\n            if (resultTo != null) {\n                ActivityClientRecord r = VActivityManager.get().getActivityRecord(resultTo);\n                if (r != null && r.activity != null) {\n                    try {\n                        TypedValue out = new TypedValue();\n                        Resources.Theme theme = r.activity.getResources().newTheme();\n                        theme.applyStyle(activityInfo.getThemeResource(), true);\n                        if (theme.resolveAttribute(android.R.attr.windowAnimationStyle, out, true)) {\n\n                            TypedArray array = theme.obtainStyledAttributes(out.data,\n                                    new int[]{\n                                            android.R.attr.activityOpenEnterAnimation,\n                                            android.R.attr.activityOpenExitAnimation\n                                    });\n\n                            r.activity.overridePendingTransition(array.getResourceId(0, 0), array.getResourceId(1, 0));\n                            array.recycle();\n                        }\n                    } catch (Throwable e) {\n                        // Ignore\n                    }\n                }\n            }\n            return res;\n        }\n\n\n        private boolean handleInstallRequest(Intent intent) {\n            IAppRequestListener listener = VirtualCore.get().getAppRequestListener();\n            if (listener != null) {\n                Uri packageUri = intent.getData();\n                if (SCHEME_FILE.equals(packageUri.getScheme())) {\n                    File sourceFile = new File(packageUri.getPath());\n                    try {\n                        listener.onRequestInstall(sourceFile.getPath());\n                        return true;\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n                }\n\n            }\n            return false;\n        }\n\n        private boolean handleUninstallRequest(Intent intent) {\n            IAppRequestListener listener = VirtualCore.get().getAppRequestListener();\n            if (listener != null) {\n                Uri packageUri = intent.getData();\n                if (SCHEME_PACKAGE.equals(packageUri.getScheme())) {\n                    String pkg = packageUri.getSchemeSpecificPart();\n                    try {\n                        listener.onRequestUninstall(pkg);\n                        return true;\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n                }\n\n            }\n            return false;\n        }\n\n    }\n\n    static class StartActivities extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"startActivities\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            Intent[] intents = ArrayUtils.getFirst(args, Intent[].class);\n            String[] resolvedTypes = ArrayUtils.getFirst(args, String[].class);\n            IBinder token = null;\n            int tokenIndex = ArrayUtils.indexOfObject(args, IBinder.class, 2);\n            if (tokenIndex != -1) {\n                token = (IBinder) args[tokenIndex];\n            }\n            Bundle options = ArrayUtils.getFirst(args, Bundle.class);\n            return VActivityManager.get().startActivities(intents, resolvedTypes, token, options, VUserHandle.myUserId());\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class FinishActivity extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"finishActivity\";\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            ActivityClientRecord r = VActivityManager.get().getActivityRecord(token);\n            boolean taskRemoved = VActivityManager.get().onActivityDestroy(token);\n            if (!taskRemoved && r != null && r.activity != null && r.info.getThemeResource() != 0) {\n                try {\n                    TypedValue out = new TypedValue();\n                    Resources.Theme theme = r.activity.getResources().newTheme();\n                    theme.applyStyle(r.info.getThemeResource(), true);\n                    if (theme.resolveAttribute(android.R.attr.windowAnimationStyle, out, true)) {\n\n                        TypedArray array = theme.obtainStyledAttributes(out.data,\n                                new int[]{\n                                        android.R.attr.activityCloseEnterAnimation,\n                                        android.R.attr.activityCloseExitAnimation\n                                });\n                        r.activity.overridePendingTransition(array.getResourceId(0, 0), array.getResourceId(1, 0));\n                        array.recycle();\n                    }\n                } catch (Throwable e) {\n                    e.printStackTrace();\n                }\n            }\n            return super.afterCall(who, method, args, result);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetCallingPackage extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getCallingPackage\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            return VActivityManager.get().getCallingPackage(token);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPackageForIntentSender extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"getPackageForIntentSender\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IInterface sender = (IInterface) args[0];\n            if (sender != null) {\n                String packageName = VActivityManager.get().getPackageForIntentSender(sender.asBinder());\n                if (packageName != null) {\n                    return packageName;\n                }\n            }\n            return super.call(who, method, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    static class PublishContentProviders extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"publishContentProviders\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetServices extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"getServices\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int maxNum = (int) args[0];\n            int flags = (int) args[1];\n            return VActivityManager.get().getServices(maxNum, flags).getList();\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class GrantUriPermissionFromOwner extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"grantUriPermissionFromOwner\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class SetServiceForeground extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"setServiceForeground\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName component = (ComponentName) args[0];\n            IBinder token = (IBinder) args[1];\n            int id = (int) args[2];\n            Notification notification = (Notification) args[3];\n            boolean removeNotification = false;\n            if (args[4] instanceof Boolean) {\n                removeNotification = (boolean) args[4];\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && args[4] instanceof Integer) {\n                int flags = (int) args[4];\n                removeNotification = (flags & Service.STOP_FOREGROUND_REMOVE) != 0;\n            } else {\n                VLog.e(getClass().getSimpleName(), \"Unknown flag : \" + args[4]);\n            }\n            VActivityManager.get().setServiceForeground(component, token, id, notification, removeNotification);\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class UpdateDeviceOwner extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"updateDeviceOwner\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n    }\n\n\n    static class GetIntentForIntentSender extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getIntentForIntentSender\";\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            Intent intent = (Intent) super.afterCall(who, method, args, result);\n            if (intent != null && intent.hasExtra(\"_VA_|_intent_\")) {\n                return intent.getParcelableExtra(\"_VA_|_intent_\");\n            }\n            return intent;\n        }\n    }\n\n\n    static class UnbindFinished extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"unbindFinished\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            Intent service = (Intent) args[1];\n            boolean doRebind = (boolean) args[2];\n            VActivityManager.get().unbindFinished(token, service, doRebind);\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class StartActivityIntentSender extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"startActivityIntentSender\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class BindService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"bindService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IInterface caller = (IInterface) args[0];\n            IBinder token = (IBinder) args[1];\n            Intent service = (Intent) args[2];\n            String resolvedType = (String) args[3];\n            IServiceConnection conn = (IServiceConnection) args[4];\n            int flags = (int) args[5];\n            int userId = VUserHandle.myUserId();\n            if (isServerProcess()) {\n                userId = service.getIntExtra(\"_VA_|_user_id_\", VUserHandle.USER_NULL);\n            }\n            if (userId == VUserHandle.USER_NULL) {\n                return method.invoke(who, args);\n            }\n            ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, userId);\n            if (serviceInfo != null) {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                    service.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));\n                }\n                conn = ServiceConnectionDelegate.getDelegate(conn);\n                return VActivityManager.get().bindService(caller.asBinder(), token, service, resolvedType,\n                        conn, flags, userId);\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess() || isServerProcess();\n        }\n    }\n\n\n    static class StartService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"startService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IInterface appThread = (IInterface) args[0];\n            Intent service = (Intent) args[1];\n            String resolvedType = (String) args[2];\n            if (service.getComponent() != null\n                    && getHostPkg().equals(service.getComponent().getPackageName())) {\n                // for server process\n                return method.invoke(who, args);\n            }\n            int userId = VUserHandle.myUserId();\n            // 如果是内部请求,获取原来的 Service\n            if (service.getBooleanExtra(\"_VA_|_from_inner_\", false)) {\n                userId = service.getIntExtra(\"_VA_|_user_id_\", userId);\n                service = service.getParcelableExtra(\"_VA_|_intent_\");\n            } else {\n                if (isServerProcess()) {\n                    userId = service.getIntExtra(\"_VA_|_user_id_\", VUserHandle.USER_NULL);\n                }\n            }\n            service.setDataAndType(service.getData(), resolvedType);\n            ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, VUserHandle.myUserId());\n\n            if (serviceInfo != null) {\n                // 远程调用 VAMS.startService()\n                return VActivityManager.get().startService(appThread, service, resolvedType, userId);\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess() || isServerProcess();\n        }\n    }\n\n    static class StartActivityAndWait extends StartActivity {\n        @Override\n        public String getMethodName() {\n            return \"startActivityAndWait\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class PublishService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"publishService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            if (!VActivityManager.get().isVAServiceToken(token)) {\n                return method.invoke(who, args);\n            }\n            Intent intent = (Intent) args[1];\n            IBinder service = (IBinder) args[2];\n            VActivityManager.get().publishService(token, intent, service);\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    static class GetRunningAppProcesses extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getRunningAppProcesses\";\n        }\n\n        @Override\n        public synchronized Object call(Object who, Method method, Object... args) throws Throwable {\n            List<ActivityManager.RunningAppProcessInfo> infoList = (List<ActivityManager.RunningAppProcessInfo>) method\n                    .invoke(who, args);\n            if (infoList != null) {\n                for (ActivityManager.RunningAppProcessInfo info : infoList) {\n                    if (VActivityManager.get().isAppPid(info.pid)) {\n                        List<String> pkgList = VActivityManager.get().getProcessPkgList(info.pid);\n                        String processName = VActivityManager.get().getAppProcessName(info.pid);\n                        if (processName != null) {\n                            info.processName = processName;\n                        }\n                        info.pkgList = pkgList.toArray(new String[pkgList.size()]);\n                        info.uid = VUserHandle.getAppId(VActivityManager.get().getUidByPid(info.pid));\n                    }\n                }\n            }\n            return infoList;\n        }\n    }\n\n\n    static class SetPackageAskScreenCompat extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"setPackageAskScreenCompat\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                if (args.length > 0 && args[0] instanceof String) {\n                    args[0] = getHostPkg();\n                }\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetCallingActivity extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getCallingActivity\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            return VActivityManager.get().getCallingActivity(token);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetCurrentUser extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getCurrentUser\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            try {\n                return UserInfo.ctor.newInstance(0, \"user\", VUserInfo.FLAG_PRIMARY);\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n            return null;\n        }\n    }\n\n\n    static class KillApplicationProcess extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"killApplicationProcess\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (args.length > 1 && args[0] instanceof String && args[1] instanceof Integer) {\n                String processName = (String) args[0];\n                int uid = (int) args[1];\n                VActivityManager.get().killApplicationProcess(processName, uid);\n                return 0;\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class StartActivityAsUser extends StartActivity {\n\n        @Override\n        public String getMethodName() {\n            return \"startActivityAsUser\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class CheckPermission extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"checkPermission\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String permission = (String) args[0];\n            if (SpecialComponentList.isWhitePermission(permission)) {\n                return PackageManager.PERMISSION_GRANTED;\n            }\n            if (permission.startsWith(\"com.google\")) {\n                return PackageManager.PERMISSION_GRANTED;\n            }\n            args[args.length - 1] = getRealUid();\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n    }\n\n\n    static class StartActivityAsCaller extends StartActivity {\n\n        @Override\n        public String getMethodName() {\n            return \"startActivityAsCaller\";\n        }\n    }\n\n\n    static class HandleIncomingUser extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"handleIncomingUser\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int lastIndex = args.length - 1;\n            if (args[lastIndex] instanceof String) {\n                args[lastIndex] = getHostPkg();\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    static class GetTasks extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getTasks\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            List<ActivityManager.RunningTaskInfo> runningTaskInfos = (List<ActivityManager.RunningTaskInfo>) method\n                    .invoke(who, args);\n            for (ActivityManager.RunningTaskInfo info : runningTaskInfos) {\n                AppTaskInfo taskInfo = VActivityManager.get().getTaskInfo(info.id);\n                if (taskInfo != null) {\n                    info.topActivity = taskInfo.topActivity;\n                    info.baseActivity = taskInfo.baseActivity;\n                }\n            }\n            return runningTaskInfos;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPersistedUriPermissions extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPersistedUriPermissions\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class RegisterReceiver extends MethodProxy {\n        private static final int IDX_IIntentReceiver = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1\n                ? 2\n                : 1;\n\n        private static final int IDX_RequiredPermission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1\n                ? 4\n                : 3;\n        private static final int IDX_IntentFilter = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1\n                ? 3\n                : 2;\n\n        private WeakHashMap<IBinder, IIntentReceiver> mProxyIIntentReceivers = new WeakHashMap<>();\n\n        @Override\n        public String getMethodName() {\n            return \"registerReceiver\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            args[IDX_RequiredPermission] = null;\n            IntentFilter filter = (IntentFilter) args[IDX_IntentFilter];\n            SpecialComponentList.protectIntentFilter(filter);\n            if (args.length > IDX_IIntentReceiver && IIntentReceiver.class.isInstance(args[IDX_IIntentReceiver])) {\n                final IInterface old = (IInterface) args[IDX_IIntentReceiver];\n                if (!IIntentReceiverProxy.class.isInstance(old)) {\n                    final IBinder token = old.asBinder();\n                    if (token != null) {\n                        token.linkToDeath(new IBinder.DeathRecipient() {\n                            @Override\n                            public void binderDied() {\n                                token.unlinkToDeath(this, 0);\n                                mProxyIIntentReceivers.remove(token);\n                            }\n                        }, 0);\n                        IIntentReceiver proxyIIntentReceiver = mProxyIIntentReceivers.get(token);\n                        if (proxyIIntentReceiver == null) {\n                            proxyIIntentReceiver = new IIntentReceiverProxy(old);\n                            mProxyIIntentReceivers.put(token, proxyIIntentReceiver);\n                        }\n                        WeakReference mDispatcher = LoadedApk.ReceiverDispatcher.InnerReceiver.mDispatcher.get(old);\n                        if (mDispatcher != null) {\n                            LoadedApk.ReceiverDispatcher.mIIntentReceiver.set(mDispatcher.get(), proxyIIntentReceiver);\n                            args[IDX_IIntentReceiver] = proxyIIntentReceiver;\n                        }\n                    }\n                }\n            }\n            return method.invoke(who, args);\n        }\n\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n        private static class IIntentReceiverProxy extends IIntentReceiver.Stub {\n\n            IInterface mOld;\n\n            IIntentReceiverProxy(IInterface old) {\n                this.mOld = old;\n            }\n\n            public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered,\n                                       boolean sticky, int sendingUser) throws RemoteException {\n                if (!accept(intent)) {\n                    return;\n                }\n                if (intent.hasExtra(\"_VA_|_intent_\")) {\n                    intent = intent.getParcelableExtra(\"_VA_|_intent_\");\n                }\n                SpecialComponentList.unprotectIntent(intent);\n                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {\n                    IIntentReceiverJB.performReceive.call(mOld, intent, resultCode, data, extras, ordered, sticky, sendingUser);\n                } else {\n                    mirror.android.content.IIntentReceiver.performReceive.call(mOld, intent, resultCode, data, extras, ordered, sticky);\n                }\n            }\n\n            private boolean accept(Intent intent) {\n                int uid = intent.getIntExtra(\"_VA_|_uid_\", -1);\n                if (uid != -1) {\n                    return VClientImpl.get().getVUid() == uid;\n                }\n                int userId = intent.getIntExtra(\"_VA_|_user_id_\", -1);\n                return userId == -1 || userId == VUserHandle.myUserId();\n            }\n\n            @SuppressWarnings(\"unused\")\n            public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered,\n                                       boolean sticky) throws RemoteException {\n                this.performReceive(intent, resultCode, data, extras, ordered, sticky, 0);\n            }\n\n        }\n    }\n\n\n    static class StopService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"stopService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IInterface caller = (IInterface) args[0];\n            Intent intent = (Intent) args[1];\n            String resolvedType = (String) args[2];\n            intent.setDataAndType(intent.getData(), resolvedType);\n            ComponentName componentName = intent.getComponent();\n            PackageManager pm = VirtualCore.getPM();\n            if (componentName == null) {\n                ResolveInfo resolveInfo = pm.resolveService(intent, 0);\n                if (resolveInfo != null && resolveInfo.serviceInfo != null) {\n                    componentName = new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);\n                }\n            }\n            if (componentName != null && !getHostPkg().equals(componentName.getPackageName())) {\n                return VActivityManager.get().stopService(caller, intent, resolvedType);\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess() || isServerProcess();\n        }\n    }\n\n\n    static class GetContentProvider extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getContentProvider\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int nameIdx = getProviderNameIndex();\n            String name = (String) args[nameIdx];\n            int userId = VUserHandle.myUserId();\n            // 远程调用 VPMS 从 VPackage 拿到 ProviderInfo\n            ProviderInfo info = VPackageManager.get().resolveContentProvider(name, 0, userId);\n            // 拿不到说明目标 App 未启动\n            if (info != null && info.enabled && isAppPkg(info.packageName)) {\n                // 远程调用 VAMS，然后 VAMS 再通过 AMS 远程调用注册在插件进程的 StubContentProvider.call 初始化插件进程\n                int targetVPid = VActivityManager.get().initProcess(info.packageName, info.processName, userId);\n                if (targetVPid == -1) {\n                    return null;\n                }\n                args[nameIdx] = VASettings.getStubAuthority(targetVPid);\n                Object holder = method.invoke(who, args);\n                if (holder == null) {\n                    return null;\n                }\n                // ContentProviderHolder 有两个成员变量provider、connection，provider 是目标 Provider 的 IBinder 句柄o\n                // connection 则是 callback\n                if (BuildCompat.isOreo()) {\n                    IInterface provider = ContentProviderHolderOreo.provider.get(holder);\n                    if (provider != null) {\n                        // 这里是重点，远程调用了 VAMS 的 acquireProviderClient\n                        provider = VActivityManager.get().acquireProviderClient(userId, info);\n                    }\n                    ContentProviderHolderOreo.provider.set(holder, provider);\n                    ContentProviderHolderOreo.info.set(holder, info);\n                } else {\n                    IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);\n                    if (provider != null) {\n                        provider = VActivityManager.get().acquireProviderClient(userId, info);\n                    }\n                    IActivityManager.ContentProviderHolder.provider.set(holder, provider);\n                    IActivityManager.ContentProviderHolder.info.set(holder, info);\n                }\n                return holder;\n            }\n            Object holder = method.invoke(who, args);\n            if (holder != null) {\n                if (BuildCompat.isOreo()) {\n                    IInterface provider = ContentProviderHolderOreo.provider.get(holder);\n                    info = ContentProviderHolderOreo.info.get(holder);\n                    if (provider != null) {\n                        provider = ProviderHook.createProxy(true, info.authority, provider);\n                    }\n                    ContentProviderHolderOreo.provider.set(holder, provider);\n                } else {\n                    IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);\n                    info = IActivityManager.ContentProviderHolder.info.get(holder);\n                    if (provider != null) {\n                        provider = ProviderHook.createProxy(true, info.authority, provider);\n                    }\n                    IActivityManager.ContentProviderHolder.provider.set(holder, provider);\n                }\n                return holder;\n            }\n            return null;\n        }\n\n\n        public int getProviderNameIndex() {\n            return 1;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    static class SetTaskDescription extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"setTaskDescription\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ActivityManager.TaskDescription td = (ActivityManager.TaskDescription) args[1];\n            String label = td.getLabel();\n            Bitmap icon = td.getIcon();\n\n            // If the activity label/icon isn't specified, the application's label/icon is shown instead\n            // Android usually does that for us, but in this case we want info about the contained app, not VIrtualApp itself\n            if (label == null || icon == null) {\n                Application app = VClientImpl.get().getCurrentApplication();\n                if (app != null) {\n                    try {\n                        if (label == null) {\n                            label = app.getApplicationInfo().loadLabel(app.getPackageManager()).toString();\n                        }\n                        if (icon == null) {\n                            Drawable drawable = app.getApplicationInfo().loadIcon(app.getPackageManager());\n                            if (drawable != null) {\n                                icon = DrawableUtils.drawableToBitMap(drawable);\n                            }\n                        }\n                        td = new ActivityManager.TaskDescription(label, icon, td.getPrimaryColor());\n                    } catch (Throwable e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n\n            TaskDescriptionDelegate descriptionDelegate = VirtualCore.get().getTaskDescriptionDelegate();\n            if (descriptionDelegate != null) {\n                td = descriptionDelegate.getTaskDescription(td);\n            }\n\n            args[1] = td;\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class StopServiceToken extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"stopServiceToken\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName componentName = (ComponentName) args[0];\n            IBinder token = (IBinder) args[1];\n            if (!VActivityManager.get().isVAServiceToken(token)) {\n                return method.invoke(who, args);\n            }\n            int startId = (int) args[2];\n            if (componentName != null) {\n                return VActivityManager.get().stopServiceToken(componentName, token, startId);\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess() || isServerProcess();\n        }\n    }\n\n    static class StartActivityWithConfig extends StartActivity {\n        @Override\n        public String getMethodName() {\n            return \"startActivityWithConfig\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return super.call(who, method, args);\n        }\n    }\n\n    static class StartNextMatchingActivity extends StartActivity {\n        @Override\n        public String getMethodName() {\n            return \"startNextMatchingActivity\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return false;\n        }\n    }\n\n\n    static class BroadcastIntent extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"broadcastIntent\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            Intent intent = (Intent) args[1];\n            String type = (String) args[2];\n            intent.setDataAndType(intent.getData(), type);\n            if (VirtualCore.get().getComponentDelegate() != null) {\n                VirtualCore.get().getComponentDelegate().onSendBroadcast(intent);\n            }\n            Intent newIntent = handleIntent(intent);\n            if (newIntent != null) {\n                args[1] = newIntent;\n            } else {\n                return 0;\n            }\n\n            if (args[7] instanceof String || args[7] instanceof String[]) {\n                // clear the permission\n                args[7] = null;\n            }\n            return method.invoke(who, args);\n        }\n\n\n        private Intent handleIntent(final Intent intent) {\n            final String action = intent.getAction();\n            if (\"android.intent.action.CREATE_SHORTCUT\".equals(action)\n                    || \"com.android.launcher.action.INSTALL_SHORTCUT\".equals(action)) {\n\n                return VASettings.ENABLE_INNER_SHORTCUT ? handleInstallShortcutIntent(intent) : null;\n\n            } else if (\"com.android.launcher.action.UNINSTALL_SHORTCUT\".equals(action)) {\n\n                handleUninstallShortcutIntent(intent);\n\n            } else {\n                return ComponentUtils.redirectBroadcastIntent(intent, VUserHandle.myUserId());\n            }\n            return intent;\n        }\n\n        private Intent handleInstallShortcutIntent(Intent intent) {\n            Intent shortcut = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);\n            if (shortcut != null) {\n                ComponentName component = shortcut.resolveActivity(VirtualCore.getPM());\n                if (component != null) {\n                    String pkg = component.getPackageName();\n                    Intent newShortcutIntent = new Intent();\n                    newShortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);\n                    newShortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);\n                    newShortcutIntent.putExtra(\"_VA_|_intent_\", shortcut);\n                    newShortcutIntent.putExtra(\"_VA_|_uri_\", shortcut.toUri(0));\n                    newShortcutIntent.putExtra(\"_VA_|_user_id_\", VUserHandle.myUserId());\n                    intent.removeExtra(Intent.EXTRA_SHORTCUT_INTENT);\n                    intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, newShortcutIntent);\n\n                    Intent.ShortcutIconResource icon = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);\n                    if (icon != null && !TextUtils.equals(icon.packageName, getHostPkg())) {\n                        try {\n                            Resources resources = VirtualCore.get().getResources(pkg);\n                            int resId = resources.getIdentifier(icon.resourceName, \"drawable\", pkg);\n                            if (resId > 0) {\n                                //noinspection deprecation\n                                Drawable iconDrawable = resources.getDrawable(resId);\n                                Bitmap newIcon = BitmapUtils.drawableToBitmap(iconDrawable);\n                                if (newIcon != null) {\n                                    intent.removeExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);\n                                    intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, newIcon);\n                                }\n                            }\n                        } catch (Throwable e) {\n                            e.printStackTrace();\n                        }\n                    }\n                }\n            }\n            return intent;\n        }\n\n        private void handleUninstallShortcutIntent(Intent intent) {\n            Intent shortcut = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);\n            if (shortcut != null) {\n                ComponentName componentName = shortcut.resolveActivity(getPM());\n                if (componentName != null) {\n                    Intent newShortcutIntent = new Intent();\n                    newShortcutIntent.putExtra(\"_VA_|_uri_\", shortcut.toUri(0));\n                    newShortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);\n                    newShortcutIntent.removeExtra(Intent.EXTRA_SHORTCUT_INTENT);\n                    intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, newShortcutIntent);\n                }\n            }\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetActivityClassForToken extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getActivityClassForToken\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            return VActivityManager.get().getActivityForToken(token);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class CheckGrantUriPermission extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"checkGrantUriPermission\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class ServiceDoneExecuting extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"serviceDoneExecuting\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            IBinder token = (IBinder) args[0];\n            if (!VActivityManager.get().isVAServiceToken(token)) {\n                return method.invoke(who, args);\n            }\n            int type = (int) args[1];\n            int startId = (int) args[2];\n            int res = (int) args[3];\n            VActivityManager.get().serviceDoneExecuting(token, type, startId, res);\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/appops/AppOpsManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.appops;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\n\nimport java.lang.reflect.Method;\n\nimport mirror.com.android.internal.app.IAppOpsService;\n\n/**\n * @author Lody\n *         <p>\n *         Fuck the AppOpsService.\n * @see android.app.AppOpsManager\n */\n@TargetApi(Build.VERSION_CODES.KITKAT)\npublic class AppOpsManagerStub extends BinderInvocationProxy {\n\n    public AppOpsManagerStub() {\n        super(IAppOpsService.Stub.asInterface, Context.APP_OPS_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new BaseMethodProxy(\"checkOperation\", 1, 2));\n        addMethodProxy(new BaseMethodProxy(\"noteOperation\", 1, 2));\n        addMethodProxy(new BaseMethodProxy(\"startOperation\", 2, 3));\n        addMethodProxy(new BaseMethodProxy(\"finishOperation\", 2, 3));\n        addMethodProxy(new BaseMethodProxy(\"startWatchingMode\", -1, 1));\n        addMethodProxy(new BaseMethodProxy(\"checkPackage\", 0, 1));\n        addMethodProxy(new BaseMethodProxy(\"getOpsForPackage\", 0, 1));\n        addMethodProxy(new BaseMethodProxy(\"setMode\", 1, 2));\n        addMethodProxy(new BaseMethodProxy(\"checkAudioOperation\", 2, 3));\n        addMethodProxy(new BaseMethodProxy(\"setAudioRestriction\", 2, -1));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"resetAllModes\"));\n        addMethodProxy(new MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"noteProxyOperation\";\n            }\n\n            @Override\n            public Object call(Object who, Method method, Object... args) throws Throwable {\n                return 0;\n            }\n        });\n    }\n\n    private class BaseMethodProxy extends StaticMethodProxy {\n        final int pkgIndex;\n        final int uidIndex;\n\n        BaseMethodProxy(String name, int uidIndex, int pkgIndex) {\n            super(name);\n            this.pkgIndex = pkgIndex;\n            this.uidIndex = uidIndex;\n        }\n\n        @Override\n        public boolean beforeCall(Object who, Method method, Object... args) {\n            if (pkgIndex != -1 && args.length > pkgIndex && args[pkgIndex] instanceof String) {\n                args[pkgIndex] = getHostPkg();\n            }\n            if (uidIndex != -1 && args[uidIndex] instanceof Integer) {\n                args[uidIndex] = getRealUid();\n            }\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/appwidget/AppWidgetManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.appwidget;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport mirror.com.android.internal.appwidget.IAppWidgetService;\n\n/**\n * @author Lody\n *\n * @see android.appwidget.AppWidgetManager\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class AppWidgetManagerStub extends BinderInvocationProxy {\n\n\tpublic AppWidgetManagerStub() {\n\t\tsuper(IAppWidgetService.Stub.asInterface, Context.APPWIDGET_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"startListening\", new int[0]));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"stopListening\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"allocateAppWidgetId\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"deleteAppWidgetId\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"deleteHost\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"deleteAllHosts\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getAppWidgetViews\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getAppWidgetIdsForHost\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"createAppWidgetConfigIntentSender\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"updateAppWidgetIds\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"updateAppWidgetOptions\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getAppWidgetOptions\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"partiallyUpdateAppWidgetIds\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"updateAppWidgetProvider\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"notifyAppWidgetViewDataChanged\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getInstalledProvidersForProfile\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getAppWidgetInfo\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"hasBindAppWidgetPermission\", false));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"setBindAppWidgetPermission\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"bindAppWidgetId\", false));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"bindRemoteViewsService\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"unbindRemoteViewsService\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getAppWidgetIds\", new int[0]));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"isBoundWidgetPackage\", false));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/audio/AudioManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.audio;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\n\nimport mirror.android.media.IAudioService;\n\n/**\n * @author Lody\n *\n * @see android.media.AudioManager\n */\n\npublic class AudioManagerStub extends BinderInvocationProxy {\n\tpublic AudioManagerStub() {\n\t\tsuper(IAudioService.Stub.asInterface, Context.AUDIO_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"adjustVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"adjustLocalOrRemoteStreamVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"adjustSuggestedStreamVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"adjustStreamVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"adjustMasterVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setStreamVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setMasterVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setMicrophoneMute\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setRingerModeExternal\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setRingerModeInternal\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setMode\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"avrcpSupportsAbsoluteVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"abandonAudioFocus\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"requestAudioFocus\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setWiredDeviceConnectionState\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setSpeakerphoneOn\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setBluetoothScoOn\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"stopBluetoothSco\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"startBluetoothSco\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"disableSafeMediaVolume\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"registerRemoteControlClient\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"unregisterAudioFocusClient\"));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/backup/BackupManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.backup;\n\nimport android.app.backup.BackupManager;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport mirror.android.app.backup.IBackupManager;\n\n/**\n * @author Lody\n *\n * @see BackupManager\n */\npublic class BackupManagerStub extends BinderInvocationProxy {\n\tpublic BackupManagerStub() {\n\t\tsuper(IBackupManager.Stub.asInterface, \"backup\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"dataChanged\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"clearBackupData\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"agentConnected\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"agentDisconnected\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"restoreAtInstall\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"setBackupEnabled\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"setBackupProvisioned\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"backupNow\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"fullBackup\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"fullTransportBackup\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"fullRestore\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"acknowledgeFullBackupOrRestore\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getCurrentTransport\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"listAllTransports\", new String[0]));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"selectBackupTransport\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"isBackupEnabled\", false));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"setBackupPassword\", true));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"hasBackupPassword\", false));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"beginRestoreSession\", null));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/bluetooth/BluetoothStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.bluetooth;\n\nimport android.os.Build;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\nimport com.lody.virtual.helper.utils.Mark;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.bluetooth.IBluetooth;\n\n/**\n * @see android.bluetooth.BluetoothManager\n */\npublic class BluetoothStub extends BinderInvocationProxy {\n    public static final String SERVICE_NAME = Build.VERSION.SDK_INT >= 17 ?\n            \"bluetooth_manager\" :\n            \"bluetooth\";\n\n    public BluetoothStub() {\n        super(IBluetooth.Stub.asInterface, SERVICE_NAME);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new GetAddress());\n    }\n\n    @Mark(\"fake MAC\")\n    private static class GetAddress extends StaticMethodProxy {\n\n        GetAddress() {\n            super(\"getAddress\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return getDeviceInfo().bluetoothMac;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/clipboard/ClipBoardStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.clipboard;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\nimport com.lody.virtual.helper.compat.BuildCompat;\n\nimport mirror.android.content.ClipboardManager;\nimport mirror.android.content.ClipboardManagerOreo;\n\n/**\n * @author Lody\n * @see ClipboardManager\n */\npublic class ClipBoardStub extends BinderInvocationProxy {\n\n    public ClipBoardStub() {\n        super(getInterface(), Context.CLIPBOARD_SERVICE);\n    }\n\n    private static IInterface getInterface() {\n        if (BuildCompat.isOreo()) {\n            android.content.ClipboardManager cm = (android.content.ClipboardManager)\n                    VirtualCore.get().getContext().getSystemService(Context.CLIPBOARD_SERVICE);\n            return ClipboardManagerOreo.mService.get(cm);\n        } else {\n            return ClipboardManager.getService.call();\n        }\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getPrimaryClip\"));\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"setPrimaryClip\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"getPrimaryClipDescription\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"hasPrimaryClip\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"addPrimaryClipChangedListener\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"removePrimaryClipChangedListener\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"hasClipboardText\"));\n        }\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        super.inject();\n        if (BuildCompat.isOreo()) {\n            android.content.ClipboardManager cm = (android.content.ClipboardManager)\n                    VirtualCore.get().getContext().getSystemService(Context.CLIPBOARD_SERVICE);\n            ClipboardManagerOreo.mService.set(cm, getInvocationStub().getProxyInterface());\n        } else {\n            ClipboardManager.sService.set(getInvocationStub().getProxyInterface());\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/connectivity/ConnectivityStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.connectivity;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\nimport com.lody.virtual.client.ipc.ServiceManagerNative;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.net.IConnectivityManager;\n\n/**\n * @author legency\n */\npublic class ConnectivityStub extends BinderInvocationProxy {\n\n    public ConnectivityStub() {\n        super(IConnectivityManager.Stub.asInterface, Context.CONNECTIVITY_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/content/ContentServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.content;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\n\nimport mirror.android.content.IContentService;\n\n/**\n * @author Lody\n *\n * @see IContentService\n */\n\npublic class ContentServiceStub extends BinderInvocationProxy {\n\n    public ContentServiceStub() {\n        super(IContentService.Stub.asInterface, \"content\");\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/context_hub/ContextHubServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.context_hub;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport mirror.android.hardware.location.IContextHubService;\n\npublic class ContextHubServiceStub extends BinderInvocationProxy {\n\n    public ContextHubServiceStub() {\n        super(IContextHubService.Stub.asInterface, \"contexthub_service\");\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ResultStaticMethodProxy(\"registerCallback\", 0));\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/display/DisplayStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.display;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.hardware.display.DisplayManagerGlobal;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class DisplayStub extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {\n\tpublic DisplayStub() {\n\t\tsuper(new MethodInvocationStub<IInterface>(\n\t\t\t\tDisplayManagerGlobal.mDm.get(DisplayManagerGlobal.getInstance.call())));\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"createVirtualDisplay\"));\n\t}\n\n\t@Override\n\tpublic void inject() throws Throwable {\n\t\tObject dmg = DisplayManagerGlobal.getInstance.call();\n\t\tDisplayManagerGlobal.mDm.set(dmg, getInvocationStub().getProxyInterface());\n\t}\n\n\t@Override\n\tpublic boolean isEnvBad() {\n\t\tObject dmg = DisplayManagerGlobal.getInstance.call();\n\t\tIInterface mDm = DisplayManagerGlobal.mDm.get(dmg);\n\t\treturn mDm != getInvocationStub().getProxyInterface();\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/dropbox/DropBoxManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.dropbox;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport mirror.com.android.internal.os.IDropBoxManagerService;\n\n/**\n * @author Lody\n */\npublic class DropBoxManagerStub extends BinderInvocationProxy {\n\tpublic DropBoxManagerStub() {\n\t\tsuper(IDropBoxManagerService.Stub.asInterface, Context.DROPBOX_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getNextEntry\", null));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/graphics/GraphicsStatsStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.graphics;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.view.IGraphicsStats;\n\n\n/**\n * @author Lody\n */\npublic class GraphicsStatsStub extends BinderInvocationProxy {\n\n\tpublic GraphicsStatsStub() {\n\t\tsuper(IGraphicsStats.Stub.asInterface, \"graphicsstats\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"requestBufferForProcess\"));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/imms/MmsStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.imms;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceSpecPkgMethodProxy;\n\nimport mirror.com.android.internal.telephony.IMms;\n\n\n/**\n * @author Lody\n */\npublic class MmsStub extends BinderInvocationProxy {\n\n\tpublic MmsStub() {\n\t\tsuper(IMms.Stub.asInterface, \"imms\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\taddMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendMessage\", 1));\n\t\taddMethodProxy(new ReplaceSpecPkgMethodProxy(\"downloadMessage\", 1));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"importTextMessage\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"importMultimediaMessage\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"deleteStoredMessage\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"deleteStoredConversation\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"updateStoredMessageStatus\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"archiveStoredConversation\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"addTextMessageDraft\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"addMultimediaMessageDraft\"));\n\t\taddMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendStoredMessage\", 1));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"setAutoPersisting\"));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/input/InputMethodManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.input;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.Inject;\n\nimport mirror.com.android.internal.view.inputmethod.InputMethodManager;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n\npublic class InputMethodManagerStub extends BinderInvocationProxy {\n\n\tpublic InputMethodManagerStub() {\n\t\tsuper(\n\t\t\t\tInputMethodManager.mService.get(\n\t\t\t\t\t\tVirtualCore.get().getContext().getSystemService(Context.INPUT_METHOD_SERVICE)),\n\t\t\t\tContext.INPUT_METHOD_SERVICE);\n\t}\n\n\t@Override\n\tpublic void inject() throws Throwable {\n\t\tObject inputMethodManager = getContext().getSystemService(Context.INPUT_METHOD_SERVICE);\n\t\tInputMethodManager.mService.set(inputMethodManager, getInvocationStub().getProxyInterface());\n\t\tgetInvocationStub().replaceService(Context.INPUT_METHOD_SERVICE);\n\t}\n\n\n\t@Override\n\tpublic boolean isEnvBad() {\n\t\tObject inputMethodManager = getContext().getSystemService(Context.INPUT_METHOD_SERVICE);\n\t\treturn InputMethodManager\n\t\t\t\t.mService.get(inputMethodManager) != getInvocationStub().getBaseInterface();\n\t}\n\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/input/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.input;\n\nimport android.view.inputmethod.EditorInfo;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.helper.utils.ArrayUtils;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\nclass MethodProxies {\n\n    static class StartInput extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"startInput\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (args.length > 2 && args[2] instanceof EditorInfo) {\n                EditorInfo attribute = (EditorInfo) args[2];\n                attribute.packageName = getHostPkg();\n            }\n            return method.invoke(who, args);\n        }\n\n    }\n\n    static class WindowGainedFocus extends MethodProxy {\n\n        private Boolean noEditorInfo = null;\n        private int editorInfoIndex = -1;\n\n        @Override\n        public String getMethodName() {\n            return \"windowGainedFocus\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (noEditorInfo == null) {\n                editorInfoIndex = ArrayUtils.indexOfFirst(args, EditorInfo.class);\n                noEditorInfo = editorInfoIndex == -1;\n            }\n            if (!noEditorInfo) {\n                EditorInfo attribute = (EditorInfo) args[editorInfoIndex];\n                if (attribute != null) {\n                    attribute.packageName = getHostPkg();\n                }\n            }\n            return method.invoke(who, args);\n        }\n\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/isms/ISmsStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.isms;\n\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceSpecPkgMethodProxy;\n\nimport mirror.com.android.internal.telephony.ISms;\n\n/**\n * @author Lody\n */\n\npublic class ISmsStub extends BinderInvocationProxy {\n\n    public ISmsStub() {\n        super(ISms.Stub.asInterface, \"isms\");\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"getAllMessagesFromIccEfForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"updateMessageOnIccEfForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"copyMessageToIccEfForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendDataForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendDataForSubscriberWithSelfPermissions\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendTextForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendTextForSubscriberWithSelfPermissions\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendMultipartTextForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendStoredText\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendStoredMultipartText\", 1));\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getAllMessagesFromIccEf\"));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"getAllMessagesFromIccEfForSubscriber\", 1));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"updateMessageOnIccEf\"));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"updateMessageOnIccEfForSubscriber\", 1));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"copyMessageToIccEf\"));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"copyMessageToIccEfForSubscriber\", 1));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"sendData\"));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendDataForSubscriber\", 1));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"sendText\"));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendTextForSubscriber\", 1));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"sendMultipartText\"));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendMultipartTextForSubscriber\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendStoredText\", 1));\n            addMethodProxy(new ReplaceSpecPkgMethodProxy(\"sendStoredMultipartText\", 1));\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getAllMessagesFromIccEf\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"updateMessageOnIccEf\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"copyMessageToIccEf\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"sendData\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"sendText\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"sendMultipartText\"));\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/isub/ISubStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.isub;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\n\nimport mirror.com.android.internal.telephony.ISub;\n\n/**\n * @author Lody\n */\npublic class ISubStub extends BinderInvocationProxy {\n\n    public ISubStub() {\n        super(ISub.Stub.asInterface, \"isub\");\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getAllSubInfoList\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getAllSubInfoCount\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getActiveSubscriptionInfo\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getActiveSubscriptionInfoForIccId\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getActiveSubscriptionInfoForSimSlotIndex\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getActiveSubscriptionInfoList\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getActiveSubInfoCount\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getSubscriptionProperty\"));\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/job/JobServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.job;\n\nimport android.annotation.TargetApi;\nimport android.app.job.JobInfo;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.ipc.VJobScheduler;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.app.job.IJobScheduler;\n\n/**\n * @author Lody\n *\n * @see android.app.job.JobScheduler\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class JobServiceStub extends BinderInvocationProxy {\n\n\tpublic JobServiceStub() {\n\t\tsuper(IJobScheduler.Stub.asInterface, Context.JOB_SCHEDULER_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new schedule());\n\t\taddMethodProxy(new getAllPendingJobs());\n\t\taddMethodProxy(new cancelAll());\n\t\taddMethodProxy(new cancel());\n\t}\n\n\n\tprivate class schedule extends MethodProxy {\n\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"schedule\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tJobInfo jobInfo = (JobInfo) args[0];\n\t\t\treturn VJobScheduler.get().schedule(jobInfo);\n\t\t}\n\t}\n\n\tprivate class getAllPendingJobs extends MethodProxy {\n\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"getAllPendingJobs\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\treturn VJobScheduler.get().getAllPendingJobs();\n\t\t}\n\t}\n\n\tprivate class cancelAll extends MethodProxy {\n\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"cancelAll\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tVJobScheduler.get().cancelAll();\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate class cancel extends MethodProxy {\n\n\t\t@Override\n\t\tpublic String getMethodName() {\n\t\t\treturn \"cancel\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tint jobId = (int) args[0];\n\t\t\tVJobScheduler.get().cancel(jobId);\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/libcore/LibCoreStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.libcore;\n\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceUidMethodProxy;\n\nimport mirror.libcore.io.ForwardingOs;\nimport mirror.libcore.io.Libcore;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic class LibCoreStub extends MethodInvocationProxy<MethodInvocationStub<Object>> {\n\n    public LibCoreStub() {\n        super(new MethodInvocationStub<Object>(getOs()));\n    }\n\n    private static Object getOs() {\n        Object os = Libcore.os.get();\n        if (ForwardingOs.os != null) {\n            Object posix = ForwardingOs.os.get(os);\n            if (posix != null) {\n                os = posix;\n            }\n        }\n        return os;\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceUidMethodProxy(\"chown\", 1));\n        addMethodProxy(new ReplaceUidMethodProxy(\"fchown\", 1));\n        addMethodProxy(new ReplaceUidMethodProxy(\"getpwuid\", 0));\n        addMethodProxy(new ReplaceUidMethodProxy(\"lchown\", 1));\n        addMethodProxy(new ReplaceUidMethodProxy(\"setuid\", 0));\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        Libcore.os.set(getInvocationStub().getProxyInterface());\n    }\n\n    @Override\n    public boolean isEnvBad() {\n        return getOs() != getInvocationStub().getProxyInterface();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/libcore/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.libcore;\n\nimport com.lody.virtual.client.NativeEngine;\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\nimport mirror.libcore.io.Os;\n\n/**\n * @author Lody\n */\n\nclass MethodProxies {\n\n    static class Lstat extends Stat {\n\n        @Override\n        public String getMethodName() {\n            return \"lstat\";\n        }\n    }\n\n    static class Getpwnam extends MethodProxy {\n            @Override\n            public String getMethodName() {\n                return \"getpwnam\";\n            }\n\n            @Override\n            public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n                if (result != null) {\n                    Reflect pwd = Reflect.on(result);\n                    int uid = pwd.get(\"pw_uid\");\n                    if (uid == VirtualCore.get().myUid()) {\n                        pwd.set(\"pw_uid\", VClientImpl.get().getVUid());\n                    }\n                }\n                return result;\n            }\n        }\n\n    static class GetUid extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getuid\";\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            int uid = (int) result;\n            return NativeEngine.onGetUid(uid);\n        }\n    }\n\n    static class GetsockoptUcred extends MethodProxy {\n            @Override\n            public String getMethodName() {\n                return \"getsockoptUcred\";\n            }\n\n            @Override\n            public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n                if (result != null) {\n                    Reflect ucred = Reflect.on(result);\n                    int uid = ucred.get(\"uid\");\n                    if (uid == VirtualCore.get().myUid()) {\n                        ucred.set(\"uid\", getBaseVUid());\n                    }\n                }\n                return result;\n            }\n        }\n\n    static class Stat extends MethodProxy {\n\n        private static Field st_uid;\n\n        static {\n            try {\n                Method stat = Os.TYPE.getMethod(\"stat\", String.class);\n                Class<?> StructStat = stat.getReturnType();\n                st_uid = StructStat.getDeclaredField(\"st_uid\");\n                st_uid.setAccessible(true);\n            } catch (Throwable e) {\n                throw new IllegalStateException(e);\n            }\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            int uid = (int) st_uid.get(result);\n            if (uid == VirtualCore.get().myUid()) {\n                st_uid.set(result, getBaseVUid());\n            }\n            return result;\n        }\n\n        @Override\n        public String getMethodName() {\n            return \"stat\";\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/location/LocationManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.location;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.location.ILocationManager;\nimport mirror.android.location.LocationRequestL;\n\n/**\n * @author Lody\n *\n * @see android.location.LocationManager\n */\npublic class LocationManagerStub extends BinderInvocationProxy {\n\tpublic LocationManagerStub() {\n\t\tsuper(ILocationManager.Stub.asInterface, Context.LOCATION_SERVICE);\n\t}\n\n\tprivate static class BaseMethodProxy extends ReplaceLastPkgMethodProxy {\n\n\t\tpublic BaseMethodProxy(String name) {\n\t\t\tsuper(name);\n\t\t}\n\t\t@Override\n\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\tif (args.length > 0) {\n\t\t\t\tObject request = args[0];\n\t\t\t\tif (LocationRequestL.mHideFromAppOps != null) {\n\t\t\t\t\tLocationRequestL.mHideFromAppOps.set(request, false);\n\t\t\t\t}\n\t\t\t\tif (LocationRequestL.mWorkSource != null) {\n\t\t\t\t\tLocationRequestL.mWorkSource.set(request, null);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn super.call(who, method, args);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"addTestProvider\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"removeTestProvider\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setTestProviderLocation\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"clearTestProviderLocation\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setTestProviderEnabled\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"clearTestProviderEnabled\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"setTestProviderStatus\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"clearTestProviderStatus\"));\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"addGpsMeasurementsListener\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"addGpsNavigationMessageListener\"));\n\t\t}\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"addGpsStatusListener\"));\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\taddMethodProxy(new BaseMethodProxy(\"requestLocationUpdates\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"removeUpdates\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"requestGeofence\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"removeGeofence\"));\n\t\t\taddMethodProxy(new BaseMethodProxy(\"getLastLocation\"));\n\t\t}\n\n\t\tif (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN\n\t\t\t\t&& TextUtils.equals(Build.VERSION.RELEASE, \"4.1.2\")) {\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"requestLocationUpdates\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"requestLocationUpdatesPI\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"removeUpdates\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"removeUpdatesPI\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"addProximityAlert\"));\n\t\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getLastKnownLocation\"));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/media/router/MediaRouterServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.media.router;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.media.IMediaRouterService;\n\n/**\n * @author Lody\n * @see android.media.MediaRouter\n */\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN)\npublic class MediaRouterServiceStub extends BinderInvocationProxy {\n\n    public MediaRouterServiceStub() {\n        super(IMediaRouterService.Stub.asInterface, Context.MEDIA_ROUTER_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"registerClientAsUser\"));\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/media/session/SessionManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.media.session;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.media.session.ISessionManager;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class SessionManagerStub extends BinderInvocationProxy {\n\n\tpublic SessionManagerStub() {\n\t\tsuper(ISessionManager.Stub.asInterface, Context.MEDIA_SESSION_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"createSession\"));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.mount;\n\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\nclass MethodProxies {\n\n    static class GetVolumeList extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getVolumeList\";\n        }\n\n        @Override\n        public boolean beforeCall(Object who, Method method, Object... args) {\n            if (args == null || args.length == 0) {\n                return super.beforeCall(who, method, args);\n            }\n            if (args[0] instanceof Integer) {\n                args[0] = getRealUid();\n            }\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return super.beforeCall(who, method, args);\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            return result;\n        }\n    }\n\n    static class Mkdirs extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"mkdirs\";\n        }\n\n        @Override\n        public boolean beforeCall(Object who, Method method, Object... args) {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return super.beforeCall(who, method, args);\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n                return super.call(who, method, args);\n            }\n            String path;\n            if (args.length == 1) {\n                path = (String) args[0];\n            } else {\n                path = (String) args[1];\n            }\n            File file = new File(path);\n            if (!file.exists() && !file.mkdirs()) {\n                return -1;\n            }\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MountServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.mount;\n\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\n\nimport mirror.android.os.mount.IMountService;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic class MountServiceStub extends BinderInvocationProxy {\n\n\tpublic MountServiceStub() {\n\t\tsuper(IMountService.Stub.asInterface, \"mount\");\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/network/NetworkManagementStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.network;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceUidMethodProxy;\n\nimport mirror.android.os.INetworkManagementService;\n\n@TargetApi(Build.VERSION_CODES.M)\npublic class\nNetworkManagementStub extends BinderInvocationProxy {\n\n\tpublic NetworkManagementStub() {\n\t\tsuper(INetworkManagementService.Stub.asInterface, \"network_management\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceUidMethodProxy(\"setUidCleartextNetworkPolicy\", 0));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/notification/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.notification;\n\nimport android.app.Notification;\nimport android.os.Build;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\nimport com.lody.virtual.client.ipc.VNotificationManager;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\n@SuppressWarnings(\"unused\")\nclass MethodProxies {\n\n    static class EnqueueNotification extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"enqueueNotification\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            if (getHostPkg().equals(pkg)) {\n                return method.invoke(who, args);\n            }\n            int notificationIndex = ArrayUtils.indexOfFirst(args, Notification.class);\n            int idIndex = ArrayUtils.indexOfFirst(args, Integer.class);\n            int id = (int) args[idIndex];\n            id = VNotificationManager.get().dealNotificationId(id, pkg, null, getAppUserId());\n            args[idIndex] = id;\n            Notification notification = (Notification) args[notificationIndex];\n            if (!VNotificationManager.get().dealNotification(id, notification, pkg)) {\n                return 0;\n            }\n            VNotificationManager.get().addNotification(id, null, pkg, getAppUserId());\n            args[0] = getHostPkg();\n            return method.invoke(who, args);\n        }\n    }\n\n    /* package */ static class EnqueueNotificationWithTag extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"enqueueNotificationWithTag\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            if (getHostPkg().equals(pkg)) {\n                return method.invoke(who, args);\n            }\n            int notificationIndex = ArrayUtils.indexOfFirst(args, Notification.class);\n            int idIndex = ArrayUtils.indexOfFirst(args, Integer.class);\n            int tagIndex = (Build.VERSION.SDK_INT >= 18 ? 2 : 1);\n            int id = (int) args[idIndex];\n            String tag = (String) args[tagIndex];\n\n            id = VNotificationManager.get().dealNotificationId(id, pkg, tag, getAppUserId());\n            tag = VNotificationManager.get().dealNotificationTag(id, pkg, tag, getAppUserId());\n            args[idIndex] = id;\n            args[tagIndex] = tag;\n            //key(tag,id)\n            Notification notification = (Notification) args[notificationIndex];\n            if (!VNotificationManager.get().dealNotification(id, notification, pkg)) {\n                return 0;\n            }\n            VNotificationManager.get().addNotification(id, tag, pkg, getAppUserId());\n            args[0] = getHostPkg();\n            if (Build.VERSION.SDK_INT >= 18 && args[1] instanceof String) {\n                args[1] = getHostPkg();\n            }\n            return method.invoke(who, args);\n        }\n    }\n\n    /* package */ static class EnqueueNotificationWithTagPriority extends EnqueueNotificationWithTag {\n\n        @Override\n        public String getMethodName() {\n            return \"enqueueNotificationWithTagPriority\";\n        }\n    }\n\n    /* package */ static class CancelNotificationWithTag extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"cancelNotificationWithTag\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = MethodParameterUtils.replaceFirstAppPkg(args);\n            if (getHostPkg().equals(pkg)) {\n                return method.invoke(who, args);\n            }\n            String tag = (String) args[1];\n            int id = (int) args[2];\n            id = VNotificationManager.get().dealNotificationId(id, pkg, tag, getAppUserId());\n            tag = VNotificationManager.get().dealNotificationTag(id, pkg, tag, getAppUserId());\n\n            args[1] = tag;\n            args[2] = id;\n            return method.invoke(who, args);\n        }\n    }\n\n    /**\n     * @author Lody\n     */\n    /* package */ static class CancelAllNotifications extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"cancelAllNotifications\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = MethodParameterUtils.replaceFirstAppPkg(args);\n            if (VirtualCore.get().isAppInstalled(pkg)) {\n                VNotificationManager.get().cancelAllNotification(pkg, getAppUserId());\n                return 0;\n            }\n            return method.invoke(who, args);\n        }\n    }\n\n    static class AreNotificationsEnabledForPackage extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"areNotificationsEnabledForPackage\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            if (getHostPkg().equals(pkg)) {\n                return method.invoke(who, args);\n            }\n            return VNotificationManager.get().areNotificationsEnabledForPackage(pkg, getAppUserId());\n        }\n    }\n\n    static class SetNotificationsEnabledForPackage extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"setNotificationsEnabledForPackage\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            if (getHostPkg().equals(pkg)) {\n                return method.invoke(who, args);\n            }\n            int enableIndex = ArrayUtils.indexOfFirst(args, Boolean.class);\n            boolean enable = (boolean) args[enableIndex];\n            VNotificationManager.get().setNotificationsEnabledForPackage(pkg, enable, getAppUserId());\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/notification/NotificationManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.notification;\n\nimport android.os.Build;\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.app.NotificationManager;\nimport mirror.android.widget.Toast;\n\n/**\n * @author Lody\n * @see android.app.NotificationManager\n * @see android.widget.Toast\n */\n@Inject(MethodProxies.class)\npublic class NotificationManagerStub extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {\n\n    public NotificationManagerStub() {\n        super(new MethodInvocationStub<IInterface>(NotificationManager.getService.call()));\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"enqueueToast\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"cancelToast\"));\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"removeAutomaticZenRules\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getImportance\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"areNotificationsEnabled\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"setNotificationPolicy\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getNotificationPolicy\"));\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"isNotificationPolicyAccessGrantedForPackage\"));\n        }\n        if (\"samsung\".equalsIgnoreCase(Build.BRAND) || \"samsung\".equalsIgnoreCase(Build.MANUFACTURER)) {\n            addMethodProxy(new ReplaceCallingPkgMethodProxy(\"removeEdgeNotification\"));\n        }\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        NotificationManager.sService.set(getInvocationStub().getProxyInterface());\n        Toast.sService.set(getInvocationStub().getProxyInterface());\n    }\n\n    @Override\n    public boolean isEnvBad() {\n        return NotificationManager.getService.call() != getInvocationStub().getProxyInterface();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/persistent_data_block/PersistentDataBlockServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.persistent_data_block;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport mirror.android.service.persistentdata.IPersistentDataBlockService;\n\n/**\n * @author Lody\n */\npublic class PersistentDataBlockServiceStub extends BinderInvocationProxy {\n\n\tpublic PersistentDataBlockServiceStub() {\n\t\tsuper(IPersistentDataBlockService.Stub.asInterface, \"persistent_data_block\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"write\", -1));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"read\", new byte[0]));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"wipe\", null));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getDataBlockSize\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getMaximumDataBlockSize\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"setOemUnlockEnabled\", 0));\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"getOemUnlockEnabled\", false));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/phonesubinfo/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.phonesubinfo;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.helper.utils.Mark;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"ALL\")\nclass MethodProxies {\n\n    @Mark(\"fake device id\")\n    static class GetDeviceId extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getDeviceId\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return getDeviceInfo().deviceId;\n        }\n    }\n\n    static class GetDeviceIdForSubscriber extends GetDeviceId {\n\n        @Override\n        public String getMethodName() {\n            return \"getDeviceIdForSubscriber\";\n        }\n\n    }\n\n    @Mark(\"fake iccid\")\n    static class GetIccSerialNumber extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getIccSerialNumber\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return getDeviceInfo().iccId;\n        }\n    }\n\n\n    static class getIccSerialNumberForSubscriber extends GetIccSerialNumber {\n        @Override\n        public String getMethodName() {\n            return \"getIccSerialNumberForSubscriber\";\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/phonesubinfo/PhoneSubInfoStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.phonesubinfo;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\n\nimport mirror.com.android.internal.telephony.IPhoneSubInfo;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic class PhoneSubInfoStub extends BinderInvocationProxy {\n\tpublic PhoneSubInfoStub() {\n\t\tsuper(IPhoneSubInfo.Stub.asInterface, \"iphonesubinfo\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getNaiForSubscriber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getImeiForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getDeviceSvn\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getDeviceSvnUsingSubId\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getSubscriberId\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getSubscriberIdForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getGroupIdLevel1\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getGroupIdLevel1ForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getLine1Number\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getLine1NumberForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getLine1AlphaTag\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getLine1AlphaTagForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getMsisdn\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getMsisdnForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getVoiceMailNumber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getVoiceMailNumberForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getVoiceMailAlphaTag\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getVoiceMailAlphaTagForSubscriber\"));\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/pm/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.pm;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.IPackageDataObserver;\nimport android.content.pm.IPackageDeleteObserver2;\nimport android.content.pm.IPackageInstallerCallback;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageInstaller;\nimport android.content.pm.PackageManager;\nimport android.content.pm.PermissionGroupInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.pm.Signature;\nimport android.graphics.Bitmap;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.IInterface;\nimport android.os.Process;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\nimport com.lody.virtual.client.ipc.VPackageManager;\nimport com.lody.virtual.helper.collection.ArraySet;\nimport com.lody.virtual.helper.compat.ParceledListSliceCompat;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.server.IPackageInstaller;\nimport com.lody.virtual.server.pm.installer.SessionInfo;\nimport com.lody.virtual.server.pm.installer.SessionParams;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\nimport mirror.android.content.pm.ParceledListSlice;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"unused\")\nclass MethodProxies {\n\n    static class IsPackageAvailable extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"isPackageAvailable\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkgName = (String) args[0];\n            if (isAppPkg(pkgName)) {\n                return true;\n            }\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetInstallerPackageName extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getInstallerPackageName\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return \"com.android.vending\";\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class GetPreferredActivities extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPreferredActivities\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceLastAppPkg(args);\n            return method.invoke(who, args);\n        }\n    }\n\n\n    static class GetComponentEnabledSetting extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getComponentEnabledSetting\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            // NOTE: 有4个状态: 0默认 1可用 2禁止 3User Disable\n            ComponentName component = (ComponentName) args[0];\n            if (component != null) {\n                return 1;\n            }\n            return method.invoke(who, args);\n        }\n    }\n\n\n    static class RemovePackageFromPreferred extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"removePackageFromPreferred\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n    }\n\n    /**\n     * @author Lody\n     *         <p>\n     *         public ActivityInfo getServiceInfo(ComponentName className, int\n     *         flags, int userId)\n     */\n    static class GetServiceInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getServiceInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName componentName = (ComponentName) args[0];\n            int flags = (int) args[1];\n            int userId = VUserHandle.myUserId();\n            ServiceInfo info = VPackageManager.get().getServiceInfo(componentName, flags, userId);\n            if (info != null) {\n                return info;\n            }\n            info = (ServiceInfo) method.invoke(who, args);\n            if (info == null || !isVisiblePackage(info.applicationInfo)) {\n                return null;\n            }\n            return info;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPackageUid extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPackageUid\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkgName = (String) args[0];\n            if (pkgName.equals(getHostPkg())) {\n                return method.invoke(who, args);\n            }\n            int uid = VPackageManager.get().getPackageUid(pkgName, 0);\n            return VUserHandle.getAppId(uid);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n    }\n\n    /**\n     * @author Lody\n     *         <p>\n     *         <p>\n     *         public ActivityInfo getActivityInfo(ComponentName className, int\n     *         flags, int userId)\n     */\n    static class GetActivityInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getActivityInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName componentName = (ComponentName) args[0];\n            if (getHostPkg().equals(componentName.getPackageName())) {\n                return method.invoke(who, args);\n            }\n            int userId = VUserHandle.myUserId();\n            int flags = (int) args[1];\n            ActivityInfo info = VPackageManager.get().getActivityInfo(componentName, flags, userId);\n            if (info == null) {\n                info = (ActivityInfo) method.invoke(who, args);\n                if (info == null || !isVisiblePackage(info.applicationInfo)) {\n                    return null;\n                }\n            }\n            return info;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class GetPackageUidEtc extends GetPackageUid {\n        @Override\n        public String getMethodName() {\n            return super.getMethodName() + \"Etc\";\n        }\n    }\n\n    static class GetPackageInstaller extends MethodProxy {\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n        @Override\n        public String getMethodName() {\n            return \"getPackageInstaller\";\n        }\n\n        @Override\n        public Object call(final Object who, Method method, Object... args) throws Throwable {\n            final IInterface installer = (IInterface) method.invoke(who, args);\n            final IPackageInstaller vInstaller = VPackageManager.get().getPackageInstaller();\n            return Proxy.newProxyInstance(installer.getClass().getClassLoader(), installer.getClass().getInterfaces(),\n                    new InvocationHandler() {\n                        @Override\n                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                            switch (method.getName()) {\n                                case \"createSession\": {\n                                    SessionParams params = SessionParams.create((PackageInstaller.SessionParams) args[0]);\n                                    String installerPackageName = (String) args[1];\n                                    return vInstaller.createSession(params, installerPackageName, VUserHandle.myUserId());\n                                }\n                                case \"updateSessionAppIcon\": {\n                                    int sessionId = (int) args[0];\n                                    Bitmap appIcon = (Bitmap) args[1];\n                                    vInstaller.updateSessionAppIcon(sessionId, appIcon);\n                                    return 0;\n                                }\n                                case \"updateSessionAppLabel\": {\n                                    int sessionId = (int) args[0];\n                                    String appLabel = (String) args[1];\n                                    vInstaller.updateSessionAppLabel(sessionId, appLabel);\n                                    return 0;\n                                }\n                                case \"abandonSession\": {\n                                    vInstaller.abandonSession((Integer) args[0]);\n                                    return 0;\n                                }\n                                case \"openSession\": {\n                                    return vInstaller.openSession((Integer) args[0]);\n                                }\n                                case \"getSessionInfo\": {\n                                    SessionInfo info = vInstaller.getSessionInfo((Integer) args[0]);\n                                    if (info != null) {\n                                        return info.alloc();\n                                    }\n                                    return null;\n                                }\n                                case \"getAllSessions\": {\n                                    return ParceledListSliceCompat.create(\n                                            vInstaller.getAllSessions((Integer) args[0]).getList()\n                                    );\n                                }\n                                case \"getMySessions\": {\n                                    String installerPackageName = (String) args[0];\n                                    int userId = (int) args[1];\n                                    return ParceledListSliceCompat.create(\n                                            vInstaller.getMySessions(installerPackageName, userId).getList()\n                                    );\n                                }\n                                case \"registerCallback\": {\n                                    IPackageInstallerCallback callback = (IPackageInstallerCallback) args[0];\n                                    vInstaller.registerCallback(callback, VUserHandle.myUserId());\n                                    return 0;\n                                }\n                                case \"unregisterCallback\": {\n                                    IPackageInstallerCallback callback = (IPackageInstallerCallback) args[0];\n                                    vInstaller.unregisterCallback(callback);\n                                    return 0;\n                                }\n                                case \"setPermissionsResult\": {\n                                    int sessionId = (int) args[0];\n                                    boolean accepted = (boolean) args[1];\n                                    vInstaller.setPermissionsResult(sessionId, accepted);\n                                    return 0;\n                                }\n                            }\n                            throw new RuntimeException(\"Not support PackageInstaller method : \" + method.getName());\n                        }\n                    });\n        }\n    }\n\n\n    static class FreeStorageAndNotify extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"freeStorageAndNotify\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (args[args.length - 1] instanceof IPackageDataObserver) {\n                IPackageDataObserver observer = (IPackageDataObserver) args[args.length - 1];\n                observer.onRemoveCompleted(null, true);\n            }\n            return 0;\n        }\n    }\n\n\n    static class GetPackageGids extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPackageGids\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n\n    }\n\n\n    static class RevokeRuntimePermission extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"revokeRuntimePermission\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class ClearPackagePreferredActivities extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"clearPackagePreferredActivities\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n    }\n\n\n    static class ResolveContentProvider extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"resolveContentProvider\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String name = (String) args[0];\n            int flags = (int) args[1];\n            int userId = VUserHandle.myUserId();\n            ProviderInfo info = VPackageManager.get().resolveContentProvider(name, flags, userId);\n            if (info == null) {\n                info = (ProviderInfo) method.invoke(who, args);\n                if (info != null && isVisiblePackage(info.applicationInfo)) {\n                    return info;\n                }\n            }\n            return info;\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    static class QueryIntentServices extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"queryIntentServices\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            boolean slice = ParceledListSliceCompat.isReturnParceledListSlice(method);\n            int userId = VUserHandle.myUserId();\n            List<ResolveInfo> appResult = VPackageManager.get().queryIntentServices((Intent) args[0],\n                    (String) args[1], (Integer) args[2], userId);\n            Object _hostResult = method.invoke(who, args);\n            if (_hostResult != null) {\n                List<ResolveInfo> hostResult = slice ? ParceledListSlice.getList.call(_hostResult)\n                        : (List) _hostResult;\n                if (hostResult != null) {\n                    Iterator<ResolveInfo> iterator = hostResult.iterator();\n                    while (iterator.hasNext()) {\n                        ResolveInfo info = iterator.next();\n                        if (info == null || info.serviceInfo == null || !isVisiblePackage(info.serviceInfo.applicationInfo)) {\n                            iterator.remove();\n                        }\n                    }\n                    appResult.addAll(hostResult);\n                }\n            }\n            if (slice) {\n                return ParceledListSliceCompat.create(appResult);\n            }\n            return appResult;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPermissions extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPermissions\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return method.invoke(who, args);\n        }\n    }\n\n    static class IsPackageForzen extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"isPackageForzen\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return false;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPackageGidsEtc extends GetPackageGids {\n\n        @Override\n        public String getMethodName() {\n            return super.getMethodName() + \"Etc\";\n        }\n\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    static class QueryIntentActivities extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"queryIntentActivities\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            boolean slice = ParceledListSliceCompat.isReturnParceledListSlice(method);\n            int userId = VUserHandle.myUserId();\n            List<ResolveInfo> appResult = VPackageManager.get().queryIntentActivities((Intent) args[0],\n                    (String) args[1], (Integer) args[2], userId);\n            Object _hostResult = method.invoke(who, args);\n            if (_hostResult != null) {\n                List<ResolveInfo> hostResult = slice ? ParceledListSlice.getList.call(_hostResult)\n                        : (List) _hostResult;\n                if (hostResult != null) {\n                    Iterator<ResolveInfo> iterator = hostResult.iterator();\n                    while (iterator.hasNext()) {\n                        ResolveInfo info = iterator.next();\n                        if (info == null || info.activityInfo == null || !isVisiblePackage(info.activityInfo.applicationInfo)) {\n                            iterator.remove();\n                        }\n                    }\n                    appResult.addAll(hostResult);\n                }\n            }\n            if (slice) {\n                return ParceledListSliceCompat.create(appResult);\n            }\n            return appResult;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class ResolveService extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"resolveService\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            Intent intent = (Intent) args[0];\n            String resolvedType = (String) args[1];\n            int flags = (int) args[2];\n            int userId = VUserHandle.myUserId();\n            ResolveInfo resolveInfo = VPackageManager.get().resolveService(intent, resolvedType, flags, userId);\n            if (resolveInfo == null) {\n                resolveInfo = (ResolveInfo) method.invoke(who, args);\n            }\n            return resolveInfo;\n        }\n    }\n\n\n    static class ClearPackagePersistentPreferredActivities extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"clearPackagePersistentPreferredActivities\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n    }\n\n    static class GetPermissionGroupInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPermissionGroupInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String name = (String) args[0];\n            int flags = (int) args[1];\n            PermissionGroupInfo info = VPackageManager.get().getPermissionGroupInfo(name, flags);\n            if (info != null) {\n                return info;\n            }\n            return super.call(who, method, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static final class GetPackageInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPackageInfo\";\n        }\n\n        @Override\n        public boolean beforeCall(Object who, Method method, Object... args) {\n            return args != null && args[0] != null;\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            int flags = (int) args[1];\n            int userId = VUserHandle.myUserId();\n            PackageInfo packageInfo = VPackageManager.get().getPackageInfo(pkg, flags, userId);\n            if (packageInfo != null) {\n                return packageInfo;\n            }\n            packageInfo = (PackageInfo) method.invoke(who, args);\n            if (packageInfo != null) {\n                if (isVisiblePackage(packageInfo.applicationInfo)) {\n                    return packageInfo;\n                }\n            }\n            return null;\n        }\n\n    }\n\n\n    static class DeleteApplicationCacheFiles extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"deleteApplicationCacheFiles\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            // TODO\n            return method.invoke(who, args);\n        }\n    }\n\n\n    static class SetApplicationBlockedSettingAsUser extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"setApplicationBlockedSettingAsUser\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetApplicationEnabledSetting extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getApplicationEnabledSetting\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n    }\n\n    static class AddPackageToPreferred extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"addPackageToPreferred\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return 0;\n        }\n    }\n\n    static class CheckPermission extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"checkPermission\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String permName = (String) args[0];\n            String pkgName = (String) args[1];\n            int userId = VUserHandle.myUserId();\n            return VPackageManager.get().checkPermission(permName, pkgName, userId);\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            return super.afterCall(who, method, args, result);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetPackagesForUid extends MethodProxy {\n\n\n        @Override\n        public String getMethodName() {\n            return \"getPackagesForUid\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int uid = (int) args[0];\n            int callingUid = Binder.getCallingUid();\n            if (uid == VirtualCore.get().myUid()) {\n                uid = getBaseVUid();\n            }\n            String[] callingPkgs = VPackageManager.get().getPackagesForUid(callingUid);\n            String[] targetPkgs = VPackageManager.get().getPackagesForUid(uid);\n            String[] selfPkgs = VPackageManager.get().getPackagesForUid(Process.myUid());\n\n            Set<String> pkgList = new ArraySet<>(2);\n            if (callingPkgs != null && callingPkgs.length > 0) {\n                pkgList.addAll(Arrays.asList(callingPkgs));\n            }\n            if (targetPkgs != null && targetPkgs.length > 0) {\n                pkgList.addAll(Arrays.asList(targetPkgs));\n            }\n            if (selfPkgs != null && selfPkgs.length > 0) {\n                pkgList.addAll(Arrays.asList(selfPkgs));\n            }\n            return pkgList.toArray(new String[pkgList.size()]);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    static class QueryContentProviders extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"queryContentProviders\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String processName = (String) args[0];\n            int flags = (int) args[2];\n            List<ProviderInfo> infos = VPackageManager.get().queryContentProviders(processName, flags, 0);\n            if (ParceledListSliceCompat.isReturnParceledListSlice(method)) {\n                return ParceledListSliceCompat.create(infos);\n            }\n            return infos;\n        }\n\n    }\n\n    static class SetApplicationEnabledSetting extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"setApplicationEnabledSetting\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    @SuppressLint(\"PackageManagerGetSignatures\")\n    static class CheckSignatures extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"checkSignatures\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n\n            if (args.length == 2 && args[0] instanceof String && args[1] instanceof String) {\n\n                PackageManager pm = VirtualCore.getPM();\n\n                String pkgNameOne = (String) args[0], pkgNameTwo = (String) args[1];\n                try {\n                    PackageInfo pkgOne = pm.getPackageInfo(pkgNameOne, PackageManager.GET_SIGNATURES);\n                    PackageInfo pkgTwo = pm.getPackageInfo(pkgNameTwo, PackageManager.GET_SIGNATURES);\n\n                    Signature[] one = pkgOne.signatures;\n                    Signature[] two = pkgTwo.signatures;\n\n                    if (ArrayUtils.isEmpty(one)) {\n                        if (!ArrayUtils.isEmpty(two)) {\n                            return PackageManager.SIGNATURE_FIRST_NOT_SIGNED;\n                        } else {\n                            return PackageManager.SIGNATURE_NEITHER_SIGNED;\n                        }\n                    } else {\n                        if (ArrayUtils.isEmpty(two)) {\n                            return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;\n                        } else {\n                            if (Arrays.equals(one, two)) {\n                                return PackageManager.SIGNATURE_MATCH;\n                            } else {\n                                return PackageManager.SIGNATURE_NO_MATCH;\n                            }\n                        }\n                    }\n                } catch (Throwable e) {\n                    // Ignore\n                }\n            }\n\n            return method.invoke(who, args);\n        }\n    }\n\n    static class checkUidSignatures extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"checkUidSignatures\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int uid1 = (int) args[0];\n            int uid2 = (int) args[1];\n            // TODO: verify the signatures by uid.\n            return PackageManager.SIGNATURE_MATCH;\n        }\n    }\n\n    static class getNameForUid extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getNameForUid\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int uid = (int) args[0];\n            return VPackageManager.get().getNameForUid(uid);\n        }\n    }\n\n\n    static class DeletePackage extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"deletePackage\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkgName = (String) args[0];\n            try {\n                VirtualCore.get().uninstallPackage(pkgName);\n                IPackageDeleteObserver2 observer = (IPackageDeleteObserver2) args[1];\n                if (observer != null) {\n                    observer.onPackageDeleted(pkgName, 0, \"done.\");\n                }\n            } catch (Throwable e) {\n                // Ignore\n            }\n            return 0;\n        }\n\n    }\n\n\n    static class ActivitySupportsIntent extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"activitySupportsIntent\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName component = (ComponentName) args[0];\n            Intent intent = (Intent) args[1];\n            String resolvedType = (String) args[2];\n            return VPackageManager.get().activitySupportsIntent(component, intent, resolvedType);\n        }\n    }\n\n\n    static class ResolveIntent extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"resolveIntent\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            Intent intent = (Intent) args[0];\n            String resolvedType = (String) args[1];\n            int flags = (int) args[2];\n            int userId = VUserHandle.myUserId();\n            ResolveInfo resolveInfo = VPackageManager.get().resolveIntent(intent, resolvedType, flags, userId);\n            if (resolveInfo == null) {\n                resolveInfo = (ResolveInfo) method.invoke(who, args);\n            }\n            return resolveInfo;\n        }\n    }\n\n\n    static class GetApplicationInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getApplicationInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            String pkg = (String) args[0];\n            int flags = (int) args[1];\n            if (getHostPkg().equals(pkg)) {\n                return method.invoke(who, args);\n            }\n            int userId = VUserHandle.myUserId();\n            ApplicationInfo info = VPackageManager.get().getApplicationInfo(pkg, flags, userId);\n            if (info != null) {\n                return info;\n            }\n            info = (ApplicationInfo) method.invoke(who, args);\n            if (info == null || !isVisiblePackage(info)) {\n                return null;\n            }\n            return info;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetProviderInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getProviderInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName componentName = (ComponentName) args[0];\n            int flags = (int) args[1];\n            if (getHostPkg().equals(componentName.getPackageName())) {\n                return method.invoke(who, args);\n            }\n            int userId = VUserHandle.myUserId();\n            ProviderInfo info = VPackageManager.get().getProviderInfo(componentName, flags, userId);\n            if (info == null) {\n                info = (ProviderInfo) method.invoke(who, args);\n                if (info == null || !isVisiblePackage(info.applicationInfo)) {\n                    return null;\n                }\n            }\n            return info;\n        }\n\n    }\n\n    static class SetComponentEnabledSetting extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"setComponentEnabledSetting\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return 0;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n    static class GetInstalledApplications extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getInstalledApplications\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n\n            int flags = (Integer) args[0];\n            int userId = VUserHandle.myUserId();\n            List<ApplicationInfo> appInfos = VPackageManager.get().getInstalledApplications(flags, userId);\n            if (ParceledListSliceCompat.isReturnParceledListSlice(method)) {\n                return ParceledListSliceCompat.create(appInfos);\n            }\n            return appInfos;\n        }\n    }\n\n    @SuppressWarnings({\"unchecked\", \"WrongConstant\"})\n    static class GetInstalledPackages extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getInstalledPackages\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int flags = (int) args[0];\n            int userId = VUserHandle.myUserId();\n            List<PackageInfo> packageInfos;\n            if (isAppProcess()) {\n                packageInfos = new ArrayList<>(VirtualCore.get().getInstalledAppCount());\n            } else {\n                packageInfos = VirtualCore.get().getUnHookPackageManager().getInstalledPackages(flags);\n            }\n            packageInfos.addAll(VPackageManager.get().getInstalledPackages(flags, userId));\n            if (ParceledListSliceCompat.isReturnParceledListSlice(method)) {\n                return ParceledListSliceCompat.create(packageInfos);\n            } else {\n                return packageInfos;\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    static class QueryIntentReceivers extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"queryIntentReceivers\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            boolean slice = ParceledListSliceCompat.isReturnParceledListSlice(method);\n            int userId = VUserHandle.myUserId();\n            List<ResolveInfo> appResult = VPackageManager.get().queryIntentReceivers((Intent) args[0], (String) args[1],\n                    (Integer) args[2], userId);\n            Object _hostResult = method.invoke(who, args);\n            List<ResolveInfo> hostResult = slice ? ParceledListSlice.getList.call(_hostResult)\n                    : (List) _hostResult;\n            if (hostResult != null) {\n                Iterator<ResolveInfo> iterator = hostResult.iterator();\n                while (iterator.hasNext()) {\n                    ResolveInfo info = iterator.next();\n                    if (info == null || info.activityInfo == null || !isVisiblePackage(info.activityInfo.applicationInfo)) {\n                        iterator.remove();\n                    }\n                }\n                appResult.addAll(hostResult);\n            }\n            if (slice) {\n                return ParceledListSliceCompat.create(appResult);\n            }\n            return appResult;\n        }\n    }\n\n\n    static class GetReceiverInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getReceiverInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName componentName = (ComponentName) args[0];\n            if (getHostPkg().equals(componentName.getPackageName())) {\n                return method.invoke(who, args);\n            }\n            int flags = (int) args[1];\n            ActivityInfo info = VPackageManager.get().getReceiverInfo(componentName, flags, 0);\n            if (info == null) {\n                info = (ActivityInfo) method.invoke(who, args);\n                if (info == null || !isVisiblePackage(info.applicationInfo)) {\n                    return null;\n                }\n            }\n            return info;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    static class GetPermissionFlags extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getPermissionFlags\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            // TODO\n            return method.invoke(who, args);\n        }\n\n    }\n\n\n    static class SetPackageStoppedState extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"setPackageStoppedState\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    static class QueryIntentContentProviders extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"queryIntentContentProviders\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            boolean slice = ParceledListSliceCompat.isReturnParceledListSlice(method);\n            int userId = VUserHandle.myUserId();\n            List<ResolveInfo> appResult = VPackageManager.get().queryIntentContentProviders((Intent) args[0], (String) args[1],\n                    (Integer) args[2], userId);\n            Object _hostResult = method.invoke(who, args);\n            List<ResolveInfo> hostResult = slice ? ParceledListSlice.getList.call(_hostResult)\n                    : (List) _hostResult;\n            if (hostResult != null) {\n                Iterator<ResolveInfo> iterator = hostResult.iterator();\n                while (iterator.hasNext()) {\n                    ResolveInfo info = iterator.next();\n                    if (info == null || info.providerInfo == null || !isVisiblePackage(info.providerInfo.applicationInfo)) {\n                        iterator.remove();\n                    }\n                }\n                appResult.addAll(hostResult);\n            }\n            if (ParceledListSliceCompat.isReturnParceledListSlice(method)) {\n                return ParceledListSliceCompat.create(appResult);\n            }\n            return appResult;\n        }\n\n        @Override\n        public boolean isEnable() {\n            return isAppProcess();\n        }\n    }\n\n\n    static class GetApplicationBlockedSettingAsUser extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getApplicationBlockedSettingAsUser\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            MethodParameterUtils.replaceFirstAppPkg(args);\n            return method.invoke(who, args);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/pm/PackageManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.pm;\n\nimport android.os.Build;\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationStub;\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\nimport com.lody.virtual.helper.compat.BuildCompat;\n\nimport mirror.android.app.ActivityThread;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic final class PackageManagerStub extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {\n\n    public PackageManagerStub() {\n        super(new MethodInvocationStub<>(ActivityThread.sPackageManager.get()));\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ResultStaticMethodProxy(\"addPermissionAsync\", true));\n        addMethodProxy(new ResultStaticMethodProxy(\"addPermission\", true));\n        addMethodProxy(new ResultStaticMethodProxy(\"performDexOpt\", true));\n        addMethodProxy(new ResultStaticMethodProxy(\"performDexOptIfNeeded\", false));\n        addMethodProxy(new ResultStaticMethodProxy(\"performDexOptSecondary\", true));\n        addMethodProxy(new ResultStaticMethodProxy(\"addOnPermissionsChangeListener\", 0));\n        addMethodProxy(new ResultStaticMethodProxy(\"removeOnPermissionsChangeListener\", 0));\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            addMethodProxy(new ResultStaticMethodProxy(\"checkPackageStartable\", 0));\n        }\n        if (BuildCompat.isOreo()) {\n            addMethodProxy(new ResultStaticMethodProxy(\"notifyDexLoad\", 0));\n            addMethodProxy(new ResultStaticMethodProxy(\"notifyPackageUse\", 0));\n            addMethodProxy(new ResultStaticMethodProxy(\"setInstantAppCookie\", false));\n            addMethodProxy(new ResultStaticMethodProxy(\"isInstantApp\", false));\n        }\n\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        final IInterface hookedPM = getInvocationStub().getProxyInterface();\n        ActivityThread.sPackageManager.set(hookedPM);\n        BinderInvocationStub pmHookBinder = new BinderInvocationStub(getInvocationStub().getBaseInterface());\n        pmHookBinder.copyMethodProxies(getInvocationStub());\n        pmHookBinder.replaceService(\"package\");\n    }\n\n    @Override\n    public boolean isEnvBad() {\n        return getInvocationStub().getProxyInterface() != ActivityThread.sPackageManager.get();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/power/PowerManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.power;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceSequencePkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport mirror.android.os.IPowerManager;\n\n/**\n * @author Lody\n */\npublic class PowerManagerStub extends BinderInvocationProxy {\n\n\tpublic PowerManagerStub() {\n\t\tsuper(IPowerManager.Stub.asInterface, Context.POWER_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceSequencePkgMethodProxy(\"acquireWakeLock\", 2) {\n\t\t\t@Override\n\t\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\t\ttry {\n\t\t\t\t\treturn super.call(who, method, args);\n\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\treturn onHandleError(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"acquireWakeLockWithUid\") {\n\n\t\t\t@Override\n\t\t\tpublic Object call(Object who, Method method, Object... args) throws Throwable {\n\t\t\t\ttry {\n\t\t\t\t\treturn super.call(who, method, args);\n\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\treturn onHandleError(e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\taddMethodProxy(new ResultStaticMethodProxy(\"updateWakeLockWorkSource\", 0));\n\t}\n\n\tprivate Object onHandleError(InvocationTargetException e) throws Throwable {\n\t\tif (e.getCause() instanceof SecurityException) {\n\t\t\treturn 0;\n\t\t}\n\t\tthrow e.getCause();\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/restriction/RestrictionStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.restriction;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.content.IRestrictionsManager;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\npublic class RestrictionStub extends BinderInvocationProxy {\n\tpublic RestrictionStub() {\n\t\tsuper(IRestrictionsManager.Stub.asInterface, Context.RESTRICTIONS_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getApplicationRestrictions\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"notifyPermissionResponse\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"requestPermission\"));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/search/SearchManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.search;\n\nimport android.annotation.TargetApi;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.os.Build;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.app.ISearchManager;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class SearchManagerStub extends BinderInvocationProxy {\n\n    public SearchManagerStub() {\n        super(ISearchManager.Stub.asInterface, Context.SEARCH_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new StaticMethodProxy(\"launchLegacyAssist\"));\n        addMethodProxy(new GetSearchableInfo());\n    }\n\n     private static class GetSearchableInfo extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getSearchableInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            ComponentName component = (ComponentName) args[0];\n            if (component != null) {\n                ActivityInfo activityInfo = VirtualCore.getPM().getActivityInfo(component, 0);\n                if (activityInfo != null) {\n                    return null;\n                }\n            }\n            return method.invoke(who, args);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/shortcut/ShortcutServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.shortcut;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport mirror.android.content.pm.ILauncherApps;\n\n/**\n * @author Lody\n */\npublic class ShortcutServiceStub extends BinderInvocationProxy {\n\n\n    public ShortcutServiceStub() {\n        super(ILauncherApps.Stub.asInterface, \"shortcut\");\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        super.inject();\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getManifestShortcuts\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getDynamicShortcuts\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"setDynamicShortcuts\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"addDynamicShortcuts\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"createShortcutResultIntent\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"disableShortcuts\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"enableShortcuts\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getRemainingCallCount\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getRateLimitResetTime\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getIconMaxDimensions\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getMaxShortcutCountPerActivity\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"reportShortcutUsed\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"onApplicationActive\"));\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.telephony;\n\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.helper.utils.Mark;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\nclass MethodProxies {\n\n    @Mark(\"fake device id.\")\n    static class GetDeviceId extends ReplaceCallingPkgMethodProxy {\n\n        public GetDeviceId() {\n            super(\"getDeviceId\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return getDeviceInfo().deviceId;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyRegistryStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.telephony;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceSequencePkgMethodProxy;\n\nimport mirror.com.android.internal.telephony.ITelephonyRegistry;\n\npublic class TelephonyRegistryStub extends BinderInvocationProxy {\n\n\tpublic TelephonyRegistryStub() {\n\t\tsuper(ITelephonyRegistry.Stub.asInterface, \"telephony.registry\");\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"listen\"));\n\t\taddMethodProxy(new ReplaceSequencePkgMethodProxy(\"listenForSubscriber\", 1));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.telephony;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\n\nimport mirror.com.android.internal.telephony.ITelephony;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic class TelephonyStub extends BinderInvocationProxy {\n\n\tpublic TelephonyStub() {\n\t\tsuper(ITelephony.Stub.asInterface, Context.TELEPHONY_SERVICE);\n\t}\n\n\t@Override\n\tprotected void onBindMethods() {\n\t\tsuper.onBindMethods();\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getNeighboringCellInfo\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getAllCellInfo\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getCellLocation\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"isOffhook\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getLine1NumberForDisplay\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"isOffhookForSubscriber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"isRingingForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"call\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"isRinging\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"isIdle\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"isIdleForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"isRadioOn\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"isRadioOnForSubscriber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"isSimPinEnabled\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getCdmaEriIconIndex\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getCdmaEriIconIndexForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getCdmaEriIconMode\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getCdmaEriIconModeForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getCdmaEriText\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getCdmaEriTextForSubscriber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getNetworkTypeForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getDataNetworkType\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getDataNetworkTypeForSubscriber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getVoiceNetworkTypeForSubscriber\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getLteOnCdmaMode\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getLteOnCdmaModeForSubscriber\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getCalculatedPreferredNetworkType\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getPcscfAddress\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getLine1AlphaTagForDisplay\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"getMergedSubscriberIds\"));\n\t\taddMethodProxy(new ReplaceLastPkgMethodProxy(\"getRadioAccessFamily\"));\n\t\taddMethodProxy(new ReplaceCallingPkgMethodProxy(\"isVideoCallingEnabled\"));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/user/UserManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.user;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.ResultStaticMethodProxy;\n\nimport java.util.Collections;\n\nimport mirror.android.content.pm.UserInfo;\nimport mirror.android.os.IUserManager;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class UserManagerStub extends BinderInvocationProxy {\n\n    public UserManagerStub() {\n        super(IUserManager.Stub.asInterface, Context.USER_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"setApplicationRestrictions\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getApplicationRestrictions\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getApplicationRestrictionsForUser\"));\n        addMethodProxy(new ResultStaticMethodProxy(\"getProfileParent\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"getUserIcon\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"getUserInfo\", UserInfo.ctor.newInstance(0, \"Admin\", UserInfo.FLAG_PRIMARY.get())));\n        addMethodProxy(new ResultStaticMethodProxy(\"getDefaultGuestRestrictions\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"setDefaultGuestRestrictions\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"removeRestrictions\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"getUsers\", Collections.EMPTY_LIST));\n        addMethodProxy(new ResultStaticMethodProxy(\"createUser\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"createProfileForUser\", null));\n        addMethodProxy(new ResultStaticMethodProxy(\"getProfiles\", Collections.EMPTY_LIST));\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/vibrator/VibratorStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.vibrator;\n\nimport android.content.Context;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\nimport java.lang.reflect.Method;\n\nimport mirror.com.android.internal.os.IVibratorService;\n\n/**\n * @author Lody\n * @see android.os.Vibrator\n */\npublic class VibratorStub extends BinderInvocationProxy {\n\n    public VibratorStub() {\n        super(IVibratorService.Stub.asInterface, Context.VIBRATOR_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        //Samsung  {\n        addMethodProxy(new VibrateMethodProxy(\"vibrateMagnitude\"));\n        addMethodProxy(new VibrateMethodProxy(\"vibratePatternMagnitude\"));\n        // }\n        addMethodProxy(new VibrateMethodProxy(\"vibrate\"));\n        addMethodProxy(new VibrateMethodProxy(\"vibratePattern\"));\n    }\n\n    private final static class VibrateMethodProxy extends ReplaceCallingPkgMethodProxy {\n\n        private VibrateMethodProxy(String name) {\n            super(name);\n        }\n\n        @Override\n        public boolean beforeCall(Object who, Method method, Object... args) {\n            if (args[0] instanceof Integer) {\n                args[0] = getRealUid();\n            }\n            return super.beforeCall(who, method, args);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/wifi/WifiManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.wifi;\n\nimport android.content.Context;\nimport android.net.wifi.WifiInfo;\nimport android.os.Build;\nimport android.os.WorkSource;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.Mark;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.net.wifi.IWifiManager;\n\n/**\n * @author Lody\n * @see android.net.wifi.WifiManager\n */\npublic class WifiManagerStub extends BinderInvocationProxy {\n\n    private class RemoveWorkSourceMethodProxy extends StaticMethodProxy {\n\n        RemoveWorkSourceMethodProxy(String name) {\n            super(name);\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int index = ArrayUtils.indexOfFirst(args, WorkSource.class);\n            if (index >= 0) {\n                args[index] = null;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n\n    public WifiManagerStub() {\n        super(IWifiManager.Stub.asInterface, Context.WIFI_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new GetConnectionInfo());\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getScanResults\"));\n        addMethodProxy(new ReplaceCallingPkgMethodProxy(\"getBatchedScanResults\"));\n        addMethodProxy(new RemoveWorkSourceMethodProxy(\"acquireWifiLock\"));\n        addMethodProxy(new RemoveWorkSourceMethodProxy(\"updateWifiLockWorkSource\"));\n        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {\n            addMethodProxy(new RemoveWorkSourceMethodProxy(\"startLocationRestrictedScan\"));\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            addMethodProxy(new RemoveWorkSourceMethodProxy(\"startScan\"));\n            addMethodProxy(new RemoveWorkSourceMethodProxy(\"requestBatchedScan\"));\n        }\n    }\n\n    @Mark(\"fake wifi MAC\")\n    private final class GetConnectionInfo extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"getConnectionInfo\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            WifiInfo wifiInfo = (WifiInfo) method.invoke(who, args);\n            if (wifiInfo != null) {\n                mirror.android.net.wifi.WifiInfo.mMacAddress.set(wifiInfo, getDeviceInfo().wifiMac);\n            }\n            return wifiInfo;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/wifi_scanner/GhostWifiScannerImpl.java",
    "content": "package com.lody.virtual.client.hook.proxies.wifi_scanner;\n\nimport android.net.wifi.IWifiScanner;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Messenger;\nimport android.os.RemoteException;\n\nimport java.util.ArrayList;\n\nimport mirror.android.net.wifi.WifiScanner;\n\n/**\n * @author Lody\n */\n\npublic class GhostWifiScannerImpl extends IWifiScanner.Stub {\n\n    private final Handler mHandler = new Handler(Looper.getMainLooper());\n\n    @Override\n    public Messenger getMessenger() throws RemoteException {\n        return new Messenger(mHandler);\n    }\n\n    @Override\n    public Bundle getAvailableChannels(int band) throws RemoteException {\n        Bundle bundle = new Bundle();\n        bundle.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA.get(), new ArrayList<Integer>(0));\n        return bundle;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/wifi_scanner/WifiScannerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.wifi_scanner;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\n\n/**\n * @author Lody\n */\n\npublic class WifiScannerStub extends BinderInvocationProxy {\n\n    public WifiScannerStub() {\n        super(new GhostWifiScannerImpl(), \"wifiscanner\");\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/window/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.window;\n\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.proxies.window.session.WindowSessionPatch;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n\nclass MethodProxies {\n\n\n    static class OpenSession extends BasePatchSession {\n\n        @Override\n        public String getMethodName() {\n            return \"openSession\";\n        }\n    }\n\n\n    static class OverridePendingAppTransition extends BasePatchSession {\n\n        @Override\n        public String getMethodName() {\n            return \"overridePendingAppTransition\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (args[0] instanceof String) {\n                args[0] = getHostPkg();\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class OverridePendingAppTransitionInPlace extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"overridePendingAppTransitionInPlace\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (args[0] instanceof String) {\n                args[0] = getHostPkg();\n            }\n            return method.invoke(who, args);\n        }\n    }\n\n\n    static class SetAppStartingWindow extends BasePatchSession {\n\n        @Override\n        public String getMethodName() {\n            return \"setAppStartingWindow\";\n        }\n    }\n\n    abstract static class BasePatchSession extends MethodProxy {\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            Object session = method.invoke(who, args);\n            if (session instanceof IInterface) {\n                return proxySession((IInterface) session);\n            }\n            return session;\n        }\n\n        private Object proxySession(IInterface session) {\n            WindowSessionPatch windowSessionPatch = new WindowSessionPatch(session);\n            return windowSessionPatch.getInvocationStub().getProxyInterface();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/window/WindowManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.window;\n\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\n\nimport mirror.android.view.Display;\nimport mirror.android.view.IWindowManager;\nimport mirror.android.view.WindowManagerGlobal;\nimport mirror.com.android.internal.policy.PhoneWindow;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic class WindowManagerStub extends BinderInvocationProxy {\n\n    public WindowManagerStub() {\n        super(IWindowManager.Stub.asInterface, Context.WINDOW_SERVICE);\n    }\n\n    @Override\n    public void inject() throws Throwable {\n        super.inject();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            if (WindowManagerGlobal.sWindowManagerService != null) {\n                WindowManagerGlobal.sWindowManagerService.set(getInvocationStub().getProxyInterface());\n            }\n        } else {\n            if (Display.sWindowManager != null) {\n                Display.sWindowManager.set(getInvocationStub().getProxyInterface());\n            }\n        }\n        if (PhoneWindow.TYPE != null) {\n            PhoneWindow.sWindowManager.set(getInvocationStub().getProxyInterface());\n        }\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new StaticMethodProxy(\"addAppToken\"));\n        addMethodProxy(new StaticMethodProxy(\"setScreenCaptureDisabled\"));\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/window/session/BaseMethodProxy.java",
    "content": "package com.lody.virtual.client.hook.proxies.window.session;\n\nimport android.view.WindowManager;\n\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\nimport com.lody.virtual.helper.utils.ArrayUtils;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n/*package*/ class BaseMethodProxy extends StaticMethodProxy {\n\n    public BaseMethodProxy(String name) {\n        super(name);\n    }\n\n    @Override\n    public Object call(Object who, Method method, Object... args) throws Throwable {\n        int index = ArrayUtils.indexOfFirst(args, WindowManager.LayoutParams.class);\n        if (index != -1) {\n            WindowManager.LayoutParams attrs = (WindowManager.LayoutParams) args[index];\n            if (attrs != null) {\n                attrs.packageName = getHostPkg();\n            }\n        }\n        return method.invoke(who, args);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/proxies/window/session/WindowSessionPatch.java",
    "content": "package com.lody.virtual.client.hook.proxies.window.session;\n\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.hook.base.MethodInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodInvocationStub;\nimport com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy;\n\n/**\n * @author Lody\n */\npublic class WindowSessionPatch extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {\n\n\tpublic WindowSessionPatch(IInterface session) {\n\t\tsuper(new MethodInvocationStub<>(session));\n\t}\n\n\t@Override\n\tpublic void onBindMethods() {\n\t\taddMethodProxy(new BaseMethodProxy(\"add\"));\n\t\taddMethodProxy(new BaseMethodProxy(\"addToDisplay\"));\n\t\taddMethodProxy(new BaseMethodProxy(\"addToDisplayWithoutInputChannel\"));\n\t\taddMethodProxy(new BaseMethodProxy(\"addWithoutInputChannel\"));\n\t\taddMethodProxy(new BaseMethodProxy(\"relayout\"));\n\t}\n\n\n\t@Override\n\tpublic void inject() throws Throwable {\n\t\t// <EMPTY>\n\t}\n\n\t@Override\n\tpublic boolean isEnvBad() {\n\t\treturn getInvocationStub().getProxyInterface() != null;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/secondary/HackAppUtils.java",
    "content": "package com.lody.virtual.client.hook.secondary;\n\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.ReflectException;\n\n/**\n * @author Lody\n */\n\npublic class HackAppUtils {\n\n    /**\n     * Enable the Log output of QQ.\n     *\n     * @param packageName package name\n     * @param classLoader class loader\n     */\n    public static void enableQQLogOutput(String packageName, ClassLoader classLoader) {\n        if (\"com.tencent.mobileqq\".equals(packageName)) {\n            try {\n                Reflect.on(\"com.tencent.qphone.base.util.QLog\", classLoader).set(\"UIN_REPORTLOG_LEVEL\", 100);\n            } catch (ReflectException e) {\n                // ignore\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/secondary/ProxyServiceFactory.java",
    "content": "package com.lody.virtual.client.hook.secondary;\n\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class ProxyServiceFactory {\n\n\tprivate static final String TAG = ProxyServiceFactory.class.getSimpleName();\n\n\tprivate static Map<String, ServiceFetcher> sHookSecondaryServiceMap = new HashMap<>();\n\n\tstatic {\n\t\tsHookSecondaryServiceMap.put(\"com.google.android.auth.IAuthManagerService\", new ServiceFetcher() {\n\t\t\t@Override\n\t\t\tpublic IBinder getService(final Context context, ClassLoader classLoader, IBinder binder) {\n\t\t\t\treturn new StubBinder(classLoader, binder) {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic InvocationHandler createHandler(Class<?> interfaceClass, final IInterface base) {\n\t\t\t\t\t\treturn new InvocationHandler() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\treturn method.invoke(base, args);\n\t\t\t\t\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\t\t\t\t\tif (e.getCause() != null) {\n\t\t\t\t\t\t\t\t\t\tthrow e.getCause();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t});\n\n\t\tsHookSecondaryServiceMap.put(\"com.android.vending.billing.IInAppBillingService\", new ServiceFetcher() {\n\t\t\t@Override\n\t\t\tpublic IBinder getService(final Context context, ClassLoader classLoader, IBinder binder) {\n\t\t\t\treturn new StubBinder(classLoader, binder) {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic InvocationHandler createHandler(Class<?> interfaceClass, final IInterface base) {\n\t\t\t\t\t\treturn new InvocationHandler() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\treturn method.invoke(base, args);\n\t\t\t\t\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\t\t\t\t\tif (e.getCause() != null) {\n\t\t\t\t\t\t\t\t\t\tthrow e.getCause();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t});\n\n\t\tsHookSecondaryServiceMap.put(\"com.google.android.gms.common.internal.IGmsServiceBroker\", new ServiceFetcher() {\n\t\t\t@Override\n\t\t\tpublic IBinder getService(final Context context, ClassLoader classLoader, IBinder binder) {\n\t\t\t\treturn new StubBinder(classLoader, binder) {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic InvocationHandler createHandler(Class<?> interfaceClass, final IInterface base) {\n\t\t\t\t\t\treturn new InvocationHandler() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\treturn method.invoke(base, args);\n\t\t\t\t\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\t\t\t\t\tif (e.getCause() != null) {\n\t\t\t\t\t\t\t\t\t\tthrow e.getCause();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t};\n\t\t\t}\n\t\t});\n\t}\n\n\n\tpublic static IBinder getProxyService(Context context, ComponentName component, IBinder binder) {\n\t\tif (context == null || binder == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tString description = binder.getInterfaceDescriptor();\n\t\t\tServiceFetcher fetcher = sHookSecondaryServiceMap.get(description);\n\t\t\tif (fetcher != null) {\n\t\t\t\tIBinder res = fetcher.getService(context, context.getClassLoader(), binder);\n\t\t\t\tif (res != null) {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\n\n\n\tprivate interface ServiceFetcher {\n\t\tIBinder getService(Context context, ClassLoader classLoader, IBinder binder);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/secondary/ServiceConnectionDelegate.java",
    "content": "package com.lody.virtual.client.hook.secondary;\n\nimport android.app.IServiceConnection;\nimport android.content.ComponentName;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.server.IBinderDelegateService;\n\n/**\n * @author Lody\n */\n\npublic class ServiceConnectionDelegate extends IServiceConnection.Stub {\n\n    private final static ArrayMap<IBinder, ServiceConnectionDelegate> DELEGATE_MAP = new ArrayMap<>();\n    private IServiceConnection mConn;\n\n    private ServiceConnectionDelegate(IServiceConnection mConn) {\n        this.mConn = mConn;\n    }\n\n    public static ServiceConnectionDelegate getDelegate(IServiceConnection conn) {\n        if(conn instanceof ServiceConnectionDelegate){\n            return (ServiceConnectionDelegate)conn;\n        }\n        IBinder binder = conn.asBinder();\n        ServiceConnectionDelegate delegate = DELEGATE_MAP.get(binder);\n        if (delegate == null) {\n            delegate = new ServiceConnectionDelegate(conn);\n            DELEGATE_MAP.put(binder, delegate);\n        }\n        return delegate;\n    }\n\n    public static ServiceConnectionDelegate removeDelegate(IServiceConnection conn) {\n        return DELEGATE_MAP.remove(conn.asBinder());\n    }\n\n    @Override\n    public void connected(ComponentName name, IBinder service) throws RemoteException {\n        IBinderDelegateService delegateService = IBinderDelegateService.Stub.asInterface(service);\n        if (delegateService != null) {\n            name = delegateService.getComponent();\n            service = delegateService.getService();\n            IBinder proxy = ProxyServiceFactory.getProxyService(VClientImpl.get().getCurrentApplication(), name, service);\n            if (proxy != null) {\n                service = proxy;\n            }\n        }\n        mConn.connected(name, service);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/secondary/StubBinder.java",
    "content": "package com.lody.virtual.client.hook.secondary;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.Parcel;\nimport android.os.RemoteException;\n\nimport java.io.FileDescriptor;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\n\n/**\n * @author Lody\n */\n\nabstract class StubBinder implements IBinder {\n\tprivate ClassLoader mClassLoader;\n\tprivate IBinder mBase;\n\tprivate IInterface mInterface;\n\n\tStubBinder(ClassLoader classLoader, IBinder base) {\n\t\tthis.mClassLoader = classLoader;\n\t\tthis.mBase = base;\n\t}\n\n\t@Override\n\tpublic String getInterfaceDescriptor() throws RemoteException {\n\t\treturn mBase.getInterfaceDescriptor();\n\t}\n\n\t@Override\n\tpublic boolean pingBinder() {\n\t\treturn mBase.pingBinder();\n\t}\n\n\t@Override\n\tpublic boolean isBinderAlive() {\n\t\treturn mBase.isBinderAlive();\n\t}\n\n\n\t/**\n\t * Anti the Proguard.\n\t *\n\t * Search the AidlClass.Stub.asInterface(IBinder) method by the StackTrace.\n\t *\n\t */\n\t@Override\n\tpublic IInterface queryLocalInterface(String descriptor) {\n\t\tif (mInterface == null) {\n\t\t\tStackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();\n\t\t\tif (stackTrace == null || stackTrace.length <= 1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tClass<?> aidlType = null;\n\t\t\tIInterface targetInterface = null;\n\n\t\t\tfor (StackTraceElement element : stackTrace) {\n\t\t\t\tif (element.isNativeMethod()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n                    Method method = mClassLoader.loadClass(element.getClassName())\n                            .getDeclaredMethod(element.getMethodName(), IBinder.class);\n                    if ((method.getModifiers() & Modifier.STATIC) != 0) {\n                        method.setAccessible(true);\n                        Class<?> returnType = method.getReturnType();\n                        if (returnType.isInterface() && IInterface.class.isAssignableFrom(returnType)) {\n                            aidlType = returnType;\n                            targetInterface = (IInterface) method.invoke(null, mBase);\n                        }\n                    }\n                } catch (Exception e) {\n                    // go to the next cycle\n                }\n\t\t\t}\n\t\t\tif (aidlType == null || targetInterface == null) {\n                return null;\n            }\n\t\t\tInvocationHandler handler = createHandler(aidlType, targetInterface);\n\t\t\tmInterface = (IInterface) Proxy.newProxyInstance(mClassLoader, new Class[]{aidlType}, handler);\n\t\t}\n\t\treturn mInterface;\n\n\t}\n\n\tpublic abstract InvocationHandler createHandler(Class<?> interfaceClass, IInterface iInterface);\n\n\n\t@Override\n\tpublic void dump(FileDescriptor fd, String[] args) throws RemoteException {\n\t\tmBase.dump(fd, args);\n\t}\n\n\t@Override\n\tpublic void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {\n\t\tmBase.dumpAsync(fd, args);\n\t}\n\n\t@Override\n\tpublic boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n\t\treturn mBase.transact(code, data, reply, flags);\n\t}\n\n\t@Override\n\tpublic void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {\n\t\tmBase.linkToDeath(recipient, flags);\n\t}\n\n\t@Override\n\tpublic boolean unlinkToDeath(DeathRecipient recipient, int flags) {\n\t\treturn mBase.unlinkToDeath(recipient, flags);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/hook/utils/MethodParameterUtils.java",
    "content": "package com.lody.virtual.client.hook.utils;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.utils.ArrayUtils;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\n\n/**\n * @author Lody\n *\n */\npublic class MethodParameterUtils {\n\n\tpublic static String replaceFirstAppPkg(Object[] args) {\n\t\tif (args == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint index = ArrayUtils.indexOfFirst(args, String.class);\n\t\tif (index != -1) {\n\t\t\tString pkg = (String) args[index];\n\t\t\targs[index] = VirtualCore.get().getHostPkg();\n\t\t\treturn pkg;\n\t\t}\n\t\treturn null;\n\t}\n\n\t// 看上去将包名替换成了 Host\n\tpublic static String replaceLastAppPkg(Object[] args) {\n\t\tint index = ArrayUtils.indexOfLast(args, String.class);\n\t\tif (index != -1) {\n\t\t\tString pkg = (String) args[index];\n\t\t\targs[index] = VirtualCore.get().getHostPkg();\n\t\t\treturn pkg;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String replaceSequenceAppPkg(Object[] args, int sequence) {\n\t\tint index = ArrayUtils.indexOf(args, String.class, sequence);\n\t\tif (index != -1) {\n\t\t\tString pkg = (String) args[index];\n\t\t\targs[index] = VirtualCore.get().getHostPkg();\n\t\t\treturn pkg;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Class<?>[] getAllInterface(Class clazz){\n\t\tHashSet<Class<?>> classes = new HashSet<>();\n\t\tgetAllInterfaces(clazz,classes);\n\t\tClass<?>[] result=new Class[classes.size()];\n\t\tclasses.toArray(result);\n\t\treturn result;\n\t}\n\n\n\tpublic static void getAllInterfaces(Class clazz, HashSet<Class<?>> interfaceCollection) {\n\t\tClass<?>[] classes = clazz.getInterfaces();\n\t\tif (classes.length != 0) {\n\t\t\tinterfaceCollection.addAll(Arrays.asList(classes));\n\t\t}\n\t\tif (clazz.getSuperclass() != Object.class) {\n\t\t\tgetAllInterfaces(clazz.getSuperclass(), interfaceCollection);\n\t\t}\n\t}\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/interfaces/IInjector.java",
    "content": "package com.lody.virtual.client.interfaces;\n\n/**\n * @author Lody\n *\n * The Objects who implemention this interface will be able to inject other object.\n *\n */\npublic interface IInjector {\n\n\t/**\n\t *\n     * Do injection.\n\t * \n\t * @throws Throwable if inject failed\n\t */\n\tvoid inject() throws Throwable;\n\n\t/**\n     * Check if the injection has bad.\n     *\n\t * @return If the injection has bad\n\t */\n\tboolean isEnvBad();\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/ActivityClientRecord.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.app.Activity;\nimport android.content.pm.ActivityInfo;\n\npublic class ActivityClientRecord {\n\tpublic Activity activity;\n\tpublic ActivityInfo info;\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/LocalProxyUtils.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\n\n/**\n * @author Lody\n */\n\npublic class LocalProxyUtils {\n\n    /**\n     * Generates the Proxy instance for a base object, each IPC call will clean its calling identity.\n     * @param interfaceClass interface class\n     * @param base base object\n     * @return proxy object\n     */\n    public static <T> T genProxy(Class<T> interfaceClass, final Object base) {\n        //noinspection ConstantConditions\n        if (true) {\n            return (T) base;\n        }\n        //noinspection unchecked\n        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{ interfaceClass }, new InvocationHandler() {\n            @Override\n            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                try {\n                    return method.invoke(base, args);\n                } catch (Throwable e) {\n                    throw e.getCause() == null ? e : e.getCause();\n                }\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/ProviderCall.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.content.Context;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Parcelable;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.compat.ContentProviderCompat;\n\nimport java.io.Serializable;\n\n/**\n * @author Lody\n *\n */\npublic class ProviderCall {\n\n\tpublic static Bundle call(String authority, String methodName, String arg, Bundle bundle) {\n\t\treturn call(authority, VirtualCore.get().getContext(), methodName, arg, bundle);\n\t}\n\n\tpublic static Bundle call(String authority, Context context, String method, String arg, Bundle bundle) {\n\t\tUri uri = Uri.parse(\"content://\" + authority);\n\t\treturn ContentProviderCompat.call(context, uri, method, arg, bundle);\n\t}\n\n\tpublic static final class Builder {\n\n\t\tprivate Context context;\n\n\t\tprivate Bundle bundle = new Bundle();\n\n\t\tprivate String method;\n\t\tprivate String auth;\n\t\tprivate String arg;\n\n\t\tpublic Builder(Context context, String auth) {\n\t\t\tthis.context = context;\n\t\t\tthis.auth = auth;\n\t\t}\n\n\t\tpublic Builder methodName(String name) {\n\t\t\tthis.method = name;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder arg(String arg) {\n\t\t\tthis.arg = arg;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder addArg(String key, Object value) {\n\t\t\tif (value != null) {\n\t\t\t\t if (value instanceof Boolean) {\n\t\t\t\t\tbundle.putBoolean(key, (Boolean) value);\n\t\t\t\t} else if (value instanceof Integer) {\n\t\t\t\t\tbundle.putInt(key, (Integer) value);\n\t\t\t\t} else if (value instanceof String) {\n\t\t\t\t\tbundle.putString(key, (String) value);\n\t\t\t\t} else if (value instanceof Serializable) {\n\t\t\t\t\tbundle.putSerializable(key, (Serializable) value);\n\t\t\t\t} else if (value instanceof Bundle) {\n\t\t\t\t\tbundle.putBundle(key, (Bundle) value);\n\t\t\t\t} else if (value instanceof Parcelable) {\n\t\t\t\t\tbundle.putParcelable(key, (Parcelable) value);\n\t\t\t\t} else {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Unknown type \" + value.getClass() + \" in Bundle.\");\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Bundle call() {\n\t\t\treturn ProviderCall.call(auth, context, method, arg, bundle);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/ServiceManagerNative.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.compat.BundleCompat;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.server.ServiceCache;\nimport com.lody.virtual.server.interfaces.IServiceFetcher;\n\n/**\n * @author Lody\n */\npublic class ServiceManagerNative {\n\n    public static final String PACKAGE = \"package\";\n    public static final String ACTIVITY = \"activity\";\n    public static final String USER = \"user\";\n    public static final String APP = \"app\";\n    public static final String ACCOUNT = \"account\";\n    public static final String JOB = \"job\";\n    public static final String NOTIFICATION = \"notification\";\n    public static final String VS = \"vs\";\n    public static final String DEVICE = \"device\";\n\n    public static final String SERVICE_DEF_AUTH = \"virtual.service.BinderProvider\";\n    private static final String TAG = ServiceManagerNative.class.getSimpleName();\n    public static String SERVICE_CP_AUTH = \"virtual.service.BinderProvider\";\n\n     // 通过 ContentProvider 传递一个\n    private static IServiceFetcher sFetcher;\n\n    private static IServiceFetcher getServiceFetcher() {\n        if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) {\n            synchronized (ServiceManagerNative.class) {\n                Context context = VirtualCore.get().getContext();\n                Bundle response = new ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName(\"@\").call();\n                if (response != null) {\n                    IBinder binder = BundleCompat.getBinder(response, \"_VA_|_binder_\");\n                    linkBinderDied(binder);\n                    sFetcher = IServiceFetcher.Stub.asInterface(binder);\n                }\n            }\n        }\n        return sFetcher;\n    }\n\n    public static void ensureServerStarted() {\n        new ProviderCall.Builder(VirtualCore.get().getContext(), SERVICE_CP_AUTH).methodName(\"ensure_created\").call();\n    }\n\n    public static void clearServerFetcher() {\n        sFetcher = null;\n    }\n\n    private static void linkBinderDied(final IBinder binder) {\n        IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {\n            @Override\n            public void binderDied() {\n                binder.unlinkToDeath(this, 0);\n            }\n        };\n        try {\n            binder.linkToDeath(deathRecipient, 0);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    // 返回服务的 IBinder 句柄\n    public static IBinder getService(String name) {\n        // 如果是本地服务，直接本地返回\n        if (VirtualCore.get().isServerProcess()) {\n            return ServiceCache.getService(name);\n        }\n        // 通过 ServiceFetcher 的句柄找到远程 Service 的句柄\n        IServiceFetcher fetcher = getServiceFetcher();\n        if (fetcher != null) {\n            try {\n                return fetcher.getService(name);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n        VLog.e(TAG, \"GetService(%s) return null.\", name);\n        return null;\n    }\n\n    public static void addService(String name, IBinder service) {\n        IServiceFetcher fetcher = getServiceFetcher();\n        if (fetcher != null) {\n            try {\n                fetcher.addService(name, service);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n\n    }\n\n    public static void removeService(String name) {\n        IServiceFetcher fetcher = getServiceFetcher();\n        if (fetcher != null) {\n            try {\n                fetcher.removeService(name);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VAccountManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManagerCallback;\nimport android.accounts.AccountManagerFuture;\nimport android.accounts.AuthenticatorDescription;\nimport android.accounts.IAccountManagerResponse;\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.stub.AmsTask;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.server.IAccountManager;\n\nimport static com.lody.virtual.helper.compat.AccountManagerCompat.KEY_ANDROID_PACKAGE_NAME;\n\n/**\n * @author Lody\n */\n\npublic class VAccountManager {\n\n    private static VAccountManager sMgr = new VAccountManager();\n\n    private IAccountManager mRemote;\n\n    public static VAccountManager get() {\n        return sMgr;\n    }\n\n    public IAccountManager getRemote() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (VAccountManager.class) {\n                Object remote = getStubInterface();\n                mRemote = LocalProxyUtils.genProxy(IAccountManager.class, remote);\n            }\n        }\n        return mRemote;\n    }\n\n    private Object getStubInterface() {\n        return IAccountManager.Stub\n                .asInterface(ServiceManagerNative.getService(ServiceManagerNative.ACCOUNT));\n    }\n\n    public AuthenticatorDescription[] getAuthenticatorTypes() {\n        try {\n            return getRemote().getAuthenticatorTypes(VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void removeAccount(IAccountManagerResponse response, Account account, boolean expectActivityLaunch) {\n        try {\n            getRemote().removeAccount(VUserHandle.myUserId(), response, account, expectActivityLaunch);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void getAuthToken(IAccountManagerResponse response, Account account, String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, Bundle loginOptions) {\n        try {\n            getRemote().getAuthToken(VUserHandle.myUserId(), response, account, authTokenType, notifyOnAuthFailure, expectActivityLaunch, loginOptions);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public boolean addAccountExplicitly(Account account, String password, Bundle extras) {\n        try {\n            return getRemote().addAccountExplicitly(VUserHandle.myUserId(), account, password, extras);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public Account[] getAccounts(int userId, String type) {\n        try {\n            return getRemote().getAccounts(userId, type);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public Account[] getAccounts(String type) {\n        try {\n            return getRemote().getAccounts(VUserHandle.myUserId(), type);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String peekAuthToken(Account account, String authTokenType) {\n        try {\n            return getRemote().peekAuthToken(VUserHandle.myUserId(), account, authTokenType);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getPreviousName(Account account) {\n        try {\n            return getRemote().getPreviousName(VUserHandle.myUserId(), account);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void hasFeatures(IAccountManagerResponse response, Account account, String[] features) {\n        try {\n            getRemote().hasFeatures(VUserHandle.myUserId(), response, account, features);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public boolean accountAuthenticated(Account account) {\n        try {\n            return getRemote().accountAuthenticated(VUserHandle.myUserId(), account);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void clearPassword(Account account) {\n        try {\n            getRemote().clearPassword(VUserHandle.myUserId(), account);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void renameAccount(IAccountManagerResponse response, Account accountToRename, String newName) {\n        try {\n            getRemote().renameAccount(VUserHandle.myUserId(), response, accountToRename, newName);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void setPassword(Account account, String password) {\n        try {\n            getRemote().setPassword(VUserHandle.myUserId(), account, password);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void addAccount(int userId, IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn) {\n        try {\n            getRemote().addAccount(userId, response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, optionsIn);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void addAccount(IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn) {\n        try {\n            getRemote().addAccount(VUserHandle.myUserId(), response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, optionsIn);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void updateCredentials(IAccountManagerResponse response, Account account, String authTokenType, boolean expectActivityLaunch, Bundle loginOptions) {\n        try {\n            getRemote().updateCredentials(VUserHandle.myUserId(), response, account, authTokenType, expectActivityLaunch, loginOptions);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public boolean removeAccountExplicitly(Account account) {\n        try {\n            return getRemote().removeAccountExplicitly(VUserHandle.myUserId(), account);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setUserData(Account account, String key, String value) {\n        try {\n            getRemote().setUserData(VUserHandle.myUserId(), account, key, value);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void editProperties(IAccountManagerResponse response, String accountType, boolean expectActivityLaunch) {\n        try {\n            getRemote().editProperties(VUserHandle.myUserId(), response, accountType, expectActivityLaunch);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void getAuthTokenLabel(IAccountManagerResponse response, String accountType, String authTokenType) {\n        try {\n            getRemote().getAuthTokenLabel(VUserHandle.myUserId(), response, accountType, authTokenType);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void confirmCredentials(IAccountManagerResponse response, Account account, Bundle options, boolean expectActivityLaunch) {\n        try {\n            getRemote().confirmCredentials(VUserHandle.myUserId(), response, account, options, expectActivityLaunch);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void invalidateAuthToken(String accountType, String authToken) {\n        try {\n            getRemote().invalidateAuthToken(VUserHandle.myUserId(), accountType, authToken);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void getAccountsByFeatures(IAccountManagerResponse response, String type, String[] features) {\n        try {\n            getRemote().getAccountsByFeatures(VUserHandle.myUserId(), response, type, features);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void setAuthToken(Account account, String authTokenType, String authToken) {\n        try {\n            getRemote().setAuthToken(VUserHandle.myUserId(), account, authTokenType, authToken);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public Object getPassword(Account account) {\n        try {\n            return getRemote().getPassword(VUserHandle.myUserId(), account);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getUserData(Account account, String key) {\n        try {\n            return getRemote().getUserData(VUserHandle.myUserId(), account, key);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    /**\n     * Asks the user to add an account of a specified type.  The authenticator\n     * for this account type processes this request with the appropriate user\n     * interface.  If the user does elect to create a new account, the account\n     * name is returned.\n     * <p>\n     * <p>This method may be called from any thread, but the returned\n     * {@link AccountManagerFuture} must not be used on the main thread.\n     * <p>\n     *\n     */\n    public AccountManagerFuture<Bundle> addAccount(final int userId, final String accountType,\n                                                   final String authTokenType, final String[] requiredFeatures,\n                                                   final Bundle addAccountOptions,\n                                                   final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {\n        if (accountType == null) throw new IllegalArgumentException(\"accountType is null\");\n        final Bundle optionsIn = new Bundle();\n        if (addAccountOptions != null) {\n            optionsIn.putAll(addAccountOptions);\n        }\n        optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, \"android\");\n\n        return new AmsTask(activity, handler, callback) {\n            @Override\n            public void doWork() throws RemoteException {\n                addAccount(userId, mResponse, accountType, authTokenType,\n                        requiredFeatures, activity != null, optionsIn);\n            }\n        }.start();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VActivityManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.app.Activity;\nimport android.app.IServiceConnection;\nimport android.app.Notification;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ProviderInfo;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.compat.ActivityManagerCompat;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.AppTaskInfo;\nimport com.lody.virtual.remote.PendingIntentData;\nimport com.lody.virtual.remote.PendingResultData;\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.IActivityManager;\nimport com.lody.virtual.server.interfaces.IProcessObserver;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport mirror.android.app.ActivityThread;\nimport mirror.android.content.ContentProviderNative;\n\n/**\n * @author Lody\n */\npublic class VActivityManager {\n\n    private static final VActivityManager sAM = new VActivityManager();\n    private final Map<IBinder, ActivityClientRecord> mActivities = new HashMap<IBinder, ActivityClientRecord>(6);\n    private IActivityManager mRemote;\n\n    public static VActivityManager get() {\n        return sAM;\n    }\n\n    public IActivityManager getService() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (VActivityManager.class) {\n                final Object remote = getRemoteInterface();\n                // 不明白为什么做代理\n                mRemote = LocalProxyUtils.genProxy(IActivityManager.class, remote);\n            }\n        }\n        return mRemote;\n    }\n\n    // 返回远程 VAMS\n    private Object getRemoteInterface() {\n        return IActivityManager.Stub\n                .asInterface(ServiceManagerNative.getService(ServiceManagerNative.ACTIVITY));\n    }\n\n\n    public int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) {\n        try {\n            // 调用服务端 VAMS\n            return getService().startActivity(intent, info, resultTo, options, resultWho, requestCode, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int startActivities(Intent[] intents, String[] resolvedTypes, IBinder token, Bundle options, int userId) {\n        try {\n            return getService().startActivities(intents, resolvedTypes, token, options, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int startActivity(Intent intent, int userId) {\n        if (userId < 0) {\n            return ActivityManagerCompat.START_NOT_CURRENT_USER_ACTIVITY;\n        }\n        ActivityInfo info = VirtualCore.get().resolveActivityInfo(intent, userId);\n        if (info == null) {\n            return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;\n        }\n        return startActivity(intent, info, null, null, null, 0, userId);\n    }\n\n    public ActivityClientRecord onActivityCreate(ComponentName component, ComponentName caller, IBinder token, ActivityInfo info, Intent intent, String affinity, int taskId, int launchMode, int flags) {\n        ActivityClientRecord r = new ActivityClientRecord();\n        r.info = info;\n        mActivities.put(token, r);\n        try {\n            // 通知服务端 VAMS Activity 创建完成\n            getService().onActivityCreated(component, caller, token, intent, affinity, taskId, launchMode, flags);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        return r;\n    }\n\n    public ActivityClientRecord getActivityRecord(IBinder token) {\n        synchronized (mActivities) {\n            return token == null ? null : mActivities.get(token);\n        }\n    }\n\n    public void onActivityResumed(Activity activity) {\n        IBinder token = mirror.android.app.Activity.mToken.get(activity);\n        try {\n            getService().onActivityResumed(VUserHandle.myUserId(), token);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public boolean onActivityDestroy(IBinder token) {\n        mActivities.remove(token);\n        try {\n            return getService().onActivityDestroyed(VUserHandle.myUserId(), token);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public AppTaskInfo getTaskInfo(int taskId) {\n        try {\n            return getService().getTaskInfo(taskId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ComponentName getCallingActivity(IBinder token) {\n        try {\n            return getService().getCallingActivity(VUserHandle.myUserId(), token);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getCallingPackage(IBinder token) {\n        try {\n            return getService().getCallingPackage(VUserHandle.myUserId(), token);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getPackageForToken(IBinder token) {\n        try {\n            return getService().getPackageForToken(VUserHandle.myUserId(), token);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ComponentName getActivityForToken(IBinder token) {\n        try {\n            return getService().getActivityClassForToken(VUserHandle.myUserId(), token);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ComponentName startService(IInterface caller, Intent service, String resolvedType, int userId) {\n        try {\n            return getService().startService(caller != null ? caller.asBinder() : null, service, resolvedType, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int stopService(IInterface caller, Intent service, String resolvedType) {\n        try {\n            return getService().stopService(caller != null ? caller.asBinder() : null, service, resolvedType, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean stopServiceToken(ComponentName className, IBinder token, int startId) {\n        try {\n            return getService().stopServiceToken(className, token, startId, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setServiceForeground(ComponentName className, IBinder token, int id, Notification notification, boolean removeNotification) {\n        try {\n            getService().setServiceForeground(className, token, id, notification,removeNotification,  VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public int bindService(IBinder caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, int userId) {\n        try {\n            return getService().bindService(caller, token, service, resolvedType, connection, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean unbindService(IServiceConnection connection) {\n        try {\n            return getService().unbindService(connection, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void unbindFinished(IBinder token, Intent service, boolean doRebind) {\n        try {\n            getService().unbindFinished(token, service, doRebind, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {\n        try {\n            getService().serviceDoneExecuting(token, type, startId, res, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public IBinder peekService(Intent service, String resolvedType) {\n        try {\n            return getService().peekService(service, resolvedType, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void publishService(IBinder token, Intent intent, IBinder service) {\n        try {\n            getService().publishService(token, intent, service, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public VParceledListSlice getServices(int maxNum, int flags) {\n        try {\n            return getService().getServices(maxNum, flags, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void processRestarted(String packageName, String processName, int userId) {\n        try {\n            getService().processRestarted(packageName, processName, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public String getAppProcessName(int pid) {\n        try {\n            return getService().getAppProcessName(pid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getInitialPackage(int pid) {\n        try {\n            return getService().getInitialPackage(pid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isAppProcess(String processName) {\n        try {\n            return getService().isAppProcess(processName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void handleApplicationCrash() {\n        try {\n            getService().handleApplicationCrash();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void killAllApps() {\n        try {\n            getService().killAllApps();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void killApplicationProcess(String procName, int uid) {\n        try {\n            getService().killApplicationProcess(procName, uid);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void registerProcessObserver(IProcessObserver observer) {\n        try {\n            getService().registerProcessObserver(observer);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void killAppByPkg(String pkg, int userId) {\n        try {\n            getService().killAppByPkg(pkg, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void unregisterProcessObserver(IProcessObserver observer) {\n        try {\n            getService().unregisterProcessObserver(observer);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void appDoneExecuting() {\n        try {\n            getService().appDoneExecuting();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public List<String> getProcessPkgList(int pid) {\n        try {\n            return getService().getProcessPkgList(pid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isAppPid(int pid) {\n        try {\n            return getService().isAppPid(pid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int getUidByPid(int pid) {\n        try {\n            return getService().getUidByPid(pid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int getSystemPid() {\n        try {\n            return getService().getSystemPid();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    // 发送给请求 Activity start 结果\n    public void sendActivityResult(IBinder resultTo, String resultWho, int requestCode) {\n        ActivityClientRecord r = mActivities.get(resultTo);\n        if (r != null && r.activity != null) {\n            Object mainThread = VirtualCore.mainThread();\n            ActivityThread.sendActivityResult.call(mainThread, resultTo, resultWho, requestCode, 0, null);\n        }\n    }\n\n    public IInterface acquireProviderClient(int userId, ProviderInfo info) throws RemoteException {\n        return ContentProviderNative.asInterface.call(getService().acquireProviderClient(userId, info));\n    }\n\n    public PendingIntentData getPendingIntent(IBinder binder) throws RemoteException {\n        return getService().getPendingIntent(binder);\n    }\n\n    public void addPendingIntent(IBinder binder, String creator) throws RemoteException {\n        getService().addPendingIntent(binder, creator);\n    }\n\n    public void removePendingIntent(IBinder binder) throws RemoteException {\n        getService().removePendingIntent(binder);\n    }\n\n    public void finishActivity(IBinder token) {\n        ActivityClientRecord r = getActivityRecord(token);\n        if (r != null) {\n            Activity activity = r.activity;\n            while (true) {\n                // We shouldn't use Activity.getParent(),\n                // because It may be overwritten.\n                Activity parent = mirror.android.app.Activity.mParent.get(activity);\n                if (parent == null) {\n                    break;\n                }\n                activity = parent;\n            }\n            // We shouldn't use Activity.isFinishing(),\n            // because It may be overwritten.\n            if (!mirror.android.app.Activity.mFinished.get(activity)) {\n                int resultCode = mirror.android.app.Activity.mResultCode.get(activity);\n                Intent resultData = mirror.android.app.Activity.mResultData.get(activity);\n                ActivityManagerCompat.finishActivity(token, resultCode, resultData);\n                mirror.android.app.Activity.mFinished.set(activity, true);\n            }\n        }\n    }\n\n    public boolean isAppRunning(String packageName, int userId) {\n        try {\n            return getService().isAppRunning(packageName, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int initProcess(String packageName, String processName, int userId) {\n        try {\n            return getService().initProcess(packageName, processName, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void sendBroadcast(Intent intent, int userId) {\n        Intent newIntent = ComponentUtils.redirectBroadcastIntent(intent, userId);\n        if (newIntent != null) {\n            VirtualCore.get().getContext().sendBroadcast(newIntent);\n        }\n    }\n\n    public boolean isVAServiceToken(IBinder token) {\n        try {\n            return getService().isVAServiceToken(token);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void broadcastFinish(PendingResultData res) {\n        try {\n            getService().broadcastFinish(res);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getPackageForIntentSender(IBinder binder) {\n        try {\n            return getService().getPackageForIntentSender(binder);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VDeviceManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.remote.VDeviceInfo;\nimport com.lody.virtual.server.IDeviceInfoManager;\n\n/**\n * @author Lody\n */\n\npublic class VDeviceManager {\n\n    private static final VDeviceManager sInstance = new VDeviceManager();\n    private IDeviceInfoManager mRemote;\n\n\n    public static VDeviceManager get() {\n        return sInstance;\n    }\n\n\n    public IDeviceInfoManager getRemote() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (this) {\n                Object remote = getRemoteInterface();\n                mRemote = LocalProxyUtils.genProxy(IDeviceInfoManager.class, remote);\n            }\n        }\n        return mRemote;\n    }\n\n    private Object getRemoteInterface() {\n        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.DEVICE);\n        return IDeviceInfoManager.Stub.asInterface(binder);\n    }\n\n    public VDeviceInfo getDeviceInfo(int userId) {\n        try {\n            return getRemote().getDeviceInfo(userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VJobScheduler.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.app.job.JobInfo;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.server.IJobScheduler;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\n\npublic class VJobScheduler {\n\n    private static final VJobScheduler sInstance = new VJobScheduler();\n\n    private IJobScheduler mRemote;\n\n    public static VJobScheduler get() {\n        return sInstance;\n    }\n\n    public IJobScheduler getRemote() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (this) {\n                Object remote = getRemoteInterface();\n                mRemote = LocalProxyUtils.genProxy(IJobScheduler.class, remote);\n            }\n        }\n        return mRemote;\n    }\n\n    private Object getRemoteInterface() {\n        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.JOB);\n        return IJobScheduler.Stub.asInterface(binder);\n    }\n\n    public int schedule(JobInfo job) {\n        try {\n            return getRemote().schedule(job);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<JobInfo> getAllPendingJobs() {\n        try {\n            return getRemote().getAllPendingJobs();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void cancelAll() {\n        try {\n            getRemote().cancelAll();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void cancel(int jobId) {\n        try {\n            getRemote().cancel(jobId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VNotificationManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.app.Notification;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.server.INotificationManager;\nimport com.lody.virtual.server.notification.NotificationCompat;\n\n/**\n * Fake notification manager\n */\npublic class VNotificationManager {\n    private static final VNotificationManager sInstance = new VNotificationManager();\n    private final NotificationCompat mNotificationCompat;\n    private INotificationManager mRemote;\n\n    private VNotificationManager() {\n        mNotificationCompat = NotificationCompat.create();\n    }\n\n    public static VNotificationManager get() {\n        return sInstance;\n    }\n\n    public INotificationManager getService() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (VNotificationManager.class) {\n                final IBinder pmBinder = ServiceManagerNative.getService(ServiceManagerNative.NOTIFICATION);\n                mRemote = INotificationManager.Stub.asInterface(pmBinder);\n            }\n        }\n        return mRemote;\n    }\n\n    public boolean dealNotification(int id, Notification notification, String packageName) {\n        return VirtualCore.get().getHostPkg().equals(packageName)\n                || mNotificationCompat.dealNotification(id, notification, packageName);\n    }\n\n    public int dealNotificationId(int id, String packageName, String tag, int userId) {\n        try {\n            return getService().dealNotificationId(id, packageName, tag, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        return id;\n    }\n\n    public String dealNotificationTag(int id, String packageName, String tag, int userId) {\n        try {\n            return getService().dealNotificationTag(id, packageName, tag, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        return tag;\n    }\n\n    public boolean areNotificationsEnabledForPackage(String packageName, int userId) {\n        try {\n            return getService().areNotificationsEnabledForPackage(packageName, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n            return true;\n        }\n    }\n\n    public void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId) {\n        try {\n            getService().setNotificationsEnabledForPackage(packageName, enable, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void addNotification(int id, String tag, String packageName, int userId) {\n        try {\n            getService().addNotification(id, tag, packageName, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void cancelAllNotification(String packageName, int userId) {\n        try {\n            getService().cancelAllNotification(packageName, userId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PermissionGroupInfo;\nimport android.content.pm.PermissionInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.server.IPackageInstaller;\nimport com.lody.virtual.server.IPackageManager;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic class VPackageManager {\n\n    private static final VPackageManager sMgr = new VPackageManager();\n    private IPackageManager mRemote;\n\n    public static VPackageManager get() {\n        return sMgr;\n    }\n\n    public IPackageManager getInterface() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (VPackageManager.class) {\n                Object remote = getRemoteInterface();\n                mRemote = LocalProxyUtils.genProxy(IPackageManager.class, remote);\n            }\n        }\n        return mRemote;\n    }\n\n    private Object getRemoteInterface() {\n        final IBinder pmBinder = ServiceManagerNative.getService(ServiceManagerNative.PACKAGE);\n        return IPackageManager.Stub.asInterface(pmBinder);\n    }\n\n    public int checkPermission(String permName, String pkgName, int userId) {\n        try {\n            return getInterface().checkPermission(permName, pkgName, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            return getInterface().resolveService(intent, resolvedType, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {\n        try {\n            return getInterface().getPermissionGroupInfo(name, flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<ApplicationInfo> getInstalledApplications(int flags, int userId) {\n        try {\n            // noinspection unchecked\n            return getInterface().getInstalledApplications(flags, userId).getList();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public PackageInfo getPackageInfo(String packageName, int flags, int userId) {\n        try {\n            return getInterface().getPackageInfo(packageName, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            return getInterface().resolveIntent(intent, resolvedType, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            return getInterface().queryIntentContentProviders(intent, resolvedType, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ActivityInfo getReceiverInfo(ComponentName componentName, int flags, int userId) {\n        try {\n            return getInterface().getReceiverInfo(componentName, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<PackageInfo> getInstalledPackages(int flags, int userId) {\n        try {\n            return getInterface().getInstalledPackages(flags, userId).getList();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {\n        try {\n            return getInterface().queryPermissionsByGroup(group, flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public PermissionInfo getPermissionInfo(String name, int flags) {\n        try {\n            return getInterface().getPermissionInfo(name, flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ActivityInfo getActivityInfo(ComponentName componentName, int flags, int userId) {\n        try {\n            return getInterface().getActivityInfo(componentName, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            return getInterface().queryIntentReceivers(intent, resolvedType, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {\n        try {\n            return getInterface().getAllPermissionGroups(flags);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            return getInterface().queryIntentActivities(intent, resolvedType, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) {\n        try {\n            return getInterface().queryIntentServices(intent, resolvedType, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {\n        try {\n            return getInterface().getApplicationInfo(packageName, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ProviderInfo resolveContentProvider(String name, int flags, int userId) {\n        try {\n            return getInterface().resolveContentProvider(name, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ServiceInfo getServiceInfo(ComponentName componentName, int flags, int userId) {\n        try {\n            return getInterface().getServiceInfo(componentName, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public ProviderInfo getProviderInfo(ComponentName componentName, int flags, int userId) {\n        try {\n            return getInterface().getProviderInfo(componentName, flags, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) {\n        try {\n            return getInterface().activitySupportsIntent(component, intent, resolvedType);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {\n        try {\n            // noinspection unchecked\n            return getInterface().queryContentProviders(processName, uid, flags).getList();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<String> querySharedPackages(String packageName) {\n        try {\n            return getInterface().querySharedPackages(packageName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String[] getPackagesForUid(int uid) {\n        try {\n            return getInterface().getPackagesForUid(uid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int getPackageUid(String packageName, int userId) {\n        try {\n            return getInterface().getPackageUid(packageName, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getNameForUid(int uid) {\n        try {\n            return getInterface().getNameForUid(uid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n\n    public IPackageInstaller getPackageInstaller() {\n        try {\n            return getInterface().getPackageInstaller();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/ipc/VirtualStorageManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\n\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.server.IVirtualStorageService;\n\n/**\n * @author Lody\n */\n\npublic class VirtualStorageManager {\n\n    private static final VirtualStorageManager sInstance = new VirtualStorageManager();\n    private IVirtualStorageService mRemote;\n\n\n    public static VirtualStorageManager get() {\n        return sInstance;\n    }\n\n\n    public IVirtualStorageService getRemote() {\n        if (mRemote == null ||\n                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {\n            synchronized (this) {\n                Object remote = getRemoteInterface();\n                mRemote = LocalProxyUtils.genProxy(IVirtualStorageService.class, remote);\n            }\n        }\n        return mRemote;\n    }\n\n    private Object getRemoteInterface() {\n        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.VS);\n        return IVirtualStorageService.Stub.asInterface(binder);\n    }\n\n    public void setVirtualStorage(String packageName, int userId, String vsPath) {\n        try {\n            getRemote().setVirtualStorage(packageName, userId, vsPath);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public String getVirtualStorage(String packageName, int userId) {\n        try {\n            return getRemote().getVirtualStorage(packageName, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setVirtualStorageState(String packageName, int userId, boolean enable) {\n        try {\n            getRemote().setVirtualStorageState(packageName, userId, enable);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean isVirtualStorageEnable(String packageName, int userId) {\n        try {\n            return getRemote().isVirtualStorageEnable(packageName, userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/natives/NativeMethods.java",
    "content": "package com.lody.virtual.client.natives;\n\nimport android.hardware.Camera;\nimport android.media.AudioRecord;\nimport android.os.Build;\n\nimport java.lang.reflect.Method;\n\nimport dalvik.system.DexFile;\n\n/**\n * @author Lody\n */\npublic class NativeMethods {\n\n    public static int gCameraMethodType;\n    public static Method gCameraNativeSetup;\n\n    public static Method gOpenDexFileNative;\n\n    public static Method gAudioRecordNativeCheckPermission;\n\n    public static void init() {\n        String methodName =\n                Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? \"openDexFileNative\" : \"openDexFile\";\n        for (Method method : DexFile.class.getDeclaredMethods()) {\n            if (method.getName().equals(methodName)) {\n                gOpenDexFileNative = method;\n                break;\n            }\n        }\n        if (gOpenDexFileNative == null) {\n            throw new RuntimeException(\"Unable to find method : \" + methodName);\n        }\n        gOpenDexFileNative.setAccessible(true);\n\n        gCameraMethodType = -1;\n        try {\n            gCameraNativeSetup = Camera.class.getDeclaredMethod(\"native_setup\", Object.class, int.class, String.class);\n            gCameraMethodType = 1;\n        } catch (NoSuchMethodException e) {\n            // ignore\n        }\n        if (gCameraNativeSetup == null) {\n            try {\n                gCameraNativeSetup = Camera.class.getDeclaredMethod(\"native_setup\", Object.class, int.class, int.class, String.class);\n                gCameraMethodType = 2;\n            } catch (NoSuchMethodException e) {\n                // ignore\n            }\n        }\n        // HuaWei common\n        if (gCameraNativeSetup == null) {\n            try {\n                gCameraNativeSetup = Camera.class.getDeclaredMethod(\"native_setup\", Object.class, int.class, int.class, String.class, boolean.class);\n                gCameraMethodType = 3;\n            } catch (NoSuchMethodException e) {\n                // ignore\n            }\n        }\n        // HUAWEI MediaPad X1 7.0\n        if (gCameraNativeSetup == null) {\n            try {\n                gCameraNativeSetup = Camera.class.getDeclaredMethod(\"native_setup\", Object.class, int.class, String.class, boolean.class);\n                gCameraMethodType = 4;\n            } catch (NoSuchMethodException e) {\n                // ignore\n            }\n        }\n        if (gCameraNativeSetup != null) {\n            gCameraNativeSetup.setAccessible(true);\n        }\n\n        for (Method mth : AudioRecord.class.getDeclaredMethods()) {\n            if (mth.getName().equals(\"native_check_permission\")\n                    && mth.getParameterTypes().length == 1\n                    && mth.getParameterTypes()[0] == String.class) {\n                gAudioRecordNativeCheckPermission = mth;\n                mth.setAccessible(true);\n                break;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/AmsTask.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.accounts.AccountManagerCallback;\nimport android.accounts.AccountManagerFuture;\nimport android.accounts.AuthenticatorException;\nimport android.accounts.IAccountManagerResponse;\nimport android.accounts.OperationCanceledException;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.io.IOException;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.FutureTask;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport static android.accounts.AccountManager.ERROR_CODE_BAD_ARGUMENTS;\nimport static android.accounts.AccountManager.ERROR_CODE_CANCELED;\nimport static android.accounts.AccountManager.ERROR_CODE_INVALID_RESPONSE;\nimport static android.accounts.AccountManager.ERROR_CODE_NETWORK_ERROR;\nimport static android.accounts.AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION;\nimport static android.accounts.AccountManager.KEY_INTENT;\nimport static com.lody.virtual.helper.compat.AccountManagerCompat.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE;\nimport static com.lody.virtual.helper.compat.AccountManagerCompat.ERROR_CODE_USER_RESTRICTED;\n\npublic abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {\n    protected final IAccountManagerResponse mResponse;\n    final Handler mHandler;\n    final AccountManagerCallback<Bundle> mCallback;\n    final Activity mActivity;\n\n    public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {\n        super(new Callable<Bundle>() {\n            @Override\n            public Bundle call() throws Exception {\n                throw new IllegalStateException(\"this should never be called\");\n            }\n        });\n\n        mHandler = handler;\n        mCallback = callback;\n        mActivity = activity;\n        mResponse = new Response();\n    }\n\n    public final AccountManagerFuture<Bundle> start() {\n        try {\n            doWork();\n        } catch (RemoteException e) {\n            setException(e);\n        }\n        return this;\n    }\n\n    @Override\n    protected void set(Bundle bundle) {\n        // TODO: somehow a null is being set as the result of the Future. Log this\n        // case to help debug where this is occurring. When this bug is fixed this\n        // condition statement should be removed.\n        if (bundle == null) {\n            VLog.e(\"AccountManager\", \"the bundle must not be null\", new Exception());\n\n        }\n        super.set(bundle);\n    }\n\n    public abstract void doWork() throws RemoteException;\n\n    private Bundle internalGetResult(Long timeout, TimeUnit unit)\n            throws OperationCanceledException, IOException, AuthenticatorException {\n        try {\n            if (timeout == null) {\n                return get();\n            } else {\n                return get(timeout, unit);\n            }\n        } catch (CancellationException e) {\n            throw new OperationCanceledException();\n        } catch (TimeoutException e) {\n            // fall through and cancel\n        } catch (InterruptedException e) {\n            // fall through and cancel\n        } catch (ExecutionException e) {\n            final Throwable cause = e.getCause();\n            if (cause instanceof IOException) {\n                throw (IOException) cause;\n            } else if (cause instanceof UnsupportedOperationException) {\n                throw new AuthenticatorException(cause);\n            } else if (cause instanceof AuthenticatorException) {\n                throw (AuthenticatorException) cause;\n            } else if (cause instanceof RuntimeException) {\n                throw (RuntimeException) cause;\n            } else if (cause instanceof Error) {\n                throw (Error) cause;\n            } else {\n                throw new IllegalStateException(cause);\n            }\n        } finally {\n            cancel(true /* interrupt if running */);\n        }\n        throw new OperationCanceledException();\n    }\n\n    @Override\n    public Bundle getResult()\n            throws OperationCanceledException, IOException, AuthenticatorException {\n        return internalGetResult(null, null);\n    }\n\n    @Override\n    public Bundle getResult(long timeout, TimeUnit unit)\n            throws OperationCanceledException, IOException, AuthenticatorException {\n        return internalGetResult(timeout, unit);\n    }\n\n    @Override\n    protected void done() {\n        if (mCallback != null) {\n            postToHandler(mHandler, mCallback, this);\n        }\n    }\n\n    /**\n     * Handles the responses from the AccountManager\n     */\n    private class Response extends IAccountManagerResponse.Stub {\n        @Override\n        public void onResult(Bundle bundle) {\n            Intent intent = bundle.getParcelable(KEY_INTENT);\n            if (intent != null && mActivity != null) {\n                // since the user provided an Activity we will silently start intents\n                // that we see\n                mActivity.startActivity(intent);\n                // leave the Future running to wait for the real response to this request\n            } else if (bundle.getBoolean(\"retry\")) {\n                try {\n                    doWork();\n                } catch (RemoteException e) {\n                    throw new RuntimeException(e);\n                }\n            } else {\n                set(bundle);\n            }\n        }\n\n        @Override\n        public void onError(int code, String message) {\n            if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED\n                    || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {\n                // the authenticator indicated that this request was canceled or we were\n                // forbidden to fulfill; cancel now\n                cancel(true /* mayInterruptIfRunning */);\n                return;\n            }\n            setException(convertErrorToException(code, message));\n        }\n    }\n\n    private Exception convertErrorToException(int code, String message) {\n        if (code == ERROR_CODE_NETWORK_ERROR) {\n            return new IOException(message);\n        }\n\n        if (code == ERROR_CODE_UNSUPPORTED_OPERATION) {\n            return new UnsupportedOperationException(message);\n        }\n\n        if (code == ERROR_CODE_INVALID_RESPONSE) {\n            return new AuthenticatorException(message);\n        }\n\n        if (code == ERROR_CODE_BAD_ARGUMENTS) {\n            return new IllegalArgumentException(message);\n        }\n\n        return new AuthenticatorException(message);\n    }\n\n    private void postToHandler(Handler handler, final AccountManagerCallback<Bundle> callback,\n                               final AccountManagerFuture<Bundle> future) {\n        handler = handler == null ? VirtualRuntime.getUIHandler() : handler;\n        handler.post(new Runnable() {\n            @Override\n            public void run() {\n                callback.run(future);\n            }\n        });\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/ChooseAccountTypeActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.accounts.AccountManager;\nimport android.accounts.AuthenticatorDescription;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Resources;\nimport android.graphics.drawable.Drawable;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.VAccountManager;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @hide\n */\npublic class ChooseAccountTypeActivity extends Activity {\n    private static final String TAG = \"AccountChooser\";\n\n    private HashMap<String, AuthInfo> mTypeToAuthenticatorInfo = new HashMap<String, AuthInfo>();\n    private ArrayList<AuthInfo> mAuthenticatorInfosToDisplay;\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        // Read the validAccountTypes, if present, and add them to the setOfAllowableAccountTypes\n        Set<String> setOfAllowableAccountTypes = null;\n        String[] validAccountTypes = getIntent().getStringArrayExtra(\n                ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);\n        if (validAccountTypes != null) {\n            setOfAllowableAccountTypes = new HashSet<>(validAccountTypes.length);\n            Collections.addAll(setOfAllowableAccountTypes, validAccountTypes);\n        }\n\n        // create a map of account authenticators\n        buildTypeToAuthDescriptionMap();\n\n        // Create a list of authenticators that are allowable. Filter out those that\n        // don't match the allowable account types, if provided.\n        mAuthenticatorInfosToDisplay = new ArrayList<>(mTypeToAuthenticatorInfo.size());\n        for (Map.Entry<String, AuthInfo> entry: mTypeToAuthenticatorInfo.entrySet()) {\n            final String type = entry.getKey();\n            final AuthInfo info = entry.getValue();\n            if (setOfAllowableAccountTypes != null\n                    && !setOfAllowableAccountTypes.contains(type)) {\n                continue;\n            }\n            mAuthenticatorInfosToDisplay.add(info);\n        }\n\n        if (mAuthenticatorInfosToDisplay.isEmpty()) {\n            Bundle bundle = new Bundle();\n            bundle.putString(AccountManager.KEY_ERROR_MESSAGE, \"no allowable account types\");\n            setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));\n            finish();\n            return;\n        }\n\n        if (mAuthenticatorInfosToDisplay.size() == 1) {\n            setResultAndFinish(mAuthenticatorInfosToDisplay.get(0).desc.type);\n            return;\n        }\n\n        setContentView(R.layout.choose_account_type);\n        // Setup the list\n        ListView list = (ListView) findViewById(android.R.id.list);\n        // Use an existing ListAdapter that will map an array of strings to TextViews\n        list.setAdapter(new AccountArrayAdapter(this,\n                android.R.layout.simple_list_item_1, mAuthenticatorInfosToDisplay));\n        list.setChoiceMode(ListView.CHOICE_MODE_NONE);\n        list.setTextFilterEnabled(false);\n        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            public void onItemClick(AdapterView<?> parent, View v, int position, long id) {\n                setResultAndFinish(mAuthenticatorInfosToDisplay.get(position).desc.type);\n            }\n        });\n    }\n\n    private void setResultAndFinish(final String type) {\n        Bundle bundle = new Bundle();\n        bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, type);\n        setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));\n        VLog.v(TAG, \"ChooseAccountTypeActivity.setResultAndFinish: \"\n                + \"selected account type \" + type);\n        finish();\n    }\n\n    private void buildTypeToAuthDescriptionMap() {\n        for(AuthenticatorDescription desc : VAccountManager.get().getAuthenticatorTypes()) {\n            String name = null;\n            Drawable icon = null;\n            try {\n                Resources res = VirtualCore.get().getResources(desc.packageName);\n                icon = res.getDrawable(desc.iconId);\n                final CharSequence sequence = res.getText(desc.labelId);\n                name = sequence.toString();\n                name = sequence.toString();\n            } catch (Resources.NotFoundException e) {\n                // Nothing we can do much here, just log\n                VLog.w(TAG, \"No icon resource for account type \" + desc.type);\n            }\n            AuthInfo authInfo = new AuthInfo(desc, name, icon);\n            mTypeToAuthenticatorInfo.put(desc.type, authInfo);\n        }\n    }\n\n    private static class AuthInfo {\n        final AuthenticatorDescription desc;\n        final String name;\n        final Drawable drawable;\n\n        AuthInfo(AuthenticatorDescription desc, String name, Drawable drawable) {\n            this.desc = desc;\n            this.name = name;\n            this.drawable = drawable;\n        }\n    }\n\n    private static class ViewHolder {\n        ImageView icon;\n        TextView text;\n    }\n\n    private static class AccountArrayAdapter extends ArrayAdapter<AuthInfo> {\n        private LayoutInflater mLayoutInflater;\n        private ArrayList<AuthInfo> mInfos;\n\n        AccountArrayAdapter(Context context, int textViewResourceId,\n                            ArrayList<AuthInfo> infos) {\n            super(context, textViewResourceId, infos);\n            mInfos = infos;\n            mLayoutInflater = (LayoutInflater) context.getSystemService(\n                    Context.LAYOUT_INFLATER_SERVICE);\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            ViewHolder holder;\n\n            if (convertView == null) {\n                convertView = mLayoutInflater.inflate(R.layout.choose_account_row, null);\n                holder = new ViewHolder();\n                holder.text = (TextView) convertView.findViewById(R.id.account_row_text);\n                holder.icon = (ImageView) convertView.findViewById(R.id.account_row_icon);\n                convertView.setTag(holder);\n            } else {\n                holder = (ViewHolder) convertView.getTag();\n            }\n\n            holder.text.setText(mInfos.get(position).name);\n            holder.icon.setImageDrawable(mInfos.get(position).drawable);\n\n            return convertView;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/ChooseTypeAndAccountActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\n\nimport android.accounts.Account;\nimport android.accounts.AccountManager;\nimport android.accounts.AccountManagerCallback;\nimport android.accounts.AccountManagerFuture;\nimport android.accounts.AuthenticatorDescription;\nimport android.accounts.AuthenticatorException;\nimport android.accounts.OperationCanceledException;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.Button;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.client.ipc.VAccountManager;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class ChooseTypeAndAccountActivity extends Activity\n        implements AccountManagerCallback<Bundle> {\n    private static final String TAG = \"AccountChooser\";\n\n    /**\n     * A Parcelable ArrayList of Account objects that limits the choosable accounts to those\n     * in this list, if this parameter is supplied.\n     */\n    public static final String EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST = \"allowableAccounts\";\n\n    /**\n     * A Parcelable ArrayList of String objects that limits the accounts to choose to those\n     * that match the types in this list, if this parameter is supplied. This list is also\n     * used to filter the allowable account types if add account is selected.\n     */\n    public static final String EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY = \"allowableAccountTypes\";\n\n    /**\n     * This is passed as the addAccountOptions parameter in AccountManager.addAccount()\n     * if it is called.\n     */\n    public static final String EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE = \"addAccountOptions\";\n\n    /**\n     * This is passed as the requiredFeatures parameter in AccountManager.addAccount()\n     * if it is called.\n     */\n    public static final String EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY =\n            \"addAccountRequiredFeatures\";\n\n    /**\n     * This is passed as the authTokenType string in AccountManager.addAccount()\n     * if it is called.\n     */\n    public static final String EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING = \"authTokenType\";\n\n    /**\n     * If set then the specified account is already \"selected\".\n     */\n    public static final String EXTRA_SELECTED_ACCOUNT = \"selectedAccount\";\n\n    /**\n     * Deprecated. Providing this extra to {@link ChooseTypeAndAccountActivity}\n     * will have no effect.\n     */\n    @Deprecated\n    public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT =\n            \"alwaysPromptForAccount\";\n\n    /**\n     * If set then this string willb e used as the description rather than\n     * the default.\n     */\n    public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE =\n            \"descriptionTextOverride\";\n\n    public static final int REQUEST_NULL = 0;\n    public static final int REQUEST_CHOOSE_TYPE = 1;\n    public static final int REQUEST_ADD_ACCOUNT = 2;\n\n    private static final String KEY_INSTANCE_STATE_PENDING_REQUEST = \"pendingRequest\";\n    private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = \"existingAccounts\";\n    private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = \"selectedAccountName\";\n    private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = \"selectedAddAccount\";\n    private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = \"accountList\";\n    public static final String KEY_USER_ID = \"userId\";\n\n    private static final int SELECTED_ITEM_NONE = -1;\n\n    private Set<Account> mSetOfAllowableAccounts;\n    private Set<String> mSetOfRelevantAccountTypes;\n    private String mSelectedAccountName = null;\n    private boolean mSelectedAddNewAccount = false;\n    private String mDescriptionOverride;\n\n    private ArrayList<Account> mAccounts;\n    private int mPendingRequest = REQUEST_NULL;\n    private Parcelable[] mExistingAccounts = null;\n    private int mSelectedItemIndex;\n    private Button mOkButton;\n    private int mCallingUserId;\n    private boolean mDontShowPicker;\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        // save some items we use frequently\n        final Intent intent = getIntent();\n\n        if (savedInstanceState != null) {\n            mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST);\n            mExistingAccounts =\n                    savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);\n\n            // Makes sure that any user selection is preserved across orientation changes.\n            mSelectedAccountName = savedInstanceState.getString(\n                    KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);\n\n            mSelectedAddNewAccount = savedInstanceState.getBoolean(\n                    KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);\n            mAccounts = savedInstanceState.getParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST);\n            mCallingUserId = savedInstanceState.getInt(KEY_USER_ID);\n        } else {\n            mPendingRequest = REQUEST_NULL;\n            mExistingAccounts = null;\n            mCallingUserId = intent.getIntExtra(KEY_USER_ID, -1);\n            // If the selected account as specified in the intent matches one in the list we will\n            // show is as pre-selected.\n            Account selectedAccount = intent.getParcelableExtra(EXTRA_SELECTED_ACCOUNT);\n            if (selectedAccount != null) {\n                mSelectedAccountName = selectedAccount.name;\n            }\n        }\n        VLog.v(TAG, \"selected account name is \" + mSelectedAccountName);\n\n        mSetOfAllowableAccounts = getAllowableAccountSet(intent);\n        mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);\n        mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);\n\n        mAccounts = getAcceptableAccountChoices(VAccountManager.get());\n\n        if (mDontShowPicker) {\n            super.onCreate(savedInstanceState);\n            return;\n        }\n\n        // In cases where the activity does not need to show an account picker, cut the chase\n        // and return the result directly. Eg:\n        // Single account -> select it directly\n        // No account -> launch add account activity directly\n        if (mPendingRequest == REQUEST_NULL) {\n            // If there are no relevant accounts and only one relevant account type go directly to\n            // add account. Otherwise let the user choose.\n            if (mAccounts.isEmpty()) {\n                setNonLabelThemeAndCallSuperCreate(savedInstanceState);\n                if (mSetOfRelevantAccountTypes.size() == 1) {\n                    runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());\n                } else {\n                    startChooseAccountTypeActivity();\n                }\n            }\n        }\n\n        String[] listItems = getListOfDisplayableOptions(mAccounts);\n        mSelectedItemIndex = getItemIndexToSelect(\n                mAccounts, mSelectedAccountName, mSelectedAddNewAccount);\n\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.choose_type_and_account);\n        overrideDescriptionIfSupplied(mDescriptionOverride);\n        populateUIAccountList(listItems);\n\n        // Only enable \"OK\" button if something has been selected.\n        mOkButton = (Button) findViewById(android.R.id.button2);\n        mOkButton.setEnabled(mSelectedItemIndex != SELECTED_ITEM_NONE);\n    }\n\n    @Override\n    protected void onDestroy() {\n        if (Log.isLoggable(TAG, Log.VERBOSE)) {\n            Log.v(TAG, \"ChooseTypeAndAccountActivity.onDestroy()\");\n        }\n        super.onDestroy();\n    }\n\n    @Override\n    protected void onSaveInstanceState(final Bundle outState) {\n        super.onSaveInstanceState(outState);\n        outState.putInt(KEY_INSTANCE_STATE_PENDING_REQUEST, mPendingRequest);\n        if (mPendingRequest == REQUEST_ADD_ACCOUNT) {\n            outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);\n        }\n        if (mSelectedItemIndex != SELECTED_ITEM_NONE) {\n            if (mSelectedItemIndex == mAccounts.size()) {\n                outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true);\n            } else {\n                outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);\n                outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME,\n                        mAccounts.get(mSelectedItemIndex).name);\n            }\n        }\n        outState.putParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST, mAccounts);\n    }\n\n    public void onCancelButtonClicked(View view) {\n        onBackPressed();\n    }\n\n    public void onOkButtonClicked(View view) {\n        if (mSelectedItemIndex == mAccounts.size()) {\n            // Selected \"Add New Account\" option\n            startChooseAccountTypeActivity();\n        } else if (mSelectedItemIndex != SELECTED_ITEM_NONE) {\n            onAccountSelected(mAccounts.get(mSelectedItemIndex));\n        }\n    }\n\n    // Called when the choose account type activity (for adding an account) returns.\n    // If it was a success read the account and set it in the result. In all cases\n    // return the result and finish this activity.\n    @Override\n    protected void onActivityResult(final int requestCode, final int resultCode,\n                                    final Intent data) {\n        if (Log.isLoggable(TAG, Log.VERBOSE)) {\n            if (data != null && data.getExtras() != null) data.getExtras().keySet();\n            Bundle extras = data != null ? data.getExtras() : null;\n            Log.v(TAG, \"ChooseTypeAndAccountActivity.onActivityResult(reqCode=\" + requestCode\n                    + \", resCode=\" + resultCode + \", extras=\" + extras + \")\");\n        }\n\n        // we got our result, so clear the fact that we had a pending request\n        mPendingRequest = REQUEST_NULL;\n\n        if (resultCode == RESULT_CANCELED) {\n            // if canceling out of addAccount and the original state caused us to skip this,\n            // finish this activity\n            if (mAccounts.isEmpty()) {\n                setResult(Activity.RESULT_CANCELED);\n                finish();\n            }\n            return;\n        }\n\n        if (resultCode == RESULT_OK) {\n            if (requestCode == REQUEST_CHOOSE_TYPE) {\n                if (data != null) {\n                    String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);\n                    if (accountType != null) {\n                        runAddAccountForAuthenticator(accountType);\n                        return;\n                    }\n                }\n                Log.d(TAG, \"ChooseTypeAndAccountActivity.onActivityResult: unable to find account \"\n                        + \"type, pretending the request was canceled\");\n            } else if (requestCode == REQUEST_ADD_ACCOUNT) {\n                String accountName = null;\n                String accountType = null;\n\n                if (data != null) {\n                    accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);\n                    accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);\n                }\n\n                if (accountName == null || accountType == null) {\n                    Account[] currentAccounts = VAccountManager.get().getAccounts(mCallingUserId, null);\n                    Set<Account> preExistingAccounts = new HashSet<>();\n                    for (Parcelable accountParcel : mExistingAccounts) {\n                        preExistingAccounts.add((Account) accountParcel);\n                    }\n                    for (Account account : currentAccounts) {\n                        if (!preExistingAccounts.contains(account)) {\n                            accountName = account.name;\n                            accountType = account.type;\n                            break;\n                        }\n                    }\n                }\n\n                if (accountName != null || accountType != null) {\n                    setResultAndFinish(accountName, accountType);\n                    return;\n                }\n            }\n            Log.d(TAG, \"ChooseTypeAndAccountActivity.onActivityResult: unable to find added \"\n                    + \"account, pretending the request was canceled\");\n        }\n        if (Log.isLoggable(TAG, Log.VERBOSE)) {\n            Log.v(TAG, \"ChooseTypeAndAccountActivity.onActivityResult: canceled\");\n        }\n        setResult(Activity.RESULT_CANCELED);\n        finish();\n    }\n\n    protected void runAddAccountForAuthenticator(String type) {\n        if (Log.isLoggable(TAG, Log.VERBOSE)) {\n            Log.v(TAG, \"runAddAccountForAuthenticator: \" + type);\n        }\n        final Bundle options = getIntent().getBundleExtra(\n                ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE);\n        final String[] requiredFeatures = getIntent().getStringArrayExtra(\n                ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY);\n        final String authTokenType = getIntent().getStringExtra(\n                ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING);\n        VAccountManager.get().addAccount(mCallingUserId, type, authTokenType, requiredFeatures,\n                options, null /* activity */, this /* callback */, null /* Handler */);\n    }\n\n    @Override\n    public void run(final AccountManagerFuture<Bundle> accountManagerFuture) {\n        try {\n            final Bundle accountManagerResult = accountManagerFuture.getResult();\n            final Intent intent = accountManagerResult.getParcelable(\n                    AccountManager.KEY_INTENT);\n            if (intent != null) {\n                mPendingRequest = REQUEST_ADD_ACCOUNT;\n                mExistingAccounts = VAccountManager.get().getAccounts(mCallingUserId, null);\n                intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);\n                startActivityForResult(intent, REQUEST_ADD_ACCOUNT);\n                return;\n            }\n        } catch (OperationCanceledException e) {\n            setResult(Activity.RESULT_CANCELED);\n            finish();\n            return;\n        } catch (IOException e) {\n        } catch (AuthenticatorException e) {\n        }\n        Bundle bundle = new Bundle();\n        bundle.putString(AccountManager.KEY_ERROR_MESSAGE, \"error communicating with server\");\n        setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));\n        finish();\n    }\n\n    /**\n     * The default activity theme shows label at the top. Set a theme which does\n     * not show label, which effectively makes the activity invisible. Note that\n     * no content is being set. If something gets set, using this theme may be\n     * useless.\n     */\n    private void setNonLabelThemeAndCallSuperCreate(Bundle savedInstanceState) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            setTheme(android.R.style.Theme_Material_Light_Dialog_NoActionBar);\n        } else {\n            setTheme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar);\n        }\n        super.onCreate(savedInstanceState);\n    }\n\n    private void onAccountSelected(Account account) {\n        Log.d(TAG, \"selected account \" + account);\n        setResultAndFinish(account.name, account.type);\n    }\n\n    private void setResultAndFinish(final String accountName, final String accountType) {\n        Bundle bundle = new Bundle();\n        bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);\n        bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);\n        setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));\n        VLog.v(TAG, \"ChooseTypeAndAccountActivity.setResultAndFinish: \"\n                + \"selected account \" + accountName + \", \" + accountType);\n        finish();\n    }\n\n    private void startChooseAccountTypeActivity() {\n        VLog.v(TAG, \"ChooseAccountTypeActivity.startChooseAccountTypeActivity()\");\n        final Intent intent = new Intent(this, ChooseAccountTypeActivity.class);\n        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);\n        intent.putExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,\n                getIntent().getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY));\n        intent.putExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,\n                getIntent().getBundleExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE));\n        intent.putExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,\n                getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY));\n        intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,\n                getIntent().getStringExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING));\n        startActivityForResult(intent, REQUEST_CHOOSE_TYPE);\n        mPendingRequest = REQUEST_CHOOSE_TYPE;\n    }\n\n    /**\n     * @return a value between 0 (inclusive) and accounts.size() (inclusive) or SELECTED_ITEM_NONE.\n     * An index value of accounts.size() indicates 'Add account' option.\n     */\n    private int getItemIndexToSelect(ArrayList<Account> accounts, String selectedAccountName,\n                                     boolean selectedAddNewAccount) {\n        // If \"Add account\" option was previously selected by user, preserve it across\n        // orientation changes.\n        if (selectedAddNewAccount) {\n            return accounts.size();\n        }\n        // search for the selected account name if present\n        for (int i = 0; i < accounts.size(); i++) {\n            if (accounts.get(i).name.equals(selectedAccountName)) {\n                return i;\n            }\n        }\n        // no account selected.\n        return SELECTED_ITEM_NONE;\n    }\n\n    private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) {\n        // List of options includes all accounts found together with \"Add new account\" as the\n        // last item in the list.\n        String[] listItems = new String[accounts.size() + 1];\n        for (int i = 0; i < accounts.size(); i++) {\n            listItems[i] = accounts.get(i).name;\n        }\n        listItems[accounts.size()] = getResources().getString(\n                R.string.add_account_button_label);\n        return listItems;\n    }\n\n    /**\n     * Create a list of Account objects for each account that is acceptable. Filter out\n     * accounts that don't match the allowable types, if provided, or that don't match the\n     * allowable accounts, if provided.\n     */\n    private ArrayList<Account> getAcceptableAccountChoices(VAccountManager accountManager) {\n        final Account[] accounts = accountManager.getAccounts(mCallingUserId, null);\n        ArrayList<Account> accountsToPopulate = new ArrayList<>(accounts.length);\n        for (Account account : accounts) {\n            if (mSetOfAllowableAccounts != null && !mSetOfAllowableAccounts.contains(account)) {\n                continue;\n            }\n            if (mSetOfRelevantAccountTypes != null\n                    && !mSetOfRelevantAccountTypes.contains(account.type)) {\n                continue;\n            }\n            accountsToPopulate.add(account);\n        }\n        return accountsToPopulate;\n    }\n\n    /**\n     * Return a set of account types specified by the intent as well as supported by the\n     * AccountManager.\n     */\n    private Set<String> getReleventAccountTypes(final Intent intent) {\n        // An account type is relevant iff it is allowed by the caller and supported by the account\n        // manager.\n        Set<String> setOfRelevantAccountTypes;\n        final String[] allowedAccountTypes =\n                intent.getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);\n        AuthenticatorDescription[] descs = VAccountManager.get().getAuthenticatorTypes();\n        Set<String> supportedAccountTypes = new HashSet<String>(descs.length);\n        for (AuthenticatorDescription desc : descs) {\n            supportedAccountTypes.add(desc.type);\n        }\n        if (allowedAccountTypes != null) {\n            setOfRelevantAccountTypes = new HashSet<>();\n            Collections.addAll(setOfRelevantAccountTypes, allowedAccountTypes);\n            setOfRelevantAccountTypes.retainAll(supportedAccountTypes);\n        } else {\n            setOfRelevantAccountTypes = supportedAccountTypes;\n        }\n        return setOfRelevantAccountTypes;\n    }\n\n    /**\n     * Returns a set of whitelisted accounts given by the intent or null if none specified by the\n     * intent.\n     */\n    private Set<Account> getAllowableAccountSet(final Intent intent) {\n        Set<Account> setOfAllowableAccounts = null;\n        final ArrayList<Parcelable> validAccounts =\n                intent.getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST);\n        if (validAccounts != null) {\n            setOfAllowableAccounts = new HashSet<>(validAccounts.size());\n            for (Parcelable parcelable : validAccounts) {\n                setOfAllowableAccounts.add((Account) parcelable);\n            }\n        }\n        return setOfAllowableAccounts;\n    }\n\n    /**\n     * Overrides the description text view for the picker activity if specified by the intent.\n     * If not specified then makes the description invisible.\n     */\n    private void overrideDescriptionIfSupplied(String descriptionOverride) {\n        TextView descriptionView = (TextView) findViewById(R.id.description);\n        if (!TextUtils.isEmpty(descriptionOverride)) {\n            descriptionView.setText(descriptionOverride);\n        } else {\n            descriptionView.setVisibility(View.GONE);\n        }\n    }\n\n    /**\n     * Populates the UI ListView with the given list of items and selects an item\n     * based on {@code mSelectedItemIndex} member variable.\n     */\n    private void populateUIAccountList(String[] listItems) {\n        ListView list = (ListView) findViewById(android.R.id.list);\n        list.setAdapter(new ArrayAdapter<>(this,\n                android.R.layout.simple_list_item_single_choice, listItems));\n        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);\n        list.setItemsCanFocus(false);\n        list.setOnItemClickListener(\n                new AdapterView.OnItemClickListener() {\n                    @Override\n                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {\n                        mSelectedItemIndex = position;\n                        mOkButton.setEnabled(true);\n                    }\n                });\n        if (mSelectedItemIndex != SELECTED_ITEM_NONE) {\n            list.setItemChecked(mSelectedItemIndex, true);\n            VLog.v(TAG, \"List item \" + mSelectedItemIndex + \" should be selected\");\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/ChooserActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.client.env.Constants;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VUserHandle;\n\npublic class ChooserActivity extends ResolverActivity {\n    public static final String EXTRA_DATA = \"android.intent.extra.virtual.data\";\n    public static final String EXTRA_WHO = \"android.intent.extra.virtual.who\";\n    public static final String EXTRA_REQUEST_CODE = \"android.intent.extra.virtual.request_code\";\n    public static final String ACTION;\n\n    static {\n        Intent target = new Intent();\n        Intent intent = Intent.createChooser(target, \"\");\n        ACTION = intent.getAction();\n    }\n    public static boolean check(Intent intent) {\n        try {\n            return TextUtils.equals(ACTION, intent.getAction())\n                    ||TextUtils.equals(Intent.ACTION_CHOOSER, intent.getAction());\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    @SuppressLint(\"MissingSuperCall\")\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        Intent intent = getIntent();\n        int userId = intent.getIntExtra(Constants.EXTRA_USER_HANDLE, VUserHandle.getCallingUserId());\n        mOptions = intent.getParcelableExtra(EXTRA_DATA);\n        mResultWho = intent.getStringExtra(EXTRA_WHO);\n        mRequestCode = intent.getIntExtra(EXTRA_REQUEST_CODE, 0);\n        Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);\n        if (!(targetParcelable instanceof Intent)) {\n            VLog.w(\"ChooseActivity\", \"Target is not an intent: \" + targetParcelable);\n            finish();\n            return;\n        }\n        Intent target = (Intent) targetParcelable;\n        CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);\n        if (title == null) {\n            title = getString(R.string.choose);\n        }\n        Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);\n        Intent[] initialIntents = null;\n        if (pa != null) {\n            initialIntents = new Intent[pa.length];\n            for (int i = 0; i < pa.length; i++) {\n                if (!(pa[i] instanceof Intent)) {\n                    VLog.w(\"ChooseActivity\", \"Initial intent #\" + i\n                            + \" not an Intent: \" + pa[i]);\n                    finish();\n                    return;\n                }\n                initialIntents[i] = (Intent) pa[i];\n            }\n        }\n        super.onCreate(savedInstanceState, target, title, initialIntents, null, false, userId);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/DaemonService.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.app.Notification;\nimport android.app.Service;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.IBinder;\n\n\n/**\n * @author Lody\n *\n */\npublic class DaemonService extends Service {\n\n    private static final int NOTIFY_ID = 1001;\n\n\tpublic static void startup(Context context) {\n\t\tcontext.startService(new Intent(context, DaemonService.class));\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tstartup(this);\n\t}\n\n\t@Override\n\tpublic IBinder onBind(Intent intent) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void onCreate() {\n\t\tsuper.onCreate();\n        startService(new Intent(this, InnerService.class));\n        startForeground(NOTIFY_ID, new Notification());\n\n\t}\n\n\t@Override\n\tpublic int onStartCommand(Intent intent, int flags, int startId) {\n\t\treturn START_STICKY;\n\t}\n\n\tpublic static final class InnerService extends Service {\n\n        @Override\n        public int onStartCommand(Intent intent, int flags, int startId) {\n            startForeground(NOTIFY_ID, new Notification());\n            stopForeground(true);\n            stopSelf();\n            return super.onStartCommand(intent, flags, startId);\n        }\n\n\t\t@Override\n\t\tpublic IBinder onBind(Intent intent) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/ResolverActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.ActivityManager;\nimport android.app.AlertDialog;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.LabeledIntent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.content.res.Resources;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.AsyncTask;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.PatternMatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.BaseAdapter;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VUserHandle;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\n\npublic class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {\n    private static final String TAG = \"ResolverActivity\";\n    private static final boolean DEBUG = false;\n    protected Bundle mOptions;\n    protected String mResultWho;\n    protected int mRequestCode;\n    private int mLaunchedFromUid;\n    private ResolveListAdapter mAdapter;\n    private PackageManager mPm;\n    private boolean mAlwaysUseOption;\n    private boolean mShowExtended;\n    private ListView mListView;\n    private Button mAlwaysButton;\n    private Button mOnceButton;\n    private int mIconDpi;\n    private int mIconSize;\n    private int mMaxColumns;\n    private int mLastSelected = ListView.INVALID_POSITION;\n    private AlertDialog dialog;\n    private boolean mRegistered;\n\n    private Intent makeMyIntent() {\n        Intent intent = new Intent(getIntent());\n        intent.setComponent(null);\n        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\n        return intent;\n    }\n\n    @SuppressLint(\"MissingSuperCall\")\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        final int titleResource;\n        final Intent intent = makeMyIntent();\n        final Set<String> categories = intent.getCategories();\n        if (Intent.ACTION_MAIN.equals(intent.getAction())\n                && categories != null\n                && categories.size() == 1\n                && categories.contains(Intent.CATEGORY_HOME)) {\n            titleResource = R.string.choose;\n        } else {\n            titleResource = R.string.choose;\n        }\n\n        onCreate(savedInstanceState, intent, getResources().getText(titleResource),\n                null, null, true, VUserHandle.getCallingUserId());\n    }\n\n    protected void onCreate(Bundle savedInstanceState, Intent intent,\n                            CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,\n                            boolean alwaysUseOption, int userid) {\n        super.onCreate(savedInstanceState);\n        mLaunchedFromUid = userid;\n        mPm = getPackageManager();\n        mAlwaysUseOption = alwaysUseOption;\n        mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);\n\n        mRegistered = true;\n\n        final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);\n        mIconDpi = am.getLauncherLargeIconDensity();\n        mIconSize = am.getLauncherLargeIconSize();\n\n        mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,\n                mLaunchedFromUid);\n        int count = mAdapter.getCount();\n        if (Build.VERSION.SDK_INT >= 17) {\n            if (mLaunchedFromUid < 0) {\n                // Gulp!\n                finish();\n                return;\n            }\n        }\n\n        if (count == 1) {\n            startSelected(0, false);\n            mRegistered = false;\n            finish();\n            return;\n        }\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setTitle(title);\n        if (count > 1) {\n            mListView = new ListView(this);\n            mListView.setAdapter(mAdapter);\n            mListView.setOnItemClickListener(this);\n            mListView.setOnItemLongClickListener(new ItemLongClickListener());\n            builder.setView(mListView);\n            if (alwaysUseOption) {\n                mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);\n            }\n        } else {\n            builder.setMessage(R.string.noApplications);\n        }\n        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {\n            @Override\n            public void onCancel(DialogInterface dialog) {\n                finish();\n            }\n        });\n        dialog = builder.show();\n//\n//        if (alwaysUseOption) {\n//            final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);\n//            if (buttonLayout != null) {\n//                buttonLayout.setVisibility(View.VISIBLE);\n//                mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);\n//                mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);\n//            } else {\n//                mAlwaysUseOption = false;\n//            }\n//            // Set the initial highlight if there was a preferred or last used choice\n//            final int initialHighlight = mAdapter.getInitialHighlight();\n//            if (initialHighlight >= 0) {\n//                mListView.setItemChecked(initialHighlight, true);\n//                onItemClick(null, null, initialHighlight, 0); // Other entries are not used\n//            }\n//        }\n    }\n\n    @Override\n    protected void onDestroy() {\n        if (dialog != null && dialog.isShowing()) {\n            dialog.dismiss();\n        }\n        super.onDestroy();\n    }\n\n    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)\n    Drawable getIcon(Resources res, int resId) {\n        Drawable result;\n        try {\n            result = res.getDrawableForDensity(resId, mIconDpi);\n        } catch (Resources.NotFoundException e) {\n            result = null;\n        }\n\n        return result;\n    }\n\n    Drawable loadIconForResolveInfo(ResolveInfo ri) {\n        Drawable dr;\n        try {\n            if (ri.resolvePackageName != null && ri.icon != 0) {\n                dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);\n                if (dr != null) {\n                    return dr;\n                }\n            }\n            final int iconRes = ri.getIconResource();\n            if (iconRes != 0) {\n                dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);\n                if (dr != null) {\n                    return dr;\n                }\n            }\n        } catch (PackageManager.NameNotFoundException e) {\n            VLog.e(TAG, \"Couldn't find resources for package\\n\" + VLog.getStackTraceString(e));\n        }\n        return ri.loadIcon(mPm);\n    }\n\n    @Override\n    protected void onRestart() {\n        super.onRestart();\n        if (!mRegistered) {\n            mRegistered = true;\n        }\n        mAdapter.handlePackagesChanged();\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        if (mRegistered) {\n            mRegistered = false;\n        }\n        if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {\n            // This resolver is in the unusual situation where it has been\n            // launched at the top of a new task.  We don't let it be added\n            // to the recent tasks shown to the user, and we need to make sure\n            // that each time we are launched we get the correct launching\n            // uid (not re-using the same resolver from an old launching uid),\n            // so we will now finish ourself since being no longer visible,\n            // the user probably can't get back to us.\n            if (!isChangingConfigurations()) {\n                finish();\n            }\n        }\n    }\n\n    @Override\n    protected void onRestoreInstanceState(Bundle savedInstanceState) {\n        super.onRestoreInstanceState(savedInstanceState);\n        if (mAlwaysUseOption) {\n            final int checkedPos = mListView.getCheckedItemPosition();\n            final boolean enabled = checkedPos != ListView.INVALID_POSITION;\n            mLastSelected = checkedPos;\n            mAlwaysButton.setEnabled(enabled);\n            mOnceButton.setEnabled(enabled);\n            if (enabled) {\n                mListView.setSelection(checkedPos);\n            }\n        }\n    }\n\n    @Override\n    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n        final int checkedPos = mListView.getCheckedItemPosition();\n        final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;\n        if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {\n            mAlwaysButton.setEnabled(hasValidSelection);\n            mOnceButton.setEnabled(hasValidSelection);\n            if (hasValidSelection) {\n                mListView.smoothScrollToPosition(checkedPos);\n            }\n            mLastSelected = checkedPos;\n        } else {\n            startSelected(position, false);\n        }\n    }\n\n    void startSelected(int which, boolean always) {\n        if (isFinishing()) {\n            return;\n        }\n        ResolveInfo ri = mAdapter.resolveInfoForPosition(which);\n        Intent intent = mAdapter.intentForPosition(which);\n        onIntentSelected(ri, intent, always);\n        finish();\n    }\n\n    protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {\n        if (mAlwaysUseOption && mAdapter.mOrigResolveList != null) {\n            // Build a reasonable intent filter, based on what matched.\n            IntentFilter filter = new IntentFilter();\n\n            if (intent.getAction() != null) {\n                filter.addAction(intent.getAction());\n            }\n            Set<String> categories = intent.getCategories();\n            if (categories != null) {\n                for (String cat : categories) {\n                    filter.addCategory(cat);\n                }\n            }\n            filter.addCategory(Intent.CATEGORY_DEFAULT);\n\n            int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;\n            Uri data = intent.getData();\n            if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {\n                String mimeType = intent.resolveType(this);\n                if (mimeType != null) {\n                    try {\n                        filter.addDataType(mimeType);\n                    } catch (IntentFilter.MalformedMimeTypeException e) {\n                        VLog.w(\"ResolverActivity\", \"mimeType\\n\" + VLog.getStackTraceString(e));\n                        filter = null;\n                    }\n                }\n            }\n            if (data != null && data.getScheme() != null) {\n                // We need the data specification if there was no type,\n                // OR if the scheme is not one of our magical \"file:\"\n                // or \"content:\" schemes (see IntentFilter for the reason).\n                if (cat != IntentFilter.MATCH_CATEGORY_TYPE\n                        || (!\"file\".equals(data.getScheme())\n                        && !\"content\".equals(data.getScheme()))) {\n                    filter.addDataScheme(data.getScheme());\n\n                    if (Build.VERSION.SDK_INT >= 19) {\n                        // Look through the resolved filter to determine which part\n                        // of it matched the original Intent.\n                        Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();\n                        if (pIt != null) {\n                            String ssp = data.getSchemeSpecificPart();\n                            while (ssp != null && pIt.hasNext()) {\n                                PatternMatcher p = pIt.next();\n                                if (p.match(ssp)) {\n                                    filter.addDataSchemeSpecificPart(p.getPath(), p.getType());\n                                    break;\n                                }\n                            }\n                        }\n                        Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();\n                        if (aIt != null) {\n                            while (aIt.hasNext()) {\n                                IntentFilter.AuthorityEntry a = aIt.next();\n                                if (a.match(data) >= 0) {\n                                    int port = a.getPort();\n                                    filter.addDataAuthority(a.getHost(),\n                                            port >= 0 ? Integer.toString(port) : null);\n                                    break;\n                                }\n                            }\n                        }\n                        pIt = ri.filter.pathsIterator();\n                        if (pIt != null) {\n                            String path = data.getPath();\n                            while (path != null && pIt.hasNext()) {\n                                PatternMatcher p = pIt.next();\n                                if (p.match(path)) {\n                                    filter.addDataPath(p.getPath(), p.getType());\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (filter != null) {\n                final int N = mAdapter.mOrigResolveList.size();\n                ComponentName[] set = new ComponentName[N];\n                int bestMatch = 0;\n                for (int i = 0; i < N; i++) {\n                    ResolveInfo r = mAdapter.mOrigResolveList.get(i);\n                    set[i] = new ComponentName(r.activityInfo.packageName,\n                            r.activityInfo.name);\n                    if (r.match > bestMatch) bestMatch = r.match;\n                }\n                if (alwaysCheck) {\n                    getPackageManager().addPreferredActivity(filter, bestMatch, set,\n                            intent.getComponent());\n                } else {\n                    try {\n                        Reflect.on(VClientImpl.get().getCurrentApplication().getPackageManager()).call(\"setLastChosenActivity\",\n                                intent,\n                                intent.resolveTypeIfNeeded(getContentResolver()),\n                                PackageManager.MATCH_DEFAULT_ONLY,\n                                filter, bestMatch, intent.getComponent());\n                    } catch (Exception re) {\n                        VLog.d(TAG, \"Error calling setLastChosenActivity\\n\" + VLog.getStackTraceString(re));\n                    }\n                }\n            }\n        }\n\n        if (intent != null) {\n            ActivityInfo info = VirtualCore.get().resolveActivityInfo(intent, mLaunchedFromUid);\n            if (info == null) {\n                //外面的\n                startActivity(intent);\n//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n//                    startActivity(intent);//, mRequestCode, mOptions);\n//                }else{\n//                    startActivity(intent);//, mRequestCode);\n//                }\n            } else {\n                VActivityManager.get().startActivity(intent, info, null, mOptions, mResultWho, mRequestCode, mLaunchedFromUid);\n            }\n\n        }\n    }\n\n    void showAppDetails(ResolveInfo ri) {\n        Intent in = new Intent().setAction(\"android.settings.APPLICATION_DETAILS_SETTINGS\")\n                .setData(Uri.fromParts(\"package\", ri.activityInfo.packageName, null))\n                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);\n        startActivity(in);\n    }\n\n    static class ViewHolder {\n        public TextView text;\n        public TextView text2;\n        public ImageView icon;\n\n        public ViewHolder(View view) {\n            text = (TextView) view.findViewById(R.id.text1);\n            text2 = (TextView) view.findViewById(R.id.text2);\n            icon = (ImageView) view.findViewById(R.id.icon);\n        }\n    }\n\n    private final class DisplayResolveInfo {\n        ResolveInfo ri;\n        CharSequence displayLabel;\n        Drawable displayIcon;\n        CharSequence extendedInfo;\n        Intent origIntent;\n\n        DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,\n                           CharSequence pInfo, Intent pOrigIntent) {\n            ri = pri;\n            displayLabel = pLabel;\n            extendedInfo = pInfo;\n            origIntent = pOrigIntent;\n        }\n    }\n\n    private final class ResolveListAdapter extends BaseAdapter {\n        private final Intent[] mInitialIntents;\n        private final List<ResolveInfo> mBaseResolveList;\n        private final Intent mIntent;\n        private final int mLaunchedFromUid;\n        private final LayoutInflater mInflater;\n        List<DisplayResolveInfo> mList;\n        List<ResolveInfo> mOrigResolveList;\n        private ResolveInfo mLastChosen;\n        private int mInitialHighlight = -1;\n\n        public ResolveListAdapter(Context context, Intent intent,\n                                  Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {\n            mIntent = new Intent(intent);\n            mInitialIntents = initialIntents;\n            mBaseResolveList = rList;\n            mLaunchedFromUid = launchedFromUid;\n            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n            mList = new ArrayList<DisplayResolveInfo>();\n            rebuildList();\n        }\n\n        public void handlePackagesChanged() {\n            final int oldItemCount = getCount();\n            rebuildList();\n            notifyDataSetChanged();\n            final int newItemCount = getCount();\n            if (newItemCount == 0) {\n                // We no longer have any items...  just finish the activity.\n                finish();\n            }\n        }\n\n        public int getInitialHighlight() {\n            return mInitialHighlight;\n        }\n\n        private void rebuildList() {\n            List<ResolveInfo> currentResolveList;\n\n//            try {\n//                mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(\n//                        mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),\n//                        PackageManager.MATCH_DEFAULT_ONLY);\n//            } catch (RemoteException re) {\n//                Log.d(TAG, \"Error calling setLastChosenActivity\\n\" + re);\n//            }\n\n            mList.clear();\n            if (mBaseResolveList != null) {\n                currentResolveList = mBaseResolveList;\n                mOrigResolveList = null;\n            } else {\n                currentResolveList = mOrigResolveList = mPm.queryIntentActivities(\n                        mIntent, PackageManager.MATCH_DEFAULT_ONLY\n                                | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));\n                // Filter out any activities that the launched uid does not\n                // have permission for.  We don't do this when we have an explicit\n                // list of resolved activities, because that only happens when\n                // we are being subclassed, so we can safely launch whatever\n                // they gave us.\n//                if (currentResolveList != null) {\n//                    for (int i = currentResolveList.size() - 1; i >= 0; i--) {\n//                        ActivityInfo ai = currentResolveList.get(i).activityInfo;\n//                        int granted = ActivityManager.checkComponentPermission(\n//                                ai.permission, mLaunchedFromUid,\n//                                ai.applicationInfo.uid, ai.exported);\n//                        if (granted != PackageManager.PERMISSION_GRANTED) {\n//                            // Access not allowed!\n//                            if (mOrigResolveList == currentResolveList) {\n//                                mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);\n//                            }\n//                            currentResolveList.remove(i);\n//                        }\n//                    }\n//                }\n            }\n            int N;\n            if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {\n                // Only display the first matches that are either of equal\n                // priority or have asked to be default options.\n                ResolveInfo r0 = currentResolveList.get(0);\n                for (int i = 1; i < N; i++) {\n                    ResolveInfo ri = currentResolveList.get(i);\n                    if (DEBUG) VLog.v(\n                            \"ResolveListActivity\",\n                            r0.activityInfo.name + \"=\" +\n                                    r0.priority + \"/\" + r0.isDefault + \" vs \" +\n                                    ri.activityInfo.name + \"=\" +\n                                    ri.priority + \"/\" + ri.isDefault);\n                    if (r0.priority != ri.priority ||\n                            r0.isDefault != ri.isDefault) {\n                        while (i < N) {\n                            if (mOrigResolveList == currentResolveList) {\n                                mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);\n                            }\n                            currentResolveList.remove(i);\n                            N--;\n                        }\n                    }\n                }\n                if (N > 1) {\n                    ResolveInfo.DisplayNameComparator rComparator =\n                            new ResolveInfo.DisplayNameComparator(mPm);\n                    Collections.sort(currentResolveList, rComparator);\n                }\n                // First put the initial items at the top.\n                if (mInitialIntents != null) {\n                    for (int i = 0; i < mInitialIntents.length; i++) {\n                        Intent ii = mInitialIntents[i];\n                        if (ii == null) {\n                            continue;\n                        }\n                        ActivityInfo ai = ii.resolveActivityInfo(\n                                getPackageManager(), 0);\n                        if (ai == null) {\n                            VLog.w(\"ResolverActivity\", \"No activity found for \"\n                                    + ii);\n                            continue;\n                        }\n                        ResolveInfo ri = new ResolveInfo();\n                        ri.activityInfo = ai;\n                        if (ii instanceof LabeledIntent) {\n                            LabeledIntent li = (LabeledIntent) ii;\n                            ri.resolvePackageName = li.getSourcePackage();\n                            ri.labelRes = li.getLabelResource();\n                            ri.nonLocalizedLabel = li.getNonLocalizedLabel();\n                            ri.icon = li.getIconResource();\n                        }\n                        mList.add(new DisplayResolveInfo(ri,\n                                ri.loadLabel(getPackageManager()), null, ii));\n                    }\n                }\n\n                // Check for applications with same name and use application name or\n                // package name if necessary\n                r0 = currentResolveList.get(0);\n                int start = 0;\n                CharSequence r0Label = r0.loadLabel(mPm);\n                mShowExtended = false;\n                for (int i = 1; i < N; i++) {\n                    if (r0Label == null) {\n                        r0Label = r0.activityInfo.packageName;\n                    }\n                    ResolveInfo ri = currentResolveList.get(i);\n                    CharSequence riLabel = ri.loadLabel(mPm);\n                    if (riLabel == null) {\n                        riLabel = ri.activityInfo.packageName;\n                    }\n                    if (riLabel.equals(r0Label)) {\n                        continue;\n                    }\n                    processGroup(currentResolveList, start, (i - 1), r0, r0Label);\n                    r0 = ri;\n                    r0Label = riLabel;\n                    start = i;\n                }\n                // Process last group\n                processGroup(currentResolveList, start, (N - 1), r0, r0Label);\n            }\n        }\n\n        private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,\n                                  CharSequence roLabel) {\n            // Process labels from start to i\n            int num = end - start + 1;\n            if (num == 1) {\n                if (mLastChosen != null\n                        && mLastChosen.activityInfo.packageName.equals(\n                        ro.activityInfo.packageName)\n                        && mLastChosen.activityInfo.name.equals(ro.activityInfo.name)) {\n                    mInitialHighlight = mList.size();\n                }\n                // No duplicate labels. Use label for entry at start\n                mList.add(new DisplayResolveInfo(ro, roLabel, null, null));\n            } else {\n                mShowExtended = true;\n                boolean usePkg = false;\n                CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);\n                if (startApp == null) {\n                    usePkg = true;\n                }\n                if (!usePkg) {\n                    // Use HashSet to track duplicates\n                    HashSet<CharSequence> duplicates =\n                            new HashSet<CharSequence>();\n                    duplicates.add(startApp);\n                    for (int j = start + 1; j <= end; j++) {\n                        ResolveInfo jRi = rList.get(j);\n                        CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);\n                        if ((jApp == null) || (duplicates.contains(jApp))) {\n                            usePkg = true;\n                            break;\n                        } else {\n                            duplicates.add(jApp);\n                        }\n                    }\n                    // Clear HashSet for later use\n                    duplicates.clear();\n                }\n                for (int k = start; k <= end; k++) {\n                    ResolveInfo add = rList.get(k);\n                    if (mLastChosen != null\n                            && mLastChosen.activityInfo.packageName.equals(\n                            add.activityInfo.packageName)\n                            && mLastChosen.activityInfo.name.equals(add.activityInfo.name)) {\n                        mInitialHighlight = mList.size();\n                    }\n                    if (usePkg) {\n                        // Use application name for all entries from start to end-1\n                        mList.add(new DisplayResolveInfo(add, roLabel,\n                                add.activityInfo.packageName, null));\n                    } else {\n                        // Use package name for all entries from start to end-1\n                        mList.add(new DisplayResolveInfo(add, roLabel,\n                                add.activityInfo.applicationInfo.loadLabel(mPm), null));\n                    }\n                }\n            }\n        }\n\n        public ResolveInfo resolveInfoForPosition(int position) {\n            return mList.get(position).ri;\n        }\n\n        public Intent intentForPosition(int position) {\n            DisplayResolveInfo dri = mList.get(position);\n\n            Intent intent = new Intent(dri.origIntent != null\n                    ? dri.origIntent : mIntent);\n            intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT\n                    | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);\n            ActivityInfo ai = dri.ri.activityInfo;\n            intent.setComponent(new ComponentName(\n                    ai.applicationInfo.packageName, ai.name));\n            return intent;\n        }\n\n        public int getCount() {\n            return mList.size();\n        }\n\n        public Object getItem(int position) {\n            return mList.get(position);\n        }\n\n        public long getItemId(int position) {\n            return position;\n        }\n\n        public View getView(int position, View convertView, ViewGroup parent) {\n            View view;\n            if (convertView == null) {\n                view = mInflater.inflate(R.layout.resolve_list_item, parent, false);\n\n                final ViewHolder holder = new ViewHolder(view);\n                view.setTag(holder);\n\n                // Fix the icon size even if we have different sized resources\n                ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();\n                lp.width = lp.height = mIconSize;\n            } else {\n                view = convertView;\n            }\n            bindView(view, mList.get(position));\n            return view;\n        }\n\n        private final void bindView(View view, DisplayResolveInfo info) {\n            final ViewHolder holder = (ViewHolder) view.getTag();\n            holder.text.setText(info.displayLabel);\n            if (mShowExtended) {\n                holder.text2.setVisibility(View.VISIBLE);\n                holder.text2.setText(info.extendedInfo);\n            } else {\n                holder.text2.setVisibility(View.GONE);\n            }\n            if (info.displayIcon == null) {\n                new LoadIconTask().execute(info);\n            }\n            holder.icon.setImageDrawable(info.displayIcon);\n        }\n    }\n\n    class ItemLongClickListener implements AdapterView.OnItemLongClickListener {\n\n        @Override\n        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {\n            ResolveInfo ri = mAdapter.resolveInfoForPosition(position);\n            showAppDetails(ri);\n            return true;\n        }\n\n    }\n\n    class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {\n        @Override\n        protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {\n            final DisplayResolveInfo info = params[0];\n            if (info.displayIcon == null) {\n                info.displayIcon = loadIconForResolveInfo(info.ri);\n            }\n            return info;\n        }\n\n        @Override\n        protected void onPostExecute(DisplayResolveInfo info) {\n            mAdapter.notifyDataSetChanged();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/ShortcutHandleActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport com.lody.virtual.client.ipc.VActivityManager;\n\nimport java.net.URISyntaxException;\n\n/**\n * @author Lody\n */\npublic class ShortcutHandleActivity extends Activity {\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        finish();\n        Intent intent = getIntent();\n        if (intent == null) {\n            return;\n        }\n        int userId = intent.getIntExtra(\"_VA_|_user_id_\", 0);\n        String splashUri = intent.getStringExtra(\"_VA_|_splash_\");\n        String targetUri = intent.getStringExtra(\"_VA_|_uri_\");\n        Intent splashIntent = null;\n        Intent targetIntent = null;\n        if (splashUri != null) {\n            try {\n                splashIntent = Intent.parseUri(splashUri, 0);\n            } catch (URISyntaxException e) {\n                e.printStackTrace();\n            }\n        }\n        if (targetUri != null) {\n            try {\n                targetIntent = Intent.parseUri(targetUri, 0);\n            } catch (URISyntaxException e) {\n                e.printStackTrace();\n            }\n        }\n        if (targetIntent == null) {\n            return;\n        }\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n            targetIntent.setSelector(null);\n        }\n\n        if (splashIntent == null) {\n            try {\n                VActivityManager.get().startActivity(targetIntent, userId);\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n        } else {\n            splashIntent.putExtra(Intent.EXTRA_INTENT, targetIntent);\n            splashIntent.putExtra(Intent.EXTRA_CC, userId);\n            startActivity(splashIntent);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.client.core.InvocationStubManager;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.hook.proxies.am.HCallbackStub;\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.StubActivityRecord;\n\n/**\n * @author Lody\n *\n */\npublic abstract class StubActivity extends Activity {\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\t// The savedInstanceState's classLoader is not exist.\n\t\tsuper.onCreate(null);\n\t\tfinish();\n        // It seems that we have conflict with the other Android-Plugin-Framework.\n\t\tIntent stubIntent = getIntent();\n        // Try to acquire the actually component information.\n\t\tStubActivityRecord r = new StubActivityRecord(stubIntent);\n\t\tif (r.intent != null) {\n\t\t\tif (TextUtils.equals(r.info.processName, VirtualRuntime.getProcessName()) && r.userId == VUserHandle.myUserId()) {\n                // Retry to inject the HCallback to instead of the exist one.\n\t\t\t\tInvocationStubManager.getInstance().checkEnv(HCallbackStub.class);\n\t\t\t\tIntent intent = r.intent;\n\t\t\t\tstartActivity(intent);\n\t\t\t} else {\n                // Start the target Activity in other process.\n\t\t\t\tVActivityManager.get().startActivity(r.intent, r.userId);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class C0 extends StubActivity {\n\t}\n\n\tpublic static class C1 extends StubActivity {\n\t}\n\n\tpublic static class C2 extends StubActivity {\n\t}\n\n\tpublic static class C3 extends StubActivity {\n\t}\n\n\tpublic static class C4 extends StubActivity {\n\t}\n\n\tpublic static class C5 extends StubActivity {\n\t}\n\n\tpublic static class C6 extends StubActivity {\n\t}\n\n\tpublic static class C7 extends StubActivity {\n\t}\n\n\tpublic static class C8 extends StubActivity {\n\t}\n\n\tpublic static class C9 extends StubActivity {\n\t}\n\n\tpublic static class C10 extends StubActivity {\n\t}\n\n\tpublic static class C11 extends StubActivity {\n\t}\n\n\tpublic static class C12 extends StubActivity {\n\t}\n\n\tpublic static class C13 extends StubActivity {\n\t}\n\n\tpublic static class C14 extends StubActivity {\n\t}\n\n\tpublic static class C15 extends StubActivity {\n\t}\n\n\tpublic static class C16 extends StubActivity {\n\t}\n\n\tpublic static class C17 extends StubActivity {\n\t}\n\n\tpublic static class C18 extends StubActivity {\n\t}\n\n\tpublic static class C19 extends StubActivity {\n\t}\n\n\tpublic static class C20 extends StubActivity {\n\t}\n\n\tpublic static class C21 extends StubActivity {\n\t}\n\n\tpublic static class C22 extends StubActivity {\n\t}\n\n\tpublic static class C23 extends StubActivity {\n\t}\n\n\tpublic static class C24 extends StubActivity {\n\t}\n\n\tpublic static class C25 extends StubActivity {\n\t}\n\n\tpublic static class C26 extends StubActivity {\n\t}\n\n\tpublic static class C27 extends StubActivity {\n\t}\n\n\tpublic static class C28 extends StubActivity {\n\t}\n\n\tpublic static class C29 extends StubActivity {\n\t}\n\n\tpublic static class C30 extends StubActivity {\n\t}\n\n\tpublic static class C31 extends StubActivity {\n\t}\n\n\tpublic static class C32 extends StubActivity {\n\t}\n\n\tpublic static class C33 extends StubActivity {\n\t}\n\n\tpublic static class C34 extends StubActivity {\n\t}\n\n\tpublic static class C35 extends StubActivity {\n\t}\n\n\tpublic static class C36 extends StubActivity {\n\t}\n\n\tpublic static class C37 extends StubActivity {\n\t}\n\n\tpublic static class C38 extends StubActivity {\n\t}\n\n\tpublic static class C39 extends StubActivity {\n\t}\n\n\tpublic static class C40 extends StubActivity {\n\t}\n\n\tpublic static class C41 extends StubActivity {\n\t}\n\n\tpublic static class C42 extends StubActivity {\n\t}\n\n\tpublic static class C43 extends StubActivity {\n\t}\n\n\tpublic static class C44 extends StubActivity {\n\t}\n\n\tpublic static class C45 extends StubActivity {\n\t}\n\n\tpublic static class C46 extends StubActivity {\n\t}\n\n\tpublic static class C47 extends StubActivity {\n\t}\n\n\tpublic static class C48 extends StubActivity {\n\t}\n\n\tpublic static class C49 extends StubActivity {\n\t}\n\n\tpublic static class C50 extends StubActivity {\n\t}\n\n\tpublic static class C51 extends StubActivity {\n\t}\n\n\tpublic static class C52 extends StubActivity {\n\t}\n\n\tpublic static class C53 extends StubActivity {\n\t}\n\n\tpublic static class C54 extends StubActivity {\n\t}\n\n\tpublic static class C55 extends StubActivity {\n\t}\n\n\tpublic static class C56 extends StubActivity {\n\t}\n\n\tpublic static class C57 extends StubActivity {\n\t}\n\n\tpublic static class C58 extends StubActivity {\n\t}\n\n\tpublic static class C59 extends StubActivity {\n\t}\n\n\tpublic static class C60 extends StubActivity {\n\t}\n\n\tpublic static class C61 extends StubActivity {\n\t}\n\n\tpublic static class C62 extends StubActivity {\n\t}\n\n\tpublic static class C63 extends StubActivity {\n\t}\n\n\tpublic static class C64 extends StubActivity {\n\t}\n\n\tpublic static class C65 extends StubActivity {\n\t}\n\n\tpublic static class C66 extends StubActivity {\n\t}\n\n\tpublic static class C67 extends StubActivity {\n\t}\n\n\tpublic static class C68 extends StubActivity {\n\t}\n\n\tpublic static class C69 extends StubActivity {\n\t}\n\n\tpublic static class C70 extends StubActivity {\n\t}\n\n\tpublic static class C71 extends StubActivity {\n\t}\n\n\tpublic static class C72 extends StubActivity {\n\t}\n\n\tpublic static class C73 extends StubActivity {\n\t}\n\n\tpublic static class C74 extends StubActivity {\n\t}\n\n\tpublic static class C75 extends StubActivity {\n\t}\n\n\tpublic static class C76 extends StubActivity {\n\t}\n\n\tpublic static class C77 extends StubActivity {\n\t}\n\n\tpublic static class C78 extends StubActivity {\n\t}\n\n\tpublic static class C79 extends StubActivity {\n\t}\n\n\tpublic static class C80 extends StubActivity {\n\t}\n\n\tpublic static class C81 extends StubActivity {\n\t}\n\n\tpublic static class C82 extends StubActivity {\n\t}\n\n\tpublic static class C83 extends StubActivity {\n\t}\n\n\tpublic static class C84 extends StubActivity {\n\t}\n\n\tpublic static class C85 extends StubActivity {\n\t}\n\n\tpublic static class C86 extends StubActivity {\n\t}\n\n\tpublic static class C87 extends StubActivity {\n\t}\n\n\tpublic static class C88 extends StubActivity {\n\t}\n\n\tpublic static class C89 extends StubActivity {\n\t}\n\n\tpublic static class C90 extends StubActivity {\n\t}\n\n\tpublic static class C91 extends StubActivity {\n\t}\n\n\tpublic static class C92 extends StubActivity {\n\t}\n\n\tpublic static class C93 extends StubActivity {\n\t}\n\n\tpublic static class C94 extends StubActivity {\n\t}\n\n\tpublic static class C95 extends StubActivity {\n\t}\n\n\tpublic static class C96 extends StubActivity {\n\t}\n\n\tpublic static class C97 extends StubActivity {\n\t}\n\n\tpublic static class C98 extends StubActivity {\n\t}\n\n\tpublic static class C99 extends StubActivity {\n\t}\n\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubContentProvider.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.ConditionVariable;\nimport android.os.IBinder;\nimport android.os.Process;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.compat.BundleCompat;\n\n/**\n * @author Lody\n *\n */\npublic class StubContentProvider extends ContentProvider {\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Bundle call(String method, String arg, Bundle extras) {\n\t\tif (\"_VA_|_init_process_\".equals(method)) {\n\t\t\treturn initProcess(extras);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate Bundle initProcess(Bundle extras) {\n\t\tConditionVariable lock = VirtualCore.get().getInitLock();\n\t\tif (lock != null) {\n\t\t\tlock.block();\n\t\t}\n\t\tIBinder token = BundleCompat.getBinder(extras,\"_VA_|_binder_\");\n\t\tint vuid = extras.getInt(\"_VA_|_vuid_\");\n\t\tVClientImpl client = VClientImpl.get();\n\t\tclient.initProcess(token, vuid);\n\t\tBundle res = new Bundle();\n\t\tBundleCompat.putBinder(res, \"_VA_|_client_\", client.asBinder());\n\t\tres.putInt(\"_VA_|_pid_\", Process.myPid());\n\t\treturn res;\n\t}\n\n\t@Override\n\tpublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getType(Uri uri) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Uri insert(Uri uri, ContentValues values) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int delete(Uri uri, String selection, String[] selectionArgs) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {\n\t\treturn 0;\n\t}\n\n\n\tpublic static class C0 extends StubContentProvider {\n\t}\n\n\tpublic static class C1 extends StubContentProvider {\n\t}\n\n\tpublic static class C2 extends StubContentProvider {\n\t}\n\n\tpublic static class C3 extends StubContentProvider {\n\t}\n\n\tpublic static class C4 extends StubContentProvider {\n\t}\n\n\tpublic static class C5 extends StubContentProvider {\n\t}\n\n\tpublic static class C6 extends StubContentProvider {\n\t}\n\n\tpublic static class C7 extends StubContentProvider {\n\t}\n\n\tpublic static class C8 extends StubContentProvider {\n\t}\n\n\tpublic static class C9 extends StubContentProvider {\n\t}\n\n\tpublic static class C10 extends StubContentProvider {\n\t}\n\n\tpublic static class C11 extends StubContentProvider {\n\t}\n\n\tpublic static class C12 extends StubContentProvider {\n\t}\n\n\tpublic static class C13 extends StubContentProvider {\n\t}\n\n\tpublic static class C14 extends StubContentProvider {\n\t}\n\n\tpublic static class C15 extends StubContentProvider {\n\t}\n\n\tpublic static class C16 extends StubContentProvider {\n\t}\n\n\tpublic static class C17 extends StubContentProvider {\n\t}\n\n\tpublic static class C18 extends StubContentProvider {\n\t}\n\n\tpublic static class C19 extends StubContentProvider {\n\t}\n\n\tpublic static class C20 extends StubContentProvider {\n\t}\n\n\tpublic static class C21 extends StubContentProvider {\n\t}\n\n\tpublic static class C22 extends StubContentProvider {\n\t}\n\n\tpublic static class C23 extends StubContentProvider {\n\t}\n\n\tpublic static class C24 extends StubContentProvider {\n\t}\n\n\tpublic static class C25 extends StubContentProvider {\n\t}\n\n\tpublic static class C26 extends StubContentProvider {\n\t}\n\n\tpublic static class C27 extends StubContentProvider {\n\t}\n\n\tpublic static class C28 extends StubContentProvider {\n\t}\n\n\tpublic static class C29 extends StubContentProvider {\n\t}\n\n\tpublic static class C30 extends StubContentProvider {\n\t}\n\n\tpublic static class C31 extends StubContentProvider {\n\t}\n\n\tpublic static class C32 extends StubContentProvider {\n\t}\n\n\tpublic static class C33 extends StubContentProvider {\n\t}\n\n\tpublic static class C34 extends StubContentProvider {\n\t}\n\n\tpublic static class C35 extends StubContentProvider {\n\t}\n\n\tpublic static class C36 extends StubContentProvider {\n\t}\n\n\tpublic static class C37 extends StubContentProvider {\n\t}\n\n\tpublic static class C38 extends StubContentProvider {\n\t}\n\n\tpublic static class C39 extends StubContentProvider {\n\t}\n\n\tpublic static class C40 extends StubContentProvider {\n\t}\n\n\tpublic static class C41 extends StubContentProvider {\n\t}\n\n\tpublic static class C42 extends StubContentProvider {\n\t}\n\n\tpublic static class C43 extends StubContentProvider {\n\t}\n\n\tpublic static class C44 extends StubContentProvider {\n\t}\n\n\tpublic static class C45 extends StubContentProvider {\n\t}\n\n\tpublic static class C46 extends StubContentProvider {\n\t}\n\n\tpublic static class C47 extends StubContentProvider {\n\t}\n\n\tpublic static class C48 extends StubContentProvider {\n\t}\n\n\tpublic static class C49 extends StubContentProvider {\n\t}\n\n\tpublic static class C50 extends StubContentProvider {\n\t}\n\n\tpublic static class C51 extends StubContentProvider {\n\t}\n\n\tpublic static class C52 extends StubContentProvider {\n\t}\n\n\tpublic static class C53 extends StubContentProvider {\n\t}\n\n\tpublic static class C54 extends StubContentProvider {\n\t}\n\n\tpublic static class C55 extends StubContentProvider {\n\t}\n\n\tpublic static class C56 extends StubContentProvider {\n\t}\n\n\tpublic static class C57 extends StubContentProvider {\n\t}\n\n\tpublic static class C58 extends StubContentProvider {\n\t}\n\n\tpublic static class C59 extends StubContentProvider {\n\t}\n\n\tpublic static class C60 extends StubContentProvider {\n\t}\n\n\tpublic static class C61 extends StubContentProvider {\n\t}\n\n\tpublic static class C62 extends StubContentProvider {\n\t}\n\n\tpublic static class C63 extends StubContentProvider {\n\t}\n\n\tpublic static class C64 extends StubContentProvider {\n\t}\n\n\tpublic static class C65 extends StubContentProvider {\n\t}\n\n\tpublic static class C66 extends StubContentProvider {\n\t}\n\n\tpublic static class C67 extends StubContentProvider {\n\t}\n\n\tpublic static class C68 extends StubContentProvider {\n\t}\n\n\tpublic static class C69 extends StubContentProvider {\n\t}\n\n\tpublic static class C70 extends StubContentProvider {\n\t}\n\n\tpublic static class C71 extends StubContentProvider {\n\t}\n\n\tpublic static class C72 extends StubContentProvider {\n\t}\n\n\tpublic static class C73 extends StubContentProvider {\n\t}\n\n\tpublic static class C74 extends StubContentProvider {\n\t}\n\n\tpublic static class C75 extends StubContentProvider {\n\t}\n\n\tpublic static class C76 extends StubContentProvider {\n\t}\n\n\tpublic static class C77 extends StubContentProvider {\n\t}\n\n\tpublic static class C78 extends StubContentProvider {\n\t}\n\n\tpublic static class C79 extends StubContentProvider {\n\t}\n\n\tpublic static class C80 extends StubContentProvider {\n\t}\n\n\tpublic static class C81 extends StubContentProvider {\n\t}\n\n\tpublic static class C82 extends StubContentProvider {\n\t}\n\n\tpublic static class C83 extends StubContentProvider {\n\t}\n\n\tpublic static class C84 extends StubContentProvider {\n\t}\n\n\tpublic static class C85 extends StubContentProvider {\n\t}\n\n\tpublic static class C86 extends StubContentProvider {\n\t}\n\n\tpublic static class C87 extends StubContentProvider {\n\t}\n\n\tpublic static class C88 extends StubContentProvider {\n\t}\n\n\tpublic static class C89 extends StubContentProvider {\n\t}\n\n\tpublic static class C90 extends StubContentProvider {\n\t}\n\n\tpublic static class C91 extends StubContentProvider {\n\t}\n\n\tpublic static class C92 extends StubContentProvider {\n\t}\n\n\tpublic static class C93 extends StubContentProvider {\n\t}\n\n\tpublic static class C94 extends StubContentProvider {\n\t}\n\n\tpublic static class C95 extends StubContentProvider {\n\t}\n\n\tpublic static class C96 extends StubContentProvider {\n\t}\n\n\tpublic static class C97 extends StubContentProvider {\n\t}\n\n\tpublic static class C98 extends StubContentProvider {\n\t}\n\n\tpublic static class C99 extends StubContentProvider {\n\t}\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubDialog.java",
    "content": "package com.lody.virtual.client.stub;\n\n/**\n * @author Lody\n *\n */\npublic abstract class StubDialog extends StubActivity {\n\n\n\tpublic static class C0 extends StubDialog {\n\t}\n\n\tpublic static class C1 extends StubDialog {\n\t}\n\n\tpublic static class C2 extends StubDialog {\n\t}\n\n\tpublic static class C3 extends StubDialog {\n\t}\n\n\tpublic static class C4 extends StubDialog {\n\t}\n\n\tpublic static class C5 extends StubDialog {\n\t}\n\n\tpublic static class C6 extends StubDialog {\n\t}\n\n\tpublic static class C7 extends StubDialog {\n\t}\n\n\tpublic static class C8 extends StubDialog {\n\t}\n\n\tpublic static class C9 extends StubDialog {\n\t}\n\n\tpublic static class C10 extends StubDialog {\n\t}\n\n\tpublic static class C11 extends StubDialog {\n\t}\n\n\tpublic static class C12 extends StubDialog {\n\t}\n\n\tpublic static class C13 extends StubDialog {\n\t}\n\n\tpublic static class C14 extends StubDialog {\n\t}\n\n\tpublic static class C15 extends StubDialog {\n\t}\n\n\tpublic static class C16 extends StubDialog {\n\t}\n\n\tpublic static class C17 extends StubDialog {\n\t}\n\n\tpublic static class C18 extends StubDialog {\n\t}\n\n\tpublic static class C19 extends StubDialog {\n\t}\n\n\tpublic static class C20 extends StubDialog {\n\t}\n\n\tpublic static class C21 extends StubDialog {\n\t}\n\n\tpublic static class C22 extends StubDialog {\n\t}\n\n\tpublic static class C23 extends StubDialog {\n\t}\n\n\tpublic static class C24 extends StubDialog {\n\t}\n\n\tpublic static class C25 extends StubDialog {\n\t}\n\n\tpublic static class C26 extends StubDialog {\n\t}\n\n\tpublic static class C27 extends StubDialog {\n\t}\n\n\tpublic static class C28 extends StubDialog {\n\t}\n\n\tpublic static class C29 extends StubDialog {\n\t}\n\n\tpublic static class C30 extends StubDialog {\n\t}\n\n\tpublic static class C31 extends StubDialog {\n\t}\n\n\tpublic static class C32 extends StubDialog {\n\t}\n\n\tpublic static class C33 extends StubDialog {\n\t}\n\n\tpublic static class C34 extends StubDialog {\n\t}\n\n\tpublic static class C35 extends StubDialog {\n\t}\n\n\tpublic static class C36 extends StubDialog {\n\t}\n\n\tpublic static class C37 extends StubDialog {\n\t}\n\n\tpublic static class C38 extends StubDialog {\n\t}\n\n\tpublic static class C39 extends StubDialog {\n\t}\n\n\tpublic static class C40 extends StubDialog {\n\t}\n\n\tpublic static class C41 extends StubDialog {\n\t}\n\n\tpublic static class C42 extends StubDialog {\n\t}\n\n\tpublic static class C43 extends StubDialog {\n\t}\n\n\tpublic static class C44 extends StubDialog {\n\t}\n\n\tpublic static class C45 extends StubDialog {\n\t}\n\n\tpublic static class C46 extends StubDialog {\n\t}\n\n\tpublic static class C47 extends StubDialog {\n\t}\n\n\tpublic static class C48 extends StubDialog {\n\t}\n\n\tpublic static class C49 extends StubDialog {\n\t}\n\n\tpublic static class C50 extends StubDialog {\n\t}\n\n\tpublic static class C51 extends StubDialog {\n\t}\n\n\tpublic static class C52 extends StubDialog {\n\t}\n\n\tpublic static class C53 extends StubDialog {\n\t}\n\n\tpublic static class C54 extends StubDialog {\n\t}\n\n\tpublic static class C55 extends StubDialog {\n\t}\n\n\tpublic static class C56 extends StubDialog {\n\t}\n\n\tpublic static class C57 extends StubDialog {\n\t}\n\n\tpublic static class C58 extends StubDialog {\n\t}\n\n\tpublic static class C59 extends StubDialog {\n\t}\n\n\tpublic static class C60 extends StubDialog {\n\t}\n\n\tpublic static class C61 extends StubDialog {\n\t}\n\n\tpublic static class C62 extends StubDialog {\n\t}\n\n\tpublic static class C63 extends StubDialog {\n\t}\n\n\tpublic static class C64 extends StubDialog {\n\t}\n\n\tpublic static class C65 extends StubDialog {\n\t}\n\n\tpublic static class C66 extends StubDialog {\n\t}\n\n\tpublic static class C67 extends StubDialog {\n\t}\n\n\tpublic static class C68 extends StubDialog {\n\t}\n\n\tpublic static class C69 extends StubDialog {\n\t}\n\n\tpublic static class C70 extends StubDialog {\n\t}\n\n\tpublic static class C71 extends StubDialog {\n\t}\n\n\tpublic static class C72 extends StubDialog {\n\t}\n\n\tpublic static class C73 extends StubDialog {\n\t}\n\n\tpublic static class C74 extends StubDialog {\n\t}\n\n\tpublic static class C75 extends StubDialog {\n\t}\n\n\tpublic static class C76 extends StubDialog {\n\t}\n\n\tpublic static class C77 extends StubDialog {\n\t}\n\n\tpublic static class C78 extends StubDialog {\n\t}\n\n\tpublic static class C79 extends StubDialog {\n\t}\n\n\tpublic static class C80 extends StubDialog {\n\t}\n\n\tpublic static class C81 extends StubDialog {\n\t}\n\n\tpublic static class C82 extends StubDialog {\n\t}\n\n\tpublic static class C83 extends StubDialog {\n\t}\n\n\tpublic static class C84 extends StubDialog {\n\t}\n\n\tpublic static class C85 extends StubDialog {\n\t}\n\n\tpublic static class C86 extends StubDialog {\n\t}\n\n\tpublic static class C87 extends StubDialog {\n\t}\n\n\tpublic static class C88 extends StubDialog {\n\t}\n\n\tpublic static class C89 extends StubDialog {\n\t}\n\n\tpublic static class C90 extends StubDialog {\n\t}\n\n\tpublic static class C91 extends StubDialog {\n\t}\n\n\tpublic static class C92 extends StubDialog {\n\t}\n\n\tpublic static class C93 extends StubDialog {\n\t}\n\n\tpublic static class C94 extends StubDialog {\n\t}\n\n\tpublic static class C95 extends StubDialog {\n\t}\n\n\tpublic static class C96 extends StubDialog {\n\t}\n\n\tpublic static class C97 extends StubDialog {\n\t}\n\n\tpublic static class C98 extends StubDialog {\n\t}\n\n\tpublic static class C99 extends StubDialog {\n\t}\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubJob.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.annotation.TargetApi;\nimport android.app.Service;\nimport android.app.job.IJobCallback;\nimport android.app.job.IJobService;\nimport android.app.job.JobParameters;\nimport android.app.job.JobScheduler;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.InvocationStubManager;\nimport com.lody.virtual.client.hook.proxies.am.ActivityManagerStub;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.helper.collection.SparseArray;\nimport com.lody.virtual.os.VUserHandle;\n\nimport java.util.Map;\n\nimport static com.lody.virtual.server.job.VJobSchedulerService.JobConfig;\nimport static com.lody.virtual.server.job.VJobSchedulerService.JobId;\nimport static com.lody.virtual.server.job.VJobSchedulerService.get;\n\n/**\n * @author Lody\n *         <p>\n *         This service running on the Server process.\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class StubJob extends Service {\n\n    private static final String TAG = StubJob.class.getSimpleName();\n    private final SparseArray<JobSession> mJobSessions = new SparseArray<>();\n    private JobScheduler mScheduler;\n    private final IJobService mService = new IJobService.Stub() {\n\n        @Override\n        public void startJob(JobParameters jobParams) throws RemoteException {\n            int jobId = jobParams.getJobId();\n            IBinder binder = mirror.android.app.job.JobParameters.callback.get(jobParams);\n            IJobCallback callback = IJobCallback.Stub.asInterface(binder);\n            Map.Entry<JobId, JobConfig> entry = get().findJobByVirtualJobId(jobId);\n            if (entry == null) {\n                emptyCallback(callback, jobId);\n                mScheduler.cancel(jobId);\n            } else {\n                JobId key = entry.getKey();\n                JobConfig config = entry.getValue();\n                synchronized (mJobSessions) {\n                    JobSession session = mJobSessions.get(jobId);\n                    if (session != null) {\n                        // Job Session has exist.\n                        emptyCallback(callback, jobId);\n                    } else {\n                        session = new JobSession(jobId, callback, jobParams);\n                        mirror.android.app.job.JobParameters.callback.set(jobParams, session.asBinder());\n                        mirror.android.app.job.JobParameters.jobId.set(jobParams, key.clientJobId);\n                        Intent service = new Intent();\n                        service.setComponent(new ComponentName(key.packageName, config.serviceName));\n                        service.putExtra(\"_VA_|_user_id_\", VUserHandle.getUserId(key.vuid));\n                        boolean bound = false;\n                        try {\n                            bound = bindService(service, session, 0);\n                        } catch (Throwable e) {\n                            VLog.e(TAG, e);\n                        }\n                        if (bound) {\n                            mJobSessions.put(jobId, session);\n                        } else {\n                            emptyCallback(callback, jobId);\n                            mScheduler.cancel(jobId);\n                            get().cancel(jobId);\n                        }\n                    }\n                }\n            }\n        }\n\n        @Override\n        public void stopJob(JobParameters jobParams) throws RemoteException {\n            int jobId = jobParams.getJobId();\n            synchronized (mJobSessions) {\n                JobSession session = mJobSessions.get(jobId);\n                if (session != null) {\n                    session.stopSession();\n                }\n            }\n        }\n    };\n\n    /**\n     * Make JobScheduler happy.\n     */\n    private void emptyCallback(IJobCallback callback, int jobId) {\n        try {\n            callback.acknowledgeStartMessage(jobId, false);\n            callback.jobFinished(jobId, false);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        InvocationStubManager.getInstance().checkEnv(ActivityManagerStub.class);\n        mScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);\n    }\n\n    @Override\n    public IBinder onBind(Intent intent) {\n        return mService.asBinder();\n    }\n\n    private final class JobSession extends IJobCallback.Stub implements ServiceConnection {\n\n        private int jobId;\n        private IJobCallback clientCallback;\n        private JobParameters jobParams;\n        private IJobService clientJobService;\n\n        JobSession(int jobId, IJobCallback clientCallback, JobParameters jobParams) {\n            this.jobId = jobId;\n            this.clientCallback = clientCallback;\n            this.jobParams = jobParams;\n        }\n\n        @Override\n        public void acknowledgeStartMessage(int jobId, boolean ongoing) throws RemoteException {\n            clientCallback.acknowledgeStartMessage(jobId, ongoing);\n        }\n\n        @Override\n        public void acknowledgeStopMessage(int jobId, boolean reschedule) throws RemoteException {\n            clientCallback.acknowledgeStopMessage(jobId, reschedule);\n        }\n\n        @Override\n        public void jobFinished(int jobId, boolean reschedule) throws RemoteException {\n            clientCallback.jobFinished(jobId, reschedule);\n        }\n\n        @Override\n        public void onServiceConnected(ComponentName name, IBinder service) {\n            clientJobService = IJobService.Stub.asInterface(service);\n            if (clientJobService == null) {\n                emptyCallback(clientCallback, jobId);\n                stopSession();\n                return;\n            }\n            try {\n                clientJobService.startJob(jobParams);\n            } catch (RemoteException e) {\n                forceFinishJob();\n                e.printStackTrace();\n            }\n        }\n\n        @Override\n        public void onServiceDisconnected(ComponentName name) {\n\n        }\n\n        void forceFinishJob() {\n            try {\n                clientCallback.jobFinished(jobId, false);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            } finally {\n                stopSession();\n            }\n        }\n\n        void stopSession() {\n            if (clientJobService != null) {\n                try {\n                    clientJobService.stopJob(jobParams);\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n            mJobSessions.remove(jobId);\n            unbindService(this);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubPendingActivity.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\n\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.remote.StubActivityRecord;\n\n/**\n * @author Lody\n */\n\npublic class StubPendingActivity extends Activity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        finish();\n        Intent intent = getIntent();\n        StubActivityRecord r = new StubActivityRecord(intent);\n        if (r.intent == null) {\n            return;\n        }\n        r.intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);\n        VActivityManager.get().startActivity(r.intent, r.userId);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubPendingReceiver.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\n\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.os.VUserHandle;\n\n/**\n * @author Lody\n */\n\npublic class StubPendingReceiver extends BroadcastReceiver {\n\n    @Override\n    public void onReceive(Context context, Intent intent) {\n                Intent realIntent = intent.getParcelableExtra(\"_VA_|_intent_\");\n        int userId = intent.getIntExtra(\"_VA_|_user_id_\", VUserHandle.USER_ALL);\n        if (realIntent != null) {\n            Intent newIntent = ComponentUtils.redirectBroadcastIntent(realIntent, userId);\n            if (newIntent != null) {\n                context.sendBroadcast(newIntent);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/StubPendingService.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport com.lody.virtual.client.ipc.VActivityManager;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\n\n\n/**\n * @author Lody\n */\n\npublic class StubPendingService extends Service {\n    @Override\n    public IBinder onBind(Intent intent) {\n        return null;\n    }\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        // _VA_|_from_inner_ marked\n        if (intent != null) {\n            Intent realIntent = intent.getParcelableExtra(\"_VA_|_intent_\");\n            int userId = intent.getIntExtra(\"_VA_|_user_id_\", 0);\n            if (realIntent != null) {\n                VActivityManager.get().startService(null, realIntent, null, userId);\n            }\n        }\n        stopSelf();\n        return START_NOT_STICKY;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/client/stub/VASettings.java",
    "content": "package com.lody.virtual.client.stub;\n\nimport java.util.Locale;\n\n/**\n * @author Lody\n */\n\npublic class VASettings {\n    public static final String STUB_DEF_AUTHORITY = \"virtual_stub_\";\n    public static String STUB_ACTIVITY = StubActivity.class.getName();\n    public static String STUB_DIALOG = StubDialog.class.getName();\n    public static String STUB_CP = StubContentProvider.class.getName();\n    public static String STUB_JOB = StubJob.class.getName();\n    public static String RESOLVER_ACTIVITY = ResolverActivity.class.getName();\n    public static String STUB_CP_AUTHORITY = \"virtual_stub_\";\n    public static int STUB_COUNT = 50;\n    public static String[] PRIVILEGE_APPS = new String[]{\n            \"com.google.android.gms\"\n    };\n\n    /**\n     * If enable,\n     * App run in VA will allowed to create shortcut on your Desktop.\n     */\n    public static boolean ENABLE_INNER_SHORTCUT = true;\n\n    /**\n     * If enable,\n     * For example:\n     * when app access '/data/data/{Package Name}' or '/data/user/0/{Package Name}',\n     * we redirect it to '/data/data/{Your Host Package Name}/virtual/user/0/{Package Name}'.\n     */\n    public static boolean ENABLE_IO_REDIRECT = true;\n\n    public static String getStubActivityName(int index) {\n        return String.format(Locale.ENGLISH, \"%s$C%d\", STUB_ACTIVITY, index);\n    }\n\n    public static String getStubDialogName(int index) {\n        return String.format(Locale.ENGLISH, \"%s$C%d\", STUB_DIALOG, index);\n    }\n\n    public static String getStubCP(int index) {\n        return String.format(Locale.ENGLISH, \"%s$C%d\", STUB_CP, index);\n    }\n\n    public static String getStubAuthority(int index) {\n        return String.format(Locale.ENGLISH, \"%s%d\", STUB_CP_AUTHORITY, index);\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/ParcelHelper.java",
    "content": "package com.lody.virtual.helper;\n\nimport android.os.Bundle;\nimport android.os.Parcel;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class ParcelHelper {\n\n    public static void writeMeta(Parcel p, Bundle meta) {\n        Map<String, String> map = new HashMap<>();\n        if (meta != null) {\n            for (String key : meta.keySet()) {\n                map.put(key, meta.getString(key));\n            }\n        }\n        p.writeMap(map);\n    }\n\n    public static Bundle readMeta(Parcel p) {\n        Bundle meta = new Bundle();\n        //noinspection unchecked\n        Map<String, String> map = p.readHashMap(String.class.getClassLoader());\n        for (Map.Entry<String, String> entry : map.entrySet()) {\n            meta.putString(entry.getKey(), entry.getValue());\n        }\n        return meta;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/PersistenceLayer.java",
    "content": "package com.lody.virtual.helper;\n\nimport android.os.Parcel;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\n/**\n * @author Lody\n */\npublic abstract class PersistenceLayer {\n\n    private File mPersistenceFile;\n\n    public PersistenceLayer(File persistenceFile) {\n        this.mPersistenceFile = persistenceFile;\n    }\n\n    public final File getPersistenceFile() {\n        return mPersistenceFile;\n    }\n    public abstract int getCurrentVersion();\n\n    public abstract void writeMagic(Parcel p);\n\n    public abstract boolean verifyMagic(Parcel p);\n\n    public abstract void writePersistenceData(Parcel p);\n\n    public abstract void readPersistenceData(Parcel p);\n\n    public abstract boolean onVersionConflict(int fileVersion, int currentVersion);\n\n    public abstract void onPersistenceFileDamage();\n\n    public void save() {\n        Parcel p = Parcel.obtain();\n        try {\n            writeMagic(p);\n            p.writeInt(getCurrentVersion());\n            writePersistenceData(p);\n            FileOutputStream fos = new FileOutputStream(mPersistenceFile);\n            fos.write(p.marshall());\n            fos.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            p.recycle();\n        }\n    }\n\n    public void read() {\n        File file = mPersistenceFile;\n        Parcel p = Parcel.obtain();\n        try {\n            FileInputStream fis = new FileInputStream(file);\n            byte[] bytes = new byte[(int) file.length()];\n            int len = fis.read(bytes);\n            fis.close();\n            if (len != bytes.length) {\n                throw new IOException(\"Unable to read Persistence file.\");\n            }\n            p.unmarshall(bytes, 0, bytes.length);\n            p.setDataPosition(0);\n            if (!verifyMagic(p)) {\n                onPersistenceFileDamage();\n                throw new IOException(\"Invalid persistence file.\");\n            }\n            int fileVersion = p.readInt();\n            int currentVersion = getCurrentVersion();\n            if (fileVersion != getCurrentVersion()) {\n                if (!onVersionConflict(fileVersion, currentVersion)) {\n                    throw new IOException(\"Unable to process the bad version persistence file.\");\n                }\n            }\n            readPersistenceData(p);\n        } catch (Exception e) {\n            if (!(e instanceof FileNotFoundException)) {\n                e.printStackTrace();\n            }\n        } finally {\n            p.recycle();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/ArrayMap.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.collection;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * ArrayMap is a generic key->value mapping data structure that is\n * designed to be more memory efficient than a traditional {@link java.util.HashMap},\n * this implementation is a version of the platform's\n * {@link android.util.ArrayMap} that can be used on older versions of the platform.\n * It keeps its mappings in an array data structure -- an integer array of hash\n * codes for each item, and an Object array of the key/value pairs.  This allows it to\n * avoid having to create an extra object for every entry put in to the map, and it\n * also tries to control the growth of the size of these arrays more aggressively\n * (since growing them only requires copying the entries in the array, not rebuilding\n * a hash map).\n *\n * <p>If you don't need the standard Java container APIs provided here (iterators etc),\n * consider using {@link SimpleArrayMap} instead.</p>\n *\n * <p>Note that this implementation is not intended to be appropriate for data structures\n * that may contain large numbers of items.  It is generally slower than a traditional\n * HashMap, since lookups require a binary search and adds and removes require inserting\n * and deleting entries in the array.  For containers holding up to hundreds of items,\n * the performance difference is not significant, less than 50%.</p>\n *\n * <p>Because this container is intended to better balance memory use, unlike most other\n * standard Java containers it will shrink its array as items are removed from it.  Currently\n * you have no control over this shrinking -- if you set a capacity and then remove an\n * item, it may reduce the capacity to better match the current size.  In the future an\n * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>\n */\npublic class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> {\n    MapCollections<K, V> mCollections;\n\n    public ArrayMap() {\n        super();\n    }\n\n    /**\n     * Create a new ArrayMap with a given initial capacity.\n     */\n    public ArrayMap(int capacity) {\n        super(capacity);\n    }\n\n    /**\n     * Create a new ArrayMap with the mappings from the given ArrayMap.\n     */\n    public ArrayMap(SimpleArrayMap map) {\n        super(map);\n    }\n\n    private MapCollections<K, V> getCollection() {\n        if (mCollections == null) {\n            mCollections = new MapCollections<K, V>() {\n                @Override\n                protected int colGetSize() {\n                    return mSize;\n                }\n\n                @Override\n                protected Object colGetEntry(int index, int offset) {\n                    return mArray[(index<<1) + offset];\n                }\n\n                @Override\n                protected int colIndexOfKey(Object key) {\n                    return indexOfKey(key);\n                }\n\n                @Override\n                protected int colIndexOfValue(Object value) {\n                    return indexOfValue(value);\n                }\n\n                @Override\n                protected Map<K, V> colGetMap() {\n                    return ArrayMap.this;\n                }\n\n                @Override\n                protected void colPut(K key, V value) {\n                    put(key, value);\n                }\n\n                @Override\n                protected V colSetValue(int index, V value) {\n                    return setValueAt(index, value);\n                }\n\n                @Override\n                protected void colRemoveAt(int index) {\n                    removeAt(index);\n                }\n\n                @Override\n                protected void colClear() {\n                    clear();\n                }\n            };\n        }\n        return mCollections;\n    }\n\n    /**\n     * Determine if the array map contains all of the keys in the given collection.\n     * @param collection The collection whose contents are to be checked against.\n     * @return Returns true if this array map contains a key for every entry\n     * in <var>collection</var>, else returns false.\n     */\n    public boolean containsAll(Collection<?> collection) {\n        return MapCollections.containsAllHelper(this, collection);\n    }\n\n    /**\n     * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>map</var>\n     * @param map The map whose contents are to be retrieved.\n     */\n    @Override\n    public void putAll(Map<? extends K, ? extends V> map) {\n        ensureCapacity(mSize + map.size());\n        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {\n            put(entry.getKey(), entry.getValue());\n        }\n    }\n\n    /**\n     * Remove all keys in the array map that exist in the given collection.\n     * @param collection The collection whose contents are to be used to remove keys.\n     * @return Returns true if any keys were removed from the array map, else false.\n     */\n    public boolean removeAll(Collection<?> collection) {\n        return MapCollections.removeAllHelper(this, collection);\n    }\n\n    /**\n     * Remove all keys in the array map that do <b>not</b> exist in the given collection.\n     * @param collection The collection whose contents are to be used to determine which\n     * keys to keep.\n     * @return Returns true if any keys were removed from the array map, else false.\n     */\n    public boolean retainAll(Collection<?> collection) {\n        return MapCollections.retainAllHelper(this, collection);\n    }\n\n    /**\n     * Return a {@link java.util.Set} for iterating over and interacting with all mappings\n     * in the array map.\n     *\n     * <p><b>Note:</b> this is a very inefficient way to access the array contents, it\n     * requires generating a number of temporary objects.</p>\n     *\n     * <p><b>Note:</b></p> the semantics of this\n     * Set are subtly different than that of a {@link java.util.HashMap}: most important,\n     * the {@link java.util.Map.Entry Map.Entry} object returned by its iterator is a single\n     * object that exists for the entire iterator, so you can <b>not</b> hold on to it\n     * after calling {@link java.util.Iterator#next() Iterator.next}.</p>\n     */\n    @Override\n    public Set<Entry<K, V>> entrySet() {\n        return getCollection().getEntrySet();\n    }\n\n    /**\n     * Return a {@link java.util.Set} for iterating over and interacting with all keys\n     * in the array map.\n     *\n     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it\n     * requires generating a number of temporary objects.</p>\n     */\n    @Override\n    public Set<K> keySet() {\n        return getCollection().getKeySet();\n    }\n\n    /**\n     * Return a {@link java.util.Collection} for iterating over and interacting with all values\n     * in the array map.\n     *\n     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it\n     * requires generating a number of temporary objects.</p>\n     */\n    @Override\n    public Collection<V> values() {\n        return getCollection().getValues();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/ArraySet.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.collection;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Array;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\n\n/**\n * ArraySet is a generic set data structure that is designed to be more memory efficient than a\n * traditional {@link java.util.HashSet}.  The design is very similar to\n * {@link ArrayMap}, with all of the caveats described there.  This implementation is\n * separate from ArrayMap, however, so the Object array contains only one item for each\n * entry in the set (instead of a pair for a mapping).\n *\n * <p>Note that this implementation is not intended to be appropriate for data structures\n * that may contain large numbers of items.  It is generally slower than a traditional\n * HashSet, since lookups require a binary search and adds and removes require inserting\n * and deleting entries in the array.  For containers holding up to hundreds of items,\n * the performance difference is not significant, less than 50%.</p>\n *\n * <p>Because this container is intended to better balance memory use, unlike most other\n * standard Java containers it will shrink its array as items are removed from it.  Currently\n * you have no control over this shrinking -- if you set a capacity and then remove an\n * item, it may reduce the capacity to better match the current size.  In the future an\n * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>\n */\npublic final class ArraySet<E> implements Collection<E>, Set<E> {\n    private static final boolean DEBUG = false;\n    private static final String TAG = \"ArraySet\";\n\n    /**\n     * The minimum amount by which the capacity of a ArraySet will increase.\n     * This is tuned to be relatively space-efficient.\n     */\n    private static final int BASE_SIZE = 4;\n\n    /**\n     * Maximum number of entries to have in array caches.\n     */\n    private static final int CACHE_SIZE = 10;\n\n    /**\n     * Caches of small array objects to avoid spamming garbage.  The cache\n     * Object[] variable is a pointer to a linked list of array objects.\n     * The first entry in the array is a pointer to the next array in the\n     * list; the second entry is a pointer to the int[] hash code array for it.\n     */\n    static Object[] mBaseCache;\n    static int mBaseCacheSize;\n    static Object[] mTwiceBaseCache;\n    static int mTwiceBaseCacheSize;\n\n    int[] mHashes;\n    Object[] mArray;\n    int mSize;\n    MapCollections<E, E> mCollections;\n\n    /**\n     * Create a new empty ArraySet.  The default capacity of an array map is 0, and\n     * will grow once items are added to it.\n     */\n    public ArraySet() {\n        mHashes = ContainerHelpers.EMPTY_INTS;\n        mArray = ContainerHelpers.EMPTY_OBJECTS;\n        mSize = 0;\n    }\n\n    /**\n     * Create a new ArraySet with a given initial capacity.\n     */\n    public ArraySet(int capacity) {\n        if (capacity == 0) {\n            mHashes = ContainerHelpers.EMPTY_INTS;\n            mArray = ContainerHelpers.EMPTY_OBJECTS;\n        } else {\n            allocArrays(capacity);\n        }\n        mSize = 0;\n    }\n\n    /**\n     * Create a new ArraySet with the mappings from the given ArraySet.\n     */\n    public ArraySet(ArraySet<E> set) {\n        this();\n        if (set != null) {\n            addAll(set);\n        }\n    }\n\n    /** {@hide} */\n    public ArraySet(Collection<E> set) {\n        this();\n        if (set != null) {\n            addAll(set);\n        }\n    }\n\n    private static void freeArrays(final int[] hashes, final Object[] array, final int size) {\n        if (hashes.length == (BASE_SIZE*2)) {\n            synchronized (ArraySet.class) {\n                if (mTwiceBaseCacheSize < CACHE_SIZE) {\n                    array[0] = mTwiceBaseCache;\n                    array[1] = hashes;\n                    for (int i=size-1; i>=2; i--) {\n                        array[i] = null;\n                    }\n                    mTwiceBaseCache = array;\n                    mTwiceBaseCacheSize++;\n                    if (DEBUG) Log.d(TAG, \"Storing 2x cache \" + array\n                            + \" now have \" + mTwiceBaseCacheSize + \" entries\");\n                }\n            }\n        } else if (hashes.length == BASE_SIZE) {\n            synchronized (ArraySet.class) {\n                if (mBaseCacheSize < CACHE_SIZE) {\n                    array[0] = mBaseCache;\n                    array[1] = hashes;\n                    for (int i=size-1; i>=2; i--) {\n                        array[i] = null;\n                    }\n                    mBaseCache = array;\n                    mBaseCacheSize++;\n                    if (DEBUG) Log.d(TAG, \"Storing 1x cache \" + array\n                            + \" now have \" + mBaseCacheSize + \" entries\");\n                }\n            }\n        }\n    }\n\n    private int indexOf(Object key, int hash) {\n        final int N = mSize;\n\n        // Important fast case: if nothing is in here, nothing to look for.\n        if (N == 0) {\n            return ~0;\n        }\n\n        int index = ContainerHelpers.binarySearch(mHashes, N, hash);\n\n        // If the hash code wasn't found, then we have no entry for this key.\n        if (index < 0) {\n            return index;\n        }\n\n        // If the key at the returned index matches, that's what we want.\n        if (key.equals(mArray[index])) {\n            return index;\n        }\n\n        // Search for a matching key after the index.\n        int end;\n        for (end = index + 1; end < N && mHashes[end] == hash; end++) {\n            if (key.equals(mArray[end])) return end;\n        }\n\n        // Search for a matching key before the index.\n        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {\n            if (key.equals(mArray[i])) return i;\n        }\n\n        // Key not found -- return negative value indicating where a\n        // new entry for this key should go.  We use the end of the\n        // hash chain to reduce the number of array entries that will\n        // need to be copied when inserting.\n        return ~end;\n    }\n\n    private int indexOfNull() {\n        final int N = mSize;\n\n        // Important fast case: if nothing is in here, nothing to look for.\n        if (N == 0) {\n            return ~0;\n        }\n\n        int index = ContainerHelpers.binarySearch(mHashes, N, 0);\n\n        // If the hash code wasn't found, then we have no entry for this key.\n        if (index < 0) {\n            return index;\n        }\n\n        // If the key at the returned index matches, that's what we want.\n        if (null == mArray[index]) {\n            return index;\n        }\n\n        // Search for a matching key after the index.\n        int end;\n        for (end = index + 1; end < N && mHashes[end] == 0; end++) {\n            if (null == mArray[end]) return end;\n        }\n\n        // Search for a matching key before the index.\n        for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {\n            if (null == mArray[i]) return i;\n        }\n\n        // Key not found -- return negative value indicating where a\n        // new entry for this key should go.  We use the end of the\n        // hash chain to reduce the number of array entries that will\n        // need to be copied when inserting.\n        return ~end;\n    }\n\n    private void allocArrays(final int size) {\n        if (size == (BASE_SIZE*2)) {\n            synchronized (ArraySet.class) {\n                if (mTwiceBaseCache != null) {\n                    final Object[] array = mTwiceBaseCache;\n                    mArray = array;\n                    mTwiceBaseCache = (Object[])array[0];\n                    mHashes = (int[])array[1];\n                    array[0] = array[1] = null;\n                    mTwiceBaseCacheSize--;\n                    if (DEBUG) Log.d(TAG, \"Retrieving 2x cache \" + mHashes\n                            + \" now have \" + mTwiceBaseCacheSize + \" entries\");\n                    return;\n                }\n            }\n        } else if (size == BASE_SIZE) {\n            synchronized (ArraySet.class) {\n                if (mBaseCache != null) {\n                    final Object[] array = mBaseCache;\n                    mArray = array;\n                    mBaseCache = (Object[])array[0];\n                    mHashes = (int[])array[1];\n                    array[0] = array[1] = null;\n                    mBaseCacheSize--;\n                    if (DEBUG) Log.d(TAG, \"Retrieving 1x cache \" + mHashes\n                            + \" now have \" + mBaseCacheSize + \" entries\");\n                    return;\n                }\n            }\n        }\n\n        mHashes = new int[size];\n        mArray = new Object[size];\n    }\n\n    /**\n     * Make the array map empty.  All storage is released.\n     */\n    @Override\n    public void clear() {\n        if (mSize != 0) {\n            freeArrays(mHashes, mArray, mSize);\n            mHashes = ContainerHelpers.EMPTY_INTS;\n            mArray = ContainerHelpers.EMPTY_OBJECTS;\n            mSize = 0;\n        }\n    }\n\n    /**\n     * Ensure the array map can hold at least <var>minimumCapacity</var>\n     * items.\n     */\n    public void ensureCapacity(int minimumCapacity) {\n        if (mHashes.length < minimumCapacity) {\n            final int[] ohashes = mHashes;\n            final Object[] oarray = mArray;\n            allocArrays(minimumCapacity);\n            if (mSize > 0) {\n                System.arraycopy(ohashes, 0, mHashes, 0, mSize);\n                System.arraycopy(oarray, 0, mArray, 0, mSize);\n            }\n            freeArrays(ohashes, oarray, mSize);\n        }\n    }\n\n    /**\n     * Check whether a value exists in the set.\n     *\n     * @param key The value to search for.\n     * @return Returns true if the value exists, else false.\n     */\n    @Override\n    public boolean contains(Object key) {\n        return indexOf(key) >= 0;\n    }\n\n    /**\n     * Returns the index of a value in the set.\n     *\n     * @param key The value to search for.\n     * @return Returns the index of the value if it exists, else a negative integer.\n     */\n    public int indexOf(Object key) {\n        return key == null ? indexOfNull() : indexOf(key, key.hashCode());\n    }\n\n    /**\n     * Return the value at the given index in the array.\n     * @param index The desired index, must be between 0 and {@link #size()}-1.\n     * @return Returns the value stored at the given index.\n     */\n    public E valueAt(int index) {\n        return (E)mArray[index];\n    }\n\n    /**\n     * Return true if the array map contains no items.\n     */\n    @Override\n    public boolean isEmpty() {\n        return mSize <= 0;\n    }\n\n    /**\n     * Adds the specified object to this set. The set is not modified if it\n     * already contains the object.\n     *\n     * @param value the object to add.\n     * @return {@code true} if this set is modified, {@code false} otherwise.\n     * @throws ClassCastException\n     *             when the class of the object is inappropriate for this set.\n     */\n    @Override\n    public boolean add(E value) {\n        final int hash;\n        int index;\n        if (value == null) {\n            hash = 0;\n            index = indexOfNull();\n        } else {\n            hash = value.hashCode();\n            index = indexOf(value, hash);\n        }\n        if (index >= 0) {\n            return false;\n        }\n\n        index = ~index;\n        if (mSize >= mHashes.length) {\n            final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))\n                    : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);\n\n            if (DEBUG) Log.d(TAG, \"add: grow from \" + mHashes.length + \" to \" + n);\n\n            final int[] ohashes = mHashes;\n            final Object[] oarray = mArray;\n            allocArrays(n);\n\n            if (mHashes.length > 0) {\n                if (DEBUG) Log.d(TAG, \"add: copy 0-\" + mSize + \" to 0\");\n                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);\n                System.arraycopy(oarray, 0, mArray, 0, oarray.length);\n            }\n\n            freeArrays(ohashes, oarray, mSize);\n        }\n\n        if (index < mSize) {\n            if (DEBUG) Log.d(TAG, \"add: move \" + index + \"-\" + (mSize-index)\n                    + \" to \" + (index+1));\n            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);\n            System.arraycopy(mArray, index, mArray, index + 1, mSize - index);\n        }\n\n        mHashes[index] = hash;\n        mArray[index] = value;\n        mSize++;\n        return true;\n    }\n\n    /**\n     * Perform a {@link #add(Object)} of all values in <var>array</var>\n     * @param array The array whose contents are to be retrieved.\n     */\n    public void addAll(ArraySet<? extends E> array) {\n        final int N = array.mSize;\n        ensureCapacity(mSize + N);\n        if (mSize == 0) {\n            if (N > 0) {\n                System.arraycopy(array.mHashes, 0, mHashes, 0, N);\n                System.arraycopy(array.mArray, 0, mArray, 0, N);\n                mSize = N;\n            }\n        } else {\n            for (int i=0; i<N; i++) {\n                add(array.valueAt(i));\n            }\n        }\n    }\n\n    /**\n     * Removes the specified object from this set.\n     *\n     * @param object the object to remove.\n     * @return {@code true} if this set was modified, {@code false} otherwise.\n     */\n    @Override\n    public boolean remove(Object object) {\n        final int index = indexOf(object);\n        if (index >= 0) {\n            removeAt(index);\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Remove the key/value mapping at the given index.\n     * @param index The desired index, must be between 0 and {@link #size()}-1.\n     * @return Returns the value that was stored at this index.\n     */\n    public E removeAt(int index) {\n        final Object old = mArray[index];\n        if (mSize <= 1) {\n            // Now empty.\n            if (DEBUG) Log.d(TAG, \"remove: shrink from \" + mHashes.length + \" to 0\");\n            freeArrays(mHashes, mArray, mSize);\n            mHashes = ContainerHelpers.EMPTY_INTS;\n            mArray = ContainerHelpers.EMPTY_OBJECTS;\n            mSize = 0;\n        } else {\n            if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {\n                // Shrunk enough to reduce size of arrays.  We don't allow it to\n                // shrink smaller than (BASE_SIZE*2) to avoid flapping between\n                // that and BASE_SIZE.\n                final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);\n\n                if (DEBUG) Log.d(TAG, \"remove: shrink from \" + mHashes.length + \" to \" + n);\n\n                final int[] ohashes = mHashes;\n                final Object[] oarray = mArray;\n                allocArrays(n);\n\n                mSize--;\n                if (index > 0) {\n                    if (DEBUG) Log.d(TAG, \"remove: copy from 0-\" + index + \" to 0\");\n                    System.arraycopy(ohashes, 0, mHashes, 0, index);\n                    System.arraycopy(oarray, 0, mArray, 0, index);\n                }\n                if (index < mSize) {\n                    if (DEBUG) Log.d(TAG, \"remove: copy from \" + (index+1) + \"-\" + mSize\n                            + \" to \" + index);\n                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);\n                    System.arraycopy(oarray, index + 1, mArray, index, mSize - index);\n                }\n            } else {\n                mSize--;\n                if (index < mSize) {\n                    if (DEBUG) Log.d(TAG, \"remove: move \" + (index+1) + \"-\" + mSize\n                            + \" to \" + index);\n                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);\n                    System.arraycopy(mArray, index + 1, mArray, index, mSize - index);\n                }\n                mArray[mSize] = null;\n            }\n        }\n        return (E)old;\n    }\n\n    /**\n     * Perform a {@link #remove(Object)} of all values in <var>array</var>\n     * @param array The array whose contents are to be removed.\n     */\n    public boolean removeAll(ArraySet<? extends E> array) {\n        // TODO: If array is sufficiently large, a marking approach might be beneficial. In a first\n        //       pass, use the property that the sets are sorted by hash to make this linear passes\n        //       (except for hash collisions, which means worst case still n*m), then do one\n        //       collection pass into a new array. This avoids binary searches and excessive memcpy.\n        final int N = array.mSize;\n\n        // Note: ArraySet does not make thread-safety guarantees. So instead of OR-ing together all\n        //       the single results, compare size before and after.\n        final int originalSize = mSize;\n        for (int i = 0; i < N; i++) {\n            remove(array.valueAt(i));\n        }\n        return originalSize != mSize;\n    }\n\n    /**\n     * Return the number of items in this array map.\n     */\n    @Override\n    public int size() {\n        return mSize;\n    }\n\n    @Override\n    public Object[] toArray() {\n        Object[] result = new Object[mSize];\n        System.arraycopy(mArray, 0, result, 0, mSize);\n        return result;\n    }\n\n    @Override\n    public <T> T[] toArray(T[] array) {\n        if (array.length < mSize) {\n            @SuppressWarnings(\"unchecked\") T[] newArray\n                = (T[]) Array.newInstance(array.getClass().getComponentType(), mSize);\n            array = newArray;\n        }\n        System.arraycopy(mArray, 0, array, 0, mSize);\n        if (array.length > mSize) {\n            array[mSize] = null;\n        }\n        return array;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * <p>This implementation returns false if the object is not a set, or\n     * if the sets have different sizes.  Otherwise, for each value in this\n     * set, it checks to make sure the value also exists in the other set.\n     * If any value doesn't exist, the method returns false; otherwise, it\n     * returns true.\n     */\n    @Override\n    public boolean equals(Object object) {\n        if (this == object) {\n            return true;\n        }\n        if (object instanceof Set) {\n            Set<?> set = (Set<?>) object;\n            if (size() != set.size()) {\n                return false;\n            }\n\n            try {\n                for (int i=0; i<mSize; i++) {\n                    E mine = valueAt(i);\n                    if (!set.contains(mine)) {\n                        return false;\n                    }\n                }\n            } catch (NullPointerException ignored) {\n                return false;\n            } catch (ClassCastException ignored) {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public int hashCode() {\n        final int[] hashes = mHashes;\n        int result = 0;\n        for (int i = 0, s = mSize; i < s; i++) {\n            result += hashes[i];\n        }\n        return result;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * <p>This implementation composes a string by iterating over its values. If\n     * this set contains itself as a value, the string \"(this Set)\"\n     * will appear in its place.\n     */\n    @Override\n    public String toString() {\n        if (isEmpty()) {\n            return \"{}\";\n        }\n\n        StringBuilder buffer = new StringBuilder(mSize * 14);\n        buffer.append('{');\n        for (int i=0; i<mSize; i++) {\n            if (i > 0) {\n                buffer.append(\", \");\n            }\n            Object value = valueAt(i);\n            if (value != this) {\n                buffer.append(value);\n            } else {\n                buffer.append(\"(this Set)\");\n            }\n        }\n        buffer.append('}');\n        return buffer.toString();\n    }\n\n    // ------------------------------------------------------------------------\n    // Interop with traditional Java containers.  Not as efficient as using\n    // specialized collection APIs.\n    // ------------------------------------------------------------------------\n\n    private MapCollections<E, E> getCollection() {\n        if (mCollections == null) {\n            mCollections = new MapCollections<E, E>() {\n                @Override\n                protected int colGetSize() {\n                    return mSize;\n                }\n\n                @Override\n                protected Object colGetEntry(int index, int offset) {\n                    return mArray[index];\n                }\n\n                @Override\n                protected int colIndexOfKey(Object key) {\n                    return indexOf(key);\n                }\n\n                @Override\n                protected int colIndexOfValue(Object value) {\n                    return indexOf(value);\n                }\n\n                @Override\n                protected Map<E, E> colGetMap() {\n                    throw new UnsupportedOperationException(\"not a map\");\n                }\n\n                @Override\n                protected void colPut(E key, E value) {\n                    add(key);\n                }\n\n                @Override\n                protected E colSetValue(int index, E value) {\n                    throw new UnsupportedOperationException(\"not a map\");\n                }\n\n                @Override\n                protected void colRemoveAt(int index) {\n                    removeAt(index);\n                }\n\n                @Override\n                protected void colClear() {\n                    clear();\n                }\n            };\n        }\n        return mCollections;\n    }\n\n    /**\n     * Return an {@link java.util.Iterator} over all values in the set.\n     *\n     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it\n     * requires generating a number of temporary objects and allocates additional state\n     * information associated with the container that will remain for the life of the container.</p>\n     */\n    @Override\n    public Iterator<E> iterator() {\n        return getCollection().getKeySet().iterator();\n    }\n\n    /**\n     * Determine if the array set contains all of the values in the given collection.\n     * @param collection The collection whose contents are to be checked against.\n     * @return Returns true if this array set contains a value for every entry\n     * in <var>collection</var>, else returns false.\n     */\n    @Override\n    public boolean containsAll(Collection<?> collection) {\n        Iterator<?> it = collection.iterator();\n        while (it.hasNext()) {\n            if (!contains(it.next())) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Perform an {@link #add(Object)} of all values in <var>collection</var>\n     * @param collection The collection whose contents are to be retrieved.\n     */\n    @Override\n    public boolean addAll(Collection<? extends E> collection) {\n        ensureCapacity(mSize + collection.size());\n        boolean added = false;\n        for (E value : collection) {\n            added |= add(value);\n        }\n        return added;\n    }\n\n    /**\n     * Remove all values in the array set that exist in the given collection.\n     * @param collection The collection whose contents are to be used to remove values.\n     * @return Returns true if any values were removed from the array set, else false.\n     */\n    @Override\n    public boolean removeAll(Collection<?> collection) {\n        boolean removed = false;\n        for (Object value : collection) {\n            removed |= remove(value);\n        }\n        return removed;\n    }\n\n    /**\n     * Remove all values in the array set that do <b>not</b> exist in the given collection.\n     * @param collection The collection whose contents are to be used to determine which\n     * values to keep.\n     * @return Returns true if any values were removed from the array set, else false.\n     */\n    @Override\n    public boolean retainAll(Collection<?> collection) {\n        boolean removed = false;\n        for (int i=mSize-1; i>=0; i--) {\n            if (!collection.contains(mArray[i])) {\n                removeAt(i);\n                removed = true;\n            }\n        }\n        return removed;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/ContainerHelpers.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.collection;\n\nclass ContainerHelpers {\n    static final int[] EMPTY_INTS = new int[0];\n    static final long[] EMPTY_LONGS = new long[0];\n    static final Object[] EMPTY_OBJECTS = new Object[0];\n\n    public static int idealIntArraySize(int need) {\n        return idealByteArraySize(need * 4) / 4;\n    }\n\n    public static int idealLongArraySize(int need) {\n        return idealByteArraySize(need * 8) / 8;\n    }\n\n    public static int idealByteArraySize(int need) {\n        for (int i = 4; i < 32; i++)\n            if (need <= (1 << i) - 12)\n                return (1 << i) - 12;\n\n        return need;\n    }\n\n    public static boolean equal(Object a, Object b) {\n        return a == b || (a != null && a.equals(b));\n    }\n\n    // This is Arrays.binarySearch(), but doesn't do any argument validation.\n    static int binarySearch(int[] array, int size, int value) {\n        int lo = 0;\n        int hi = size - 1;\n\n        while (lo <= hi) {\n            int mid = (lo + hi) >>> 1;\n            int midVal = array[mid];\n\n            if (midVal < value) {\n                lo = mid + 1;\n            } else if (midVal > value) {\n                hi = mid - 1;\n            } else {\n                return mid;  // value found\n            }\n        }\n        return ~lo;  // value not present\n    }\n\n    static int binarySearch(long[] array, int size, long value) {\n        int lo = 0;\n        int hi = size - 1;\n\n        while (lo <= hi) {\n            final int mid = (lo + hi) >>> 1;\n            final long midVal = array[mid];\n\n            if (midVal < value) {\n                lo = mid + 1;\n            } else if (midVal > value) {\n                hi = mid - 1;\n            } else {\n                return mid;  // value found\n            }\n        }\n        return ~lo;  // value not present\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/IntArray.java",
    "content": "/*\n *  Copyright 2011 Alexey Andreev.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *       http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\npackage com.lody.virtual.helper.collection;\n\nimport java.util.Arrays;\n\n/**\n * @author Lody\n */\npublic class IntArray {\n    private static final int[] EMPTY_ARRAY = new int[0];\n    private int[] mData;\n    private int mSize;\n\n    private IntArray() {\n    }\n\n    public IntArray(int capacity) {\n        this.mData = new int[capacity];\n    }\n\n    public static IntArray of(int... values) {\n        IntArray array = new IntArray();\n        array.mData = Arrays.copyOf(values, values.length);\n        array.mSize = values.length;\n        return array;\n    }\n\n    public void clear() {\n        mSize = 0;\n    }\n\n    public void optimize() {\n        if (mSize > mData.length) {\n            mData = Arrays.copyOf(mData, mSize);\n        }\n    }\n\n    public int[] getAll() {\n        return mSize > 0 ? Arrays.copyOf(mData, mSize) : EMPTY_ARRAY;\n    }\n\n    public int get(int index) {\n        return mData[index];\n    }\n\n    public int[] getRange(int start, int end) {\n        return Arrays.copyOfRange(mData, start, end);\n    }\n\n    public void set(int index, int value) {\n        if (index >= mSize) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" is greater than the list size \" + mSize);\n        }\n        mData[index] = value;\n    }\n\n    private void ensureCapacity() {\n        if (mSize <= mData.length) {\n            return;\n        }\n        int newCap = mData.length;\n        while (mSize > newCap) {\n            newCap = newCap * 3 / 2 + 1;\n        }\n        mData = Arrays.copyOf(mData, newCap);\n    }\n\n    public int size() {\n        return mSize;\n    }\n\n    public void addAll(int[] items) {\n        int target = mSize;\n        mSize += items.length;\n        ensureCapacity();\n        System.arraycopy(items, 0, mData, target, items.length);\n    }\n\n    public void add(int item) {\n        ++mSize;\n        ensureCapacity();\n        mData[mSize - 1] = item;\n    }\n\n    public void remove(int index) {\n        remove(index, 1);\n    }\n\n    public void remove(int index, int count) {\n        System.arraycopy(mData, index + count, mData, index, mSize - index - count);\n        mSize -= count;\n    }\n\n    public boolean contains(int item) {\n        for (int i = 0; i < mSize; ++i) {\n            if (mData[i] == item) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/MapCollections.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.collection;\n\nimport java.lang.reflect.Array;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Helper for writing standard Java collection interfaces to a data\n * structure like {@link ArrayMap}.\n * @hide\n */\nabstract class MapCollections<K, V> {\n    EntrySet mEntrySet;\n    KeySet mKeySet;\n    ValuesCollection mValues;\n\n    public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) {\n        Iterator<?> it = collection.iterator();\n        while (it.hasNext()) {\n            if (!map.containsKey(it.next())) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) {\n        int oldSize = map.size();\n        Iterator<?> it = collection.iterator();\n        while (it.hasNext()) {\n            map.remove(it.next());\n        }\n        return oldSize != map.size();\n    }\n\n        public static <K, V> boolean retainAllHelper(Map<K, V> map, Collection<?> collection) {\n        int oldSize = map.size();\n        Iterator<K> it = map.keySet().iterator();\n        while (it.hasNext()) {\n            if (!collection.contains(it.next())) {\n                it.remove();\n            }\n        }\n        return oldSize != map.size();\n    };\n\n        public static <T> boolean equalsSetHelper(Set<T> set, Object object) {\n        if (set == object) {\n            return true;\n        }\n        if (object instanceof Set) {\n            Set<?> s = (Set<?>) object;\n\n            try {\n                return set.size() == s.size() && set.containsAll(s);\n            } catch (NullPointerException ignored) {\n                return false;\n            } catch (ClassCastException ignored) {\n                return false;\n            }\n        }\n        return false;\n    };\n\n        public Object[] toArrayHelper(int offset) {\n        final int N = colGetSize();\n        Object[] result = new Object[N];\n        for (int i=0; i<N; i++) {\n            result[i] = colGetEntry(i, offset);\n        }\n        return result;\n    };\n\n    public <T> T[] toArrayHelper(T[] array, int offset) {\n        final int N  = colGetSize();\n        if (array.length < N) {\n            @SuppressWarnings(\"unchecked\") T[] newArray\n                = (T[]) Array.newInstance(array.getClass().getComponentType(), N);\n            array = newArray;\n        }\n        for (int i=0; i<N; i++) {\n            array[i] = (T)colGetEntry(i, offset);\n        }\n        if (array.length > N) {\n            array[N] = null;\n        }\n        return array;\n    }\n\n    public Set<Map.Entry<K, V>> getEntrySet() {\n        if (mEntrySet == null) {\n            mEntrySet = new EntrySet();\n        }\n        return mEntrySet;\n    }\n\n    public Set<K> getKeySet() {\n        if (mKeySet == null) {\n            mKeySet = new KeySet();\n        }\n        return mKeySet;\n    }\n\n    public Collection<V> getValues() {\n        if (mValues == null) {\n            mValues = new ValuesCollection();\n        }\n        return mValues;\n    }\n\n    protected abstract int colGetSize();\n\n    protected abstract Object colGetEntry(int index, int offset);\n\n    protected abstract int colIndexOfKey(Object key);\n\n    protected abstract int colIndexOfValue(Object key);\n\n    protected abstract Map<K, V> colGetMap();\n\n    protected abstract void colPut(K key, V value);\n\n    protected abstract V colSetValue(int index, V value);\n\n    protected abstract void colRemoveAt(int index);\n\n    protected abstract void colClear();\n\n    final class ArrayIterator<T> implements Iterator<T> {\n        final int mOffset;\n        int mSize;\n        int mIndex;\n        boolean mCanRemove = false;\n\n        ArrayIterator(int offset) {\n            mOffset = offset;\n            mSize = colGetSize();\n        }\n\n        @Override\n        public boolean hasNext() {\n            return mIndex < mSize;\n        }\n\n        @Override\n        public T next() {\n            Object res = colGetEntry(mIndex, mOffset);\n            mIndex++;\n            mCanRemove = true;\n            return (T)res;\n        }\n\n        @Override\n        public void remove() {\n            if (!mCanRemove) {\n                throw new IllegalStateException();\n            }\n            mIndex--;\n            mSize--;\n            mCanRemove = false;\n            colRemoveAt(mIndex);\n        }\n    }\n\n    final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> {\n        int mEnd;\n        int mIndex;\n        boolean mEntryValid = false;\n\n        MapIterator() {\n            mEnd = colGetSize() - 1;\n            mIndex = -1;\n        }\n\n        @Override\n        public boolean hasNext() {\n            return mIndex < mEnd;\n        }\n\n        @Override\n        public Map.Entry<K, V> next() {\n            mIndex++;\n            mEntryValid = true;\n            return this;\n        }\n\n        @Override\n        public void remove() {\n            if (!mEntryValid) {\n                throw new IllegalStateException();\n            }\n            colRemoveAt(mIndex);\n            mIndex--;\n            mEnd--;\n            mEntryValid = false;\n        }\n\n        @Override\n        public K getKey() {\n            if (!mEntryValid) {\n                throw new IllegalStateException(\n                        \"This container does not support retaining Map.Entry objects\");\n            }\n            return (K)colGetEntry(mIndex, 0);\n        }\n\n        @Override\n        public V getValue() {\n            if (!mEntryValid) {\n                throw new IllegalStateException(\n                        \"This container does not support retaining Map.Entry objects\");\n            }\n            return (V)colGetEntry(mIndex, 1);\n        }\n\n        @Override\n        public V setValue(V object) {\n            if (!mEntryValid) {\n                throw new IllegalStateException(\n                        \"This container does not support retaining Map.Entry objects\");\n            }\n            return colSetValue(mIndex, object);\n        }\n\n        @Override\n        public final boolean equals(Object o) {\n            if (!mEntryValid) {\n                throw new IllegalStateException(\n                        \"This container does not support retaining Map.Entry objects\");\n            }\n            if (!(o instanceof Map.Entry)) {\n                return false;\n            }\n            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;\n            return ContainerHelpers.equal(e.getKey(), colGetEntry(mIndex, 0))\n                    && ContainerHelpers.equal(e.getValue(), colGetEntry(mIndex, 1));\n        }\n\n        @Override\n        public final int hashCode() {\n            if (!mEntryValid) {\n                throw new IllegalStateException(\n                        \"This container does not support retaining Map.Entry objects\");\n            }\n            final Object key = colGetEntry(mIndex, 0);\n            final Object value = colGetEntry(mIndex, 1);\n            return (key == null ? 0 : key.hashCode()) ^\n                    (value == null ? 0 : value.hashCode());\n        }\n\n        @Override\n        public final String toString() {\n            return getKey() + \"=\" + getValue();\n        }\n    }\n\nfinal class EntrySet implements Set<Map.Entry<K, V>> {\n        @Override\n        public boolean add(Map.Entry<K, V> object) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {\n            int oldSize = colGetSize();\n            for (Map.Entry<K, V> entry : collection) {\n                colPut(entry.getKey(), entry.getValue());\n            }\n            return oldSize != colGetSize();\n        }\n\n        @Override\n        public void clear() {\n            colClear();\n        }\n\n        @Override\n        public boolean contains(Object o) {\n            if (!(o instanceof Map.Entry))\n                return false;\n            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;\n            int index = colIndexOfKey(e.getKey());\n            if (index < 0) {\n                return false;\n            }\n            Object foundVal = colGetEntry(index, 1);\n            return ContainerHelpers.equal(foundVal, e.getValue());\n        }\n\n        @Override\n        public boolean containsAll(Collection<?> collection) {\n            Iterator<?> it = collection.iterator();\n            while (it.hasNext()) {\n                if (!contains(it.next())) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        @Override\n        public boolean isEmpty() {\n            return colGetSize() == 0;\n        }\n\n        @Override\n        public Iterator<Map.Entry<K, V>> iterator() {\n            return new MapIterator();\n        }\n\n        @Override\n        public boolean remove(Object object) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public boolean removeAll(Collection<?> collection) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public boolean retainAll(Collection<?> collection) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public int size() {\n            return colGetSize();\n        }\n\n        @Override\n        public Object[] toArray() {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public <T> T[] toArray(T[] array) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public boolean equals(Object object) {\n            return equalsSetHelper(this, object);\n        }\n\n        @Override\n        public int hashCode() {\n            int result = 0;\n            for (int i=colGetSize()-1; i>=0; i--) {\n                final Object key = colGetEntry(i, 0);\n                final Object value = colGetEntry(i, 1);\n                result += ( (key == null ? 0 : key.hashCode()) ^\n                        (value == null ? 0 : value.hashCode()) );\n            }\n            return result;\n        }\n    }\n\nfinal class KeySet implements Set<K> {\n\n        @Override\n        public boolean add(K object) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public boolean addAll(Collection<? extends K> collection) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public void clear() {\n            colClear();\n        }\n\n        @Override\n        public boolean contains(Object object) {\n            return colIndexOfKey(object) >= 0;\n        }\n\n        @Override\n        public boolean containsAll(Collection<?> collection) {\n            return containsAllHelper(colGetMap(), collection);\n        }\n\n        @Override\n        public boolean isEmpty() {\n            return colGetSize() == 0;\n        }\n\n        @Override\n        public Iterator<K> iterator() {\n            return new ArrayIterator<K>(0);\n        }\n\n        @Override\n        public boolean remove(Object object) {\n            int index = colIndexOfKey(object);\n            if (index >= 0) {\n                colRemoveAt(index);\n                return true;\n            }\n            return false;\n        }\n\n        @Override\n        public boolean removeAll(Collection<?> collection) {\n            return removeAllHelper(colGetMap(), collection);\n        }\n\n        @Override\n        public boolean retainAll(Collection<?> collection) {\n            return retainAllHelper(colGetMap(), collection);\n        }\n\n        @Override\n        public int size() {\n            return colGetSize();\n        }\n\n        @Override\n        public Object[] toArray() {\n            return toArrayHelper(0);\n        }\n\n        @Override\n        public <T> T[] toArray(T[] array) {\n            return toArrayHelper(array, 0);\n        }\n\n        @Override\n        public boolean equals(Object object) {\n            return equalsSetHelper(this, object);\n        }\n\n        @Override\n        public int hashCode() {\n            int result = 0;\n            for (int i=colGetSize()-1; i>=0; i--) {\n                Object obj = colGetEntry(i, 0);\n                result += obj == null ? 0 : obj.hashCode();\n            }\n            return result;\n        }\n    }\n\nfinal class ValuesCollection implements Collection<V> {\n\n        @Override\n        public boolean add(V object) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public boolean addAll(Collection<? extends V> collection) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public void clear() {\n            colClear();\n        }\n\n        @Override\n        public boolean contains(Object object) {\n            return colIndexOfValue(object) >= 0;\n        }\n\n        @Override\n        public boolean containsAll(Collection<?> collection) {\n            Iterator<?> it = collection.iterator();\n            while (it.hasNext()) {\n                if (!contains(it.next())) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        @Override\n        public boolean isEmpty() {\n            return colGetSize() == 0;\n        }\n\n        @Override\n        public Iterator<V> iterator() {\n            return new ArrayIterator<V>(1);\n        }\n\n        @Override\n        public boolean remove(Object object) {\n            int index = colIndexOfValue(object);\n            if (index >= 0) {\n                colRemoveAt(index);\n                return true;\n            }\n            return false;\n        }\n\n        @Override\n        public boolean removeAll(Collection<?> collection) {\n            int N = colGetSize();\n            boolean changed = false;\n            for (int i=0; i<N; i++) {\n                Object cur = colGetEntry(i, 1);\n                if (collection.contains(cur)) {\n                    colRemoveAt(i);\n                    i--;\n                    N--;\n                    changed = true;\n                }\n            }\n            return changed;\n        }\n\n        @Override\n        public boolean retainAll(Collection<?> collection) {\n            int N = colGetSize();\n            boolean changed = false;\n            for (int i=0; i<N; i++) {\n                Object cur = colGetEntry(i, 1);\n                if (!collection.contains(cur)) {\n                    colRemoveAt(i);\n                    i--;\n                    N--;\n                    changed = true;\n                }\n            }\n            return changed;\n        }\n\n        @Override\n        public int size() {\n            return colGetSize();\n        }\n\n        @Override\n        public Object[] toArray() {\n            return toArrayHelper(1);\n        }\n\n        @Override\n        public <T> T[] toArray(T[] array) {\n            return toArrayHelper(array, 1);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/SimpleArrayMap.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.collection;\n\nimport android.util.Log;\n\nimport java.util.Map;\n\n/**\n * Base implementation of {@link ArrayMap} that doesn't include any standard Java\n * container API interoperability.  These features are generally heavier-weight ways\n * to interact with the container, so discouraged, but they can be useful to make it\n * easier to use as a drop-in replacement for HashMap.  If you don't need them, this\n * class can be preferrable since it doesn't bring in any of the implementation of those\n * APIs, allowing that code to be stripped by ProGuard.\n */\npublic class SimpleArrayMap<K, V> {\n    private static final boolean DEBUG = false;\n    private static final String TAG = \"ArrayMap\";\n\n    /**\n     * The minimum amount by which the capacity of a ArrayMap will increase.\n     * This is tuned to be relatively space-efficient.\n     */\n    private static final int BASE_SIZE = 4;\n\n    /**\n     * Maximum number of entries to have in array caches.\n     */\n    private static final int CACHE_SIZE = 10;\n\n    /**\n     * Caches of small array objects to avoid spamming garbage.  The cache\n     * Object[] variable is a pointer to a linked list of array objects.\n     * The first entry in the array is a pointer to the next array in the\n     * list; the second entry is a pointer to the int[] hash code array for it.\n     */\n    static Object[] mBaseCache;\n    static int mBaseCacheSize;\n    static Object[] mTwiceBaseCache;\n    static int mTwiceBaseCacheSize;\n\n    int[] mHashes;\n    Object[] mArray;\n    int mSize;\n\n    /**\n     * Create a new empty ArrayMap.  The default capacity of an array map is 0, and\n     * will grow once items are added to it.\n     */\n    public SimpleArrayMap() {\n        mHashes = ContainerHelpers.EMPTY_INTS;\n        mArray = ContainerHelpers.EMPTY_OBJECTS;\n        mSize = 0;\n    }\n\n    /**\n     * Create a new ArrayMap with a given initial capacity.\n     */\n    public SimpleArrayMap(int capacity) {\n        if (capacity == 0) {\n            mHashes = ContainerHelpers.EMPTY_INTS;\n            mArray = ContainerHelpers.EMPTY_OBJECTS;\n        } else {\n            allocArrays(capacity);\n        }\n        mSize = 0;\n    }\n\n    /**\n     * Create a new ArrayMap with the mappings from the given ArrayMap.\n     */\n    public SimpleArrayMap(SimpleArrayMap map) {\n        this();\n        if (map != null) {\n            putAll(map);\n        }\n    }\n\n    private static void freeArrays(final int[] hashes, final Object[] array, final int size) {\n        if (hashes.length == (BASE_SIZE*2)) {\n            synchronized (ArrayMap.class) {\n                if (mTwiceBaseCacheSize < CACHE_SIZE) {\n                    array[0] = mTwiceBaseCache;\n                    array[1] = hashes;\n                    for (int i=(size<<1)-1; i>=2; i--) {\n                        array[i] = null;\n                    }\n                    mTwiceBaseCache = array;\n                    mTwiceBaseCacheSize++;\n                    if (DEBUG) Log.d(TAG, \"Storing 2x cache \" + array\n                            + \" now have \" + mTwiceBaseCacheSize + \" entries\");\n                }\n            }\n        } else if (hashes.length == BASE_SIZE) {\n            synchronized (ArrayMap.class) {\n                if (mBaseCacheSize < CACHE_SIZE) {\n                    array[0] = mBaseCache;\n                    array[1] = hashes;\n                    for (int i=(size<<1)-1; i>=2; i--) {\n                        array[i] = null;\n                    }\n                    mBaseCache = array;\n                    mBaseCacheSize++;\n                    if (DEBUG) Log.d(TAG, \"Storing 1x cache \" + array\n                            + \" now have \" + mBaseCacheSize + \" entries\");\n                }\n            }\n        }\n    }\n\n    int indexOf(Object key, int hash) {\n        final int N = mSize;\n\n        // Important fast case: if nothing is in here, nothing to look for.\n        if (N == 0) {\n            return ~0;\n        }\n\n        int index = ContainerHelpers.binarySearch(mHashes, N, hash);\n\n        // If the hash code wasn't found, then we have no entry for this key.\n        if (index < 0) {\n            return index;\n        }\n\n        // If the key at the returned index matches, that's what we want.\n        if (key.equals(mArray[index<<1])) {\n            return index;\n        }\n\n        // Search for a matching key after the index.\n        int end;\n        for (end = index + 1; end < N && mHashes[end] == hash; end++) {\n            if (key.equals(mArray[end << 1])) return end;\n        }\n\n        // Search for a matching key before the index.\n        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {\n            if (key.equals(mArray[i << 1])) return i;\n        }\n\n        // Key not found -- return negative value indicating where a\n        // new entry for this key should go.  We use the end of the\n        // hash chain to reduce the number of array entries that will\n        // need to be copied when inserting.\n        return ~end;\n    }\n\n    int indexOfNull() {\n        final int N = mSize;\n\n        // Important fast case: if nothing is in here, nothing to look for.\n        if (N == 0) {\n            return ~0;\n        }\n\n        int index = ContainerHelpers.binarySearch(mHashes, N, 0);\n\n        // If the hash code wasn't found, then we have no entry for this key.\n        if (index < 0) {\n            return index;\n        }\n\n        // If the key at the returned index matches, that's what we want.\n        if (null == mArray[index<<1]) {\n            return index;\n        }\n\n        // Search for a matching key after the index.\n        int end;\n        for (end = index + 1; end < N && mHashes[end] == 0; end++) {\n            if (null == mArray[end << 1]) return end;\n        }\n\n        // Search for a matching key before the index.\n        for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {\n            if (null == mArray[i << 1]) return i;\n        }\n\n        // Key not found -- return negative value indicating where a\n        // new entry for this key should go.  We use the end of the\n        // hash chain to reduce the number of array entries that will\n        // need to be copied when inserting.\n        return ~end;\n    }\n\n    private void allocArrays(final int size) {\n        if (size == (BASE_SIZE*2)) {\n            synchronized (ArrayMap.class) {\n                if (mTwiceBaseCache != null) {\n                    final Object[] array = mTwiceBaseCache;\n                    mArray = array;\n                    mTwiceBaseCache = (Object[])array[0];\n                    mHashes = (int[])array[1];\n                    array[0] = array[1] = null;\n                    mTwiceBaseCacheSize--;\n                    if (DEBUG) Log.d(TAG, \"Retrieving 2x cache \" + mHashes\n                            + \" now have \" + mTwiceBaseCacheSize + \" entries\");\n                    return;\n                }\n            }\n        } else if (size == BASE_SIZE) {\n            synchronized (ArrayMap.class) {\n                if (mBaseCache != null) {\n                    final Object[] array = mBaseCache;\n                    mArray = array;\n                    mBaseCache = (Object[])array[0];\n                    mHashes = (int[])array[1];\n                    array[0] = array[1] = null;\n                    mBaseCacheSize--;\n                    if (DEBUG) Log.d(TAG, \"Retrieving 1x cache \" + mHashes\n                            + \" now have \" + mBaseCacheSize + \" entries\");\n                    return;\n                }\n            }\n        }\n\n        mHashes = new int[size];\n        mArray = new Object[size<<1];\n    }\n\n    /**\n     * Make the array map empty.  All storage is released.\n     */\n    public void clear() {\n        if (mSize != 0) {\n            freeArrays(mHashes, mArray, mSize);\n            mHashes = ContainerHelpers.EMPTY_INTS;\n            mArray = ContainerHelpers.EMPTY_OBJECTS;\n            mSize = 0;\n        }\n    }\n\n    /**\n     * Ensure the array map can hold at least <var>minimumCapacity</var>\n     * items.\n     */\n    public void ensureCapacity(int minimumCapacity) {\n        if (mHashes.length < minimumCapacity) {\n            final int[] ohashes = mHashes;\n            final Object[] oarray = mArray;\n            allocArrays(minimumCapacity);\n            if (mSize > 0) {\n                System.arraycopy(ohashes, 0, mHashes, 0, mSize);\n                System.arraycopy(oarray, 0, mArray, 0, mSize<<1);\n            }\n            freeArrays(ohashes, oarray, mSize);\n        }\n    }\n\n    /**\n     * Check whether a key exists in the array.\n     *\n     * @param key The key to search for.\n     * @return Returns true if the key exists, else false.\n     */\n    public boolean containsKey(Object key) {\n        return indexOfKey(key) >= 0;\n    }\n\n    /**\n     * Returns the index of a key in the set.\n     *\n     * @param key The key to search for.\n     * @return Returns the index of the key if it exists, else a negative integer.\n     */\n    public int indexOfKey(Object key) {\n        return key == null ? indexOfNull() : indexOf(key, key.hashCode());\n    }\n\n    int indexOfValue(Object value) {\n        final int N = mSize*2;\n        final Object[] array = mArray;\n        if (value == null) {\n            for (int i=1; i<N; i+=2) {\n                if (array[i] == null) {\n                    return i>>1;\n                }\n            }\n        } else {\n            for (int i=1; i<N; i+=2) {\n                if (value.equals(array[i])) {\n                    return i>>1;\n                }\n            }\n        }\n        return -1;\n    }\n\n    /**\n     * Check whether a value exists in the array.  This requires a linear search\n     * through the entire array.\n     *\n     * @param value The value to search for.\n     * @return Returns true if the value exists, else false.\n     */\n    public boolean containsValue(Object value) {\n        return indexOfValue(value) >= 0;\n    }\n\n    /**\n     * Retrieve a value from the array.\n     * @param key The key of the value to retrieve.\n     * @return Returns the value associated with the given key,\n     * or null if there is no such key.\n     */\n    public V get(Object key) {\n        final int index = indexOfKey(key);\n        return index >= 0 ? (V)mArray[(index<<1)+1] : null;\n    }\n\n    /**\n     * Return the key at the given index in the array.\n     * @param index The desired index, must be between 0 and {@link #size()}-1.\n     * @return Returns the key stored at the given index.\n     */\n    public K keyAt(int index) {\n        return (K)mArray[index << 1];\n    }\n\n    /**\n     * Return the value at the given index in the array.\n     * @param index The desired index, must be between 0 and {@link #size()}-1.\n     * @return Returns the value stored at the given index.\n     */\n    public V valueAt(int index) {\n        return (V)mArray[(index << 1) + 1];\n    }\n\n    /**\n     * Set the value at a given index in the array.\n     * @param index The desired index, must be between 0 and {@link #size()}-1.\n     * @param value The new value to store at this index.\n     * @return Returns the previous value at the given index.\n     */\n    public V setValueAt(int index, V value) {\n        index = (index << 1) + 1;\n        V old = (V)mArray[index];\n        mArray[index] = value;\n        return old;\n    }\n\n    /**\n     * Return true if the array map contains no items.\n     */\n    public boolean isEmpty() {\n        return mSize <= 0;\n    }\n\n    /**\n     * Add a new value to the array map.\n     * @param key The key under which to store the value.  <b>Must not be null.</b>  If\n     * this key already exists in the array, its value will be replaced.\n     * @param value The value to store for the given key.\n     * @return Returns the old value that was stored for the given key, or null if there\n     * was no such key.\n     */\n    public V put(K key, V value) {\n        final int hash;\n        int index;\n        if (key == null) {\n            hash = 0;\n            index = indexOfNull();\n        } else {\n            hash = key.hashCode();\n            index = indexOf(key, hash);\n        }\n        if (index >= 0) {\n            index = (index<<1) + 1;\n            final V old = (V)mArray[index];\n            mArray[index] = value;\n            return old;\n        }\n\n        index = ~index;\n        if (mSize >= mHashes.length) {\n            final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))\n                    : (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);\n\n            if (DEBUG) Log.d(TAG, \"put: grow from \" + mHashes.length + \" to \" + n);\n\n            final int[] ohashes = mHashes;\n            final Object[] oarray = mArray;\n            allocArrays(n);\n\n            if (mHashes.length > 0) {\n                if (DEBUG) Log.d(TAG, \"put: copy 0-\" + mSize + \" to 0\");\n                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);\n                System.arraycopy(oarray, 0, mArray, 0, oarray.length);\n            }\n\n            freeArrays(ohashes, oarray, mSize);\n        }\n\n        if (index < mSize) {\n            if (DEBUG) Log.d(TAG, \"put: move \" + index + \"-\" + (mSize-index)\n                    + \" to \" + (index+1));\n            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);\n            System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);\n        }\n\n        mHashes[index] = hash;\n        mArray[index<<1] = key;\n        mArray[(index<<1)+1] = value;\n        mSize++;\n        return null;\n    }\n\n    /**\n     * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>array</var>\n     * @param array The array whose contents are to be retrieved.\n     */\n    public void putAll(SimpleArrayMap<? extends K, ? extends V> array) {\n        final int N = array.mSize;\n        ensureCapacity(mSize + N);\n        if (mSize == 0) {\n            if (N > 0) {\n                System.arraycopy(array.mHashes, 0, mHashes, 0, N);\n                System.arraycopy(array.mArray, 0, mArray, 0, N<<1);\n                mSize = N;\n            }\n        } else {\n            for (int i=0; i<N; i++) {\n                put(array.keyAt(i), array.valueAt(i));\n            }\n        }\n    }\n\n    /**\n     * Remove an existing key from the array map.\n     * @param key The key of the mapping to remove.\n     * @return Returns the value that was stored under the key, or null if there\n     * was no such key.\n     */\n    public V remove(Object key) {\n        final int index = indexOfKey(key);\n        if (index >= 0) {\n            return removeAt(index);\n        }\n\n        return null;\n    }\n\n    /**\n     * Remove the key/value mapping at the given index.\n     * @param index The desired index, must be between 0 and {@link #size()}-1.\n     * @return Returns the value that was stored at this index.\n     */\n    public V removeAt(int index) {\n        final Object old = mArray[(index << 1) + 1];\n        if (mSize <= 1) {\n            // Now empty.\n            if (DEBUG) Log.d(TAG, \"remove: shrink from \" + mHashes.length + \" to 0\");\n            freeArrays(mHashes, mArray, mSize);\n            mHashes = ContainerHelpers.EMPTY_INTS;\n            mArray = ContainerHelpers.EMPTY_OBJECTS;\n            mSize = 0;\n        } else {\n            if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {\n                // Shrunk enough to reduce size of arrays.  We don't allow it to\n                // shrink smaller than (BASE_SIZE*2) to avoid flapping between\n                // that and BASE_SIZE.\n                final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2);\n\n                if (DEBUG) Log.d(TAG, \"remove: shrink from \" + mHashes.length + \" to \" + n);\n\n                final int[] ohashes = mHashes;\n                final Object[] oarray = mArray;\n                allocArrays(n);\n\n                mSize--;\n                if (index > 0) {\n                    if (DEBUG) Log.d(TAG, \"remove: copy from 0-\" + index + \" to 0\");\n                    System.arraycopy(ohashes, 0, mHashes, 0, index);\n                    System.arraycopy(oarray, 0, mArray, 0, index << 1);\n                }\n                if (index < mSize) {\n                    if (DEBUG) Log.d(TAG, \"remove: copy from \" + (index+1) + \"-\" + mSize\n                            + \" to \" + index);\n                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);\n                    System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,\n                            (mSize - index) << 1);\n                }\n            } else {\n                mSize--;\n                if (index < mSize) {\n                    if (DEBUG) Log.d(TAG, \"remove: move \" + (index+1) + \"-\" + mSize\n                            + \" to \" + index);\n                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);\n                    System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,\n                            (mSize - index) << 1);\n                }\n                mArray[mSize << 1] = null;\n                mArray[(mSize << 1) + 1] = null;\n            }\n        }\n        return (V)old;\n    }\n\n    /**\n     * Return the number of items in this array map.\n     */\n    public int size() {\n        return mSize;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * <p>This implementation returns false if the object is not a map, or\n     * if the maps have different sizes. Otherwise, for each key in this map,\n     * values of both maps are compared. If the values for any key are not\n     * equal, the method returns false, otherwise it returns true.\n     */\n    @Override\n    public boolean equals(Object object) {\n        if (this == object) {\n            return true;\n        }\n        if (object instanceof Map) {\n            Map<?, ?> map = (Map<?, ?>) object;\n            if (size() != map.size()) {\n                return false;\n            }\n\n            try {\n                for (int i=0; i<mSize; i++) {\n                    K key = keyAt(i);\n                    V mine = valueAt(i);\n                    Object theirs = map.get(key);\n                    if (mine == null) {\n                        if (theirs != null || !map.containsKey(key)) {\n                            return false;\n                        }\n                    } else if (!mine.equals(theirs)) {\n                        return false;\n                    }\n                }\n            } catch (NullPointerException ignored) {\n                return false;\n            } catch (ClassCastException ignored) {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public int hashCode() {\n        final int[] hashes = mHashes;\n        final Object[] array = mArray;\n        int result = 0;\n        for (int i = 0, v = 1, s = mSize; i < s; i++, v+=2) {\n            Object value = array[v];\n            result += hashes[i] ^ (value == null ? 0 : value.hashCode());\n        }\n        return result;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * <p>This implementation composes a string by iterating over its mappings. If\n     * this map contains itself as a key or a value, the string \"(this Map)\"\n     * will appear in its place.\n     */\n    @Override\n    public String toString() {\n        if (isEmpty()) {\n            return \"{}\";\n        }\n\n        StringBuilder buffer = new StringBuilder(mSize * 28);\n        buffer.append('{');\n        for (int i=0; i<mSize; i++) {\n            if (i > 0) {\n                buffer.append(\", \");\n            }\n            Object key = keyAt(i);\n            if (key != this) {\n                buffer.append(key);\n            } else {\n                buffer.append(\"(this Map)\");\n            }\n            buffer.append('=');\n            Object value = valueAt(i);\n            if (value != this) {\n                buffer.append(value);\n            } else {\n                buffer.append(\"(this Map)\");\n            }\n        }\n        buffer.append('}');\n        return buffer.toString();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/collection/SparseArray.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.collection;\n\n/**\n * A copy of the current platform (currently {@link android.os.Build.VERSION_CODES#KITKAT}\n * version of {@link android.util.SparseArray}; provides a removeAt() method and other things.\n */\npublic class SparseArray<E> implements Cloneable {\n    private static final Object DELETED = new Object();\n    private boolean mGarbage = false;\n\n    private int[] mKeys;\n    private Object[] mValues;\n    private int mSize;\n\n    /**\n     * Creates a new SparseArray containing no mappings.\n     */\n    public SparseArray() {\n        this(10);\n    }\n\n    /**\n     * Creates a new SparseArray containing no mappings that will not\n     * require any additional memory allocation to store the specified\n     * number of mappings.  If you supply an initial capacity of 0, the\n     * sparse array will be initialized with a light-weight representation\n     * not requiring any additional array allocations.\n     */\n    public SparseArray(int initialCapacity) {\n        if (initialCapacity == 0) {\n            mKeys =  ContainerHelpers.EMPTY_INTS;\n            mValues =  ContainerHelpers.EMPTY_OBJECTS;\n        } else {\n            initialCapacity =  ContainerHelpers.idealIntArraySize(initialCapacity);\n            mKeys = new int[initialCapacity];\n            mValues = new Object[initialCapacity];\n        }\n        mSize = 0;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public SparseArray<E> clone() {\n        SparseArray<E> clone = null;\n        try {\n            clone = (SparseArray<E>) super.clone();\n            clone.mKeys = mKeys.clone();\n            clone.mValues = mValues.clone();\n        } catch (CloneNotSupportedException cnse) {\n            /* ignore */\n        }\n        return clone;\n    }\n\n    /**\n     * Gets the Object mapped from the specified key, or <code>null</code>\n     * if no such mapping has been made.\n     */\n    public E get(int key) {\n        return get(key, null);\n    }\n\n    /**\n     * Gets the Object mapped from the specified key, or the specified Object\n     * if no such mapping has been made.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public E get(int key, E valueIfKeyNotFound) {\n        int i =  ContainerHelpers.binarySearch(mKeys, mSize, key);\n\n        if (i < 0 || mValues[i] == DELETED) {\n            return valueIfKeyNotFound;\n        } else {\n            return (E) mValues[i];\n        }\n    }\n\n    /**\n     * Removes the mapping from the specified key, if there was any.\n     */\n    public void delete(int key) {\n        int i =  ContainerHelpers.binarySearch(mKeys, mSize, key);\n\n        if (i >= 0) {\n            if (mValues[i] != DELETED) {\n                mValues[i] = DELETED;\n                mGarbage = true;\n            }\n        }\n    }\n\n    /**\n     * Alias for {@link #delete(int)}.\n     */\n    public void remove(int key) {\n        delete(key);\n    }\n\n    /**\n     * Removes the mapping at the specified index.\n     */\n    public void removeAt(int index) {\n        if (mValues[index] != DELETED) {\n            mValues[index] = DELETED;\n            mGarbage = true;\n        }\n    }\n\n    /**\n     * Remove a range of mappings as a batch.\n     *\n     * @param index Index to begin at\n     * @param size Number of mappings to remove\n     */\n    public void removeAtRange(int index, int size) {\n        final int end = Math.min(mSize, index + size);\n        for (int i = index; i < end; i++) {\n            removeAt(i);\n        }\n    }\n\n    private void gc() {\n        // Log.e(\"SparseArray\", \"gc start with \" + mSize);\n\n        int n = mSize;\n        int o = 0;\n        int[] keys = mKeys;\n        Object[] values = mValues;\n\n        for (int i = 0; i < n; i++) {\n            Object val = values[i];\n\n            if (val != DELETED) {\n                if (i != o) {\n                    keys[o] = keys[i];\n                    values[o] = val;\n                    values[i] = null;\n                }\n\n                o++;\n            }\n        }\n\n        mGarbage = false;\n        mSize = o;\n\n        // Log.e(\"SparseArray\", \"gc end with \" + mSize);\n    }\n\n    /**\n     * Adds a mapping from the specified key to the specified value,\n     * replacing the previous mapping from the specified key if there\n     * was one.\n     */\n    public void put(int key, E value) {\n        int i =  ContainerHelpers.binarySearch(mKeys, mSize, key);\n\n        if (i >= 0) {\n            mValues[i] = value;\n        } else {\n            i = ~i;\n\n            if (i < mSize && mValues[i] == DELETED) {\n                mKeys[i] = key;\n                mValues[i] = value;\n                return;\n            }\n\n            if (mGarbage && mSize >= mKeys.length) {\n                gc();\n\n                // Search again because indices may have changed.\n                i = ~ ContainerHelpers.binarySearch(mKeys, mSize, key);\n            }\n\n            if (mSize >= mKeys.length) {\n                int n =  ContainerHelpers.idealIntArraySize(mSize + 1);\n\n                int[] nkeys = new int[n];\n                Object[] nvalues = new Object[n];\n\n                // Log.e(\"SparseArray\", \"grow \" + mKeys.length + \" to \" + n);\n                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);\n                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);\n\n                mKeys = nkeys;\n                mValues = nvalues;\n            }\n\n            if (mSize - i != 0) {\n                // Log.e(\"SparseArray\", \"move \" + (mSize - i));\n                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);\n                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);\n            }\n\n            mKeys[i] = key;\n            mValues[i] = value;\n            mSize++;\n        }\n    }\n\n    /**\n     * Returns the number of key-value mappings that this SparseArray\n     * currently stores.\n     */\n    public int size() {\n        if (mGarbage) {\n            gc();\n        }\n\n        return mSize;\n    }\n\n    /**\n     * Given an index in the range <code>0...size()-1</code>, returns\n     * the key from the <code>index</code>th key-value mapping that this\n     * SparseArray stores.\n     */\n    public int keyAt(int index) {\n        if (mGarbage) {\n            gc();\n        }\n\n        return mKeys[index];\n    }\n\n    /**\n     * Given an index in the range <code>0...size()-1</code>, returns\n     * the value from the <code>index</code>th key-value mapping that this\n     * SparseArray stores.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public E valueAt(int index) {\n        if (mGarbage) {\n            gc();\n        }\n\n        return (E) mValues[index];\n    }\n\n    /**\n     * @hide\n     * Removes the mapping from the specified key, if there was any, returning the old value.\n     */\n    public E removeReturnOld(int key) {\n        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);\n\n        if (i >= 0) {\n            if (mValues[i] != DELETED) {\n                final E old = (E) mValues[i];\n                mValues[i] = DELETED;\n                mGarbage = true;\n                return old;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Given an index in the range <code>0...size()-1</code>, sets a new\n     * value for the <code>index</code>th key-value mapping that this\n     * SparseArray stores.\n     */\n    public void setValueAt(int index, E value) {\n        if (mGarbage) {\n            gc();\n        }\n\n        mValues[index] = value;\n    }\n\n    /**\n     * Returns the index for which {@link #keyAt} would return the\n     * specified key, or a negative number if the specified\n     * key is not mapped.\n     */\n    public int indexOfKey(int key) {\n        if (mGarbage) {\n            gc();\n        }\n\n        return  ContainerHelpers.binarySearch(mKeys, mSize, key);\n    }\n\n    /**\n     * Returns an index for which {@link #valueAt} would return the\n     * specified key, or a negative number if no keys map to the\n     * specified value.\n     * <p>Beware that this is a linear search, unlike lookups by key,\n     * and that multiple keys can map to the same value and this will\n     * find only one of them.\n     * <p>Note also that unlike most collections' {@code indexOf} methods,\n     * this method compares values using {@code ==} rather than {@code equals}.\n     */\n    public int indexOfValue(E value) {\n        if (mGarbage) {\n            gc();\n        }\n\n        for (int i = 0; i < mSize; i++)\n            if (mValues[i] == value)\n                return i;\n\n        return -1;\n    }\n\n    /**\n     * Removes all key-value mappings from this SparseArray.\n     */\n    public void clear() {\n        int n = mSize;\n        Object[] values = mValues;\n\n        for (int i = 0; i < n; i++) {\n            values[i] = null;\n        }\n\n        mSize = 0;\n        mGarbage = false;\n    }\n\n    /**\n     * Puts a key/value pair into the array, optimizing for the case where\n     * the key is greater than all existing keys in the array.\n     */\n    public void append(int key, E value) {\n        if (mSize != 0 && key <= mKeys[mSize - 1]) {\n            put(key, value);\n            return;\n        }\n\n        if (mGarbage && mSize >= mKeys.length) {\n            gc();\n        }\n\n        int pos = mSize;\n        if (pos >= mKeys.length) {\n            int n =  ContainerHelpers.idealIntArraySize(pos + 1);\n\n            int[] nkeys = new int[n];\n            Object[] nvalues = new Object[n];\n\n            // Log.e(\"SparseArray\", \"grow \" + mKeys.length + \" to \" + n);\n            System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);\n            System.arraycopy(mValues, 0, nvalues, 0, mValues.length);\n\n            mKeys = nkeys;\n            mValues = nvalues;\n        }\n\n        mKeys[pos] = key;\n        mValues[pos] = value;\n        mSize = pos + 1;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * <p>This implementation composes a string by iterating over its mappings. If\n     * this map contains itself as a value, the string \"(this Map)\"\n     * will appear in its place.\n     */\n    @Override\n    public String toString() {\n        if (size() <= 0) {\n            return \"{}\";\n        }\n\n        StringBuilder buffer = new StringBuilder(mSize * 28);\n        buffer.append('{');\n        for (int i=0; i<mSize; i++) {\n            if (i > 0) {\n                buffer.append(\", \");\n            }\n            int key = keyAt(i);\n            buffer.append(key);\n            buffer.append('=');\n            Object value = valueAt(i);\n            if (value != this) {\n                buffer.append(value);\n            } else {\n                buffer.append(\"(this Map)\");\n            }\n        }\n        buffer.append('}');\n        return buffer.toString();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/AccountManagerCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.accounts.AccountManager;\n\n/**\n * @author Lody\n */\n\npublic class AccountManagerCompat {\n\n    /**\n     * Bundle key used for the {@code long} expiration time (in millis from the unix epoch) of the\n     * associated auth token.\n     *\n     */\n    public static final String KEY_CUSTOM_TOKEN_EXPIRY = \"android.accounts.expiry\";\n\n    /**\n     * Bundle key used to supply the last time the credentials of the account\n     * were authenticated successfully. Time is specified in milliseconds since\n     * epoch. Associated time is updated on successful authentication of account\n     * on adding account, confirming credentials, or updating credentials.\n     */\n    public static final String KEY_LAST_AUTHENTICATED_TIME = \"lastAuthenticatedTime\";\n\n    /**\n     * Boolean, if set and 'customTokens' the authenticator is responsible for\n     * notifications.\n     */\n    public static final String KEY_NOTIFY_ON_FAILURE = \"notifyOnAuthFailure\";\n\n    /**\n     * The Android package of the caller will be set in the options bundle by the\n     * {@link AccountManager} and will be passed to the AccountManagerService and\n     * to the AccountAuthenticators. The vuid of the caller will be known by the\n     * AccountManagerService as well as the AccountAuthenticators so they will be able to\n     * verify that the package is consistent with the vuid (a vuid might be shared by many\n     * packages).\n     */\n    public static final String KEY_ANDROID_PACKAGE_NAME = \"androidPackageName\";\n\n    public static final int ERROR_CODE_USER_RESTRICTED = 100;\n\n    public static final int ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101;\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/ActivityManagerCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.IBinder;\n\nimport mirror.android.app.ActivityManagerNative;\nimport mirror.android.app.IActivityManagerICS;\nimport mirror.android.app.IActivityManagerL;\nimport mirror.android.app.IActivityManagerN;\n\n/**\n * @author Lody\n */\n\npublic class ActivityManagerCompat {\n\t/** Type for IActivityManager.serviceDoneExecuting: anonymous operation */\n\tpublic static final int SERVICE_DONE_EXECUTING_ANON = 0;\n\t/** Type for IActivityManager.serviceDoneExecuting: done with an onStart call */\n\tpublic static final int SERVICE_DONE_EXECUTING_START = 1;\n\t/** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */\n\tpublic static final int SERVICE_DONE_EXECUTING_STOP = 2;\n\n\t/**\n\t * Result for IActivityManager.startActivity: an error where the\n\t * given Intent could not be resolved to an activity.\n\t */\n\tpublic static final int START_INTENT_NOT_RESOLVED = -1;\n\n\t/**\n\t * Result for IActivityManager.startActivity: trying to start a background user\n\t * activity that shouldn't be displayed for all users.\n\t */\n\tpublic static final int START_NOT_CURRENT_USER_ACTIVITY = -8;\n\n\t/**\n\t * Result for IActivityManaqer.startActivity: activity wasn't really started, but\n\t * a task was simply brought to the foreground.\n\t */\n\tpublic static final int START_TASK_TO_FRONT = 2;\n\n\t/**\n\t * Type for IActivityManaqer.getIntentSender: this PendingIntent is\n\t * for a sendBroadcast operation.\n\t */\n\tpublic static final int INTENT_SENDER_BROADCAST = 1;\n\n\t/**\n\t * Type for IActivityManaqer.getIntentSender: this PendingIntent is\n\t * for a startActivity operation.\n\t */\n\tpublic static final int INTENT_SENDER_ACTIVITY = 2;\n\n\t/**\n\t * Type for IActivityManaqer.getIntentSender: this PendingIntent is\n\t * for an activity result operation.\n\t */\n\tpublic static final int INTENT_SENDER_ACTIVITY_RESULT = 3;\n\n\t/**\n\t * Type for IActivityManaqer.getIntentSender: this PendingIntent is\n\t * for a startService operation.\n\t */\n\tpublic static final int INTENT_SENDER_SERVICE = 4;\n\n\t/** User operation call: success! */\n\tpublic static final int USER_OP_SUCCESS = 0;\n\n\tpublic static boolean finishActivity(IBinder token, int code, Intent data) {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n\t\t\treturn IActivityManagerN.finishActivity.call(\n\t\t\t\t\tActivityManagerNative.getDefault.call(),\n\t\t\t\t\ttoken, code, data, 0);\n\t\t} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\treturn IActivityManagerL.finishActivity.call(\n\t\t\t\t\t\tActivityManagerNative.getDefault.call(),\n\t\t\t\t\t\ttoken, code, data, false);\n\t\t} else {\n\t\t\tIActivityManagerICS.finishActivity.call(\n\t\t\t\t\tActivityManagerNative.getDefault.call(),\n\t\t\t\t\ttoken, code, data\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/ApplicationThreadCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.android.app.ApplicationThreadNative;\nimport mirror.android.app.IApplicationThreadOreo;\n\n/**\n * @author Lody\n */\n\npublic class ApplicationThreadCompat {\n\n    public static IInterface asInterface(IBinder binder) {\n        if (BuildCompat.isOreo()) {\n            return IApplicationThreadOreo.Stub.asInterface.call(binder);\n        }\n        return ApplicationThreadNative.asInterface.call(binder);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/BuildCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.os.Build;\n\n/**\n * @author Lody\n */\n\npublic class BuildCompat {\n\n    public static int getPreviewSDKInt() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            try {\n                return Build.VERSION.PREVIEW_SDK_INT;\n            } catch (Throwable e) {\n                // ignore\n            }\n        }\n        return 0;\n    }\n\n    public static boolean isOreo() {\n\n        return (Build.VERSION.SDK_INT == 25 && getPreviewSDKInt() > 0)\n                || Build.VERSION.SDK_INT > 25;\n    }\n\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/BundleCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\n\n/**\n * @author Lody\n *\n */\npublic class BundleCompat {\n\n\t//返回远程 IBinder 句柄\n\tpublic static IBinder getBinder(Bundle bundle, String key) {\n\t\tif (Build.VERSION.SDK_INT >= 18) {\n\t\t\treturn bundle.getBinder(key);\n\t\t} else {\n\t\t\treturn mirror.android.os.Bundle.getIBinder.call(bundle, key);\n\t\t}\n\t}\n\n\tpublic static void putBinder(Bundle bundle, String key, IBinder value) {\n\t\tif (Build.VERSION.SDK_INT >= 18) {\n\t\t\tbundle.putBinder(key, value);\n\t\t} else {\n\t\t\tmirror.android.os.Bundle.putIBinder.call(bundle, key, value);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/ContentProviderCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.content.ContentProviderClient;\nimport android.content.Context;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Build.VERSION;\nimport android.os.Bundle;\nimport android.os.RemoteException;\nimport android.os.SystemClock;\n\n/**\n * @author Lody\n */\npublic class ContentProviderCompat {\n\n    public static Bundle call(Context context, Uri uri, String method, String arg, Bundle extras) {\n        if (VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return context.getContentResolver().call(uri, method, arg, extras);\n        }\n        ContentProviderClient client = crazyAcquireContentProvider(context, uri);\n        Bundle res = null;\n        try {\n            res = client.call(method, arg, extras);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        } finally {\n            releaseQuietly(client);\n        }\n        return res;\n    }\n\n\n    private static ContentProviderClient acquireContentProviderClient(Context context, Uri uri) {\n        if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            return context.getContentResolver().acquireUnstableContentProviderClient(uri);\n        }\n        return context.getContentResolver().acquireContentProviderClient(uri);\n    }\n\n    public static ContentProviderClient crazyAcquireContentProvider(Context context, Uri uri) {\n        ContentProviderClient client = acquireContentProviderClient(context, uri);\n        if (client == null) {\n            int retry = 0;\n            while (retry < 5 && client == null) {\n                SystemClock.sleep(100);\n                retry++;\n                client = acquireContentProviderClient(context, uri);\n            }\n        }\n        return client;\n    }\n\n    public static ContentProviderClient crazyAcquireContentProvider(Context context, String name) {\n        ContentProviderClient client = acquireContentProviderClient(context, name);\n        if (client == null) {\n            int retry = 0;\n            while (retry < 5 && client == null) {\n                SystemClock.sleep(100);\n                retry++;\n                client = acquireContentProviderClient(context, name);\n            }\n        }\n        return client;\n    }\n\n    private static ContentProviderClient acquireContentProviderClient(Context context, String name) {\n        if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            return context.getContentResolver().acquireUnstableContentProviderClient(name);\n        }\n        return context.getContentResolver().acquireContentProviderClient(name);\n    }\n\n    public static void releaseQuietly(ContentProviderClient client) {\n        if (client != null) {\n            try {\n                if (VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                    client.close();\n                } else {\n                    client.release();\n                }\n            } catch (Exception ignored) {\n            }\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/ContentResolverCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\n/**\n * @author Lody\n */\n\npublic class ContentResolverCompat {\n\n    public static final int SYNC_OBSERVER_TYPE_STATUS = 1 << 3;\n\n    \n    public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;\n    \n    public static final int SYNC_ERROR_AUTHENTICATION = 2;\n    \n    public static final int SYNC_ERROR_IO = 3;\n    \n    public static final int SYNC_ERROR_PARSE = 4;\n    \n    public static final int SYNC_ERROR_CONFLICT = 5;\n    \n    public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;\n    \n    public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;\n    \n    public static final int SYNC_ERROR_INTERNAL = 8;\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/IApplicationThreadCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.content.Intent;\nimport android.content.pm.ServiceInfo;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.RemoteException;\n\nimport mirror.android.app.IApplicationThread;\nimport mirror.android.app.IApplicationThreadICSMR1;\nimport mirror.android.app.IApplicationThreadKitkat;\nimport mirror.android.content.res.CompatibilityInfo;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadCompat {\n\n\tpublic static void scheduleCreateService(IInterface appThread, IBinder token, ServiceInfo info,\n\t\t\tint processState) throws RemoteException {\n\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n\t\t\tIApplicationThreadKitkat.scheduleCreateService.call(appThread, token, info, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO.get(),\n\t\t\t\t\t\tprocessState);\n\t\t} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n\t\t\tIApplicationThreadICSMR1.scheduleCreateService.call(appThread, token, info, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO.get());\n\t\t} else {\n\t\t\tIApplicationThread.scheduleCreateService.call(appThread, token, info);\n\t\t}\n\n\t}\n\n\tpublic static void scheduleBindService(IInterface appThread, IBinder token, Intent intent, boolean rebind,\n\t\t\tint processState) throws RemoteException {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n\t\t\tIApplicationThreadKitkat.scheduleBindService.call(appThread, token, intent, rebind, processState);\n\t\t} else {\n\t\t\tIApplicationThread.scheduleBindService.call(appThread, token, intent, rebind);\n\t\t}\n\t}\n\n\tpublic static void scheduleUnbindService(IInterface appThread, IBinder token, Intent intent) throws RemoteException {\n\t\tIApplicationThread.scheduleUnbindService.call(appThread, token, intent);\n\t}\n\n\tpublic static void scheduleServiceArgs(IInterface appThread, IBinder token, boolean taskRemoved,\n\t\t\tint startId, int flags, Intent args) throws RemoteException {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n\t\t\tIApplicationThreadICSMR1.scheduleServiceArgs.call(appThread, token, taskRemoved, startId, flags, args);\n\t\t} else {\n\t\t\tIApplicationThread.scheduleServiceArgs.call(appThread, token, startId, flags, args);\n\t\t}\n\t}\n\n\n\tpublic static void scheduleStopService(IInterface appThread, IBinder token) throws RemoteException {\n\t\tIApplicationThread.scheduleStopService.call(appThread, token);\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/NativeLibraryHelperCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.io.File;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport mirror.com.android.internal.content.NativeLibraryHelper;\nimport mirror.dalvik.system.VMRuntime;\n\npublic class NativeLibraryHelperCompat {\n\n\tprivate static String TAG = NativeLibraryHelperCompat.class.getSimpleName();\n\n\tpublic static int copyNativeBinaries(File apkFile, File sharedLibraryDir) {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\treturn copyNativeBinariesAfterL(apkFile, sharedLibraryDir);\n\t\t} else {\n\t\t\treturn copyNativeBinariesBeforeL(apkFile, sharedLibraryDir);\n\t\t}\n\t}\n\n\tprivate static int copyNativeBinariesBeforeL(File apkFile, File sharedLibraryDir) {\n\t\ttry {\n\t\t\treturn Reflect.on(NativeLibraryHelper.TYPE).call(\"copyNativeBinariesIfNeededLI\", apkFile, sharedLibraryDir)\n\t\t\t\t\t.get();\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\tprivate static int copyNativeBinariesAfterL(File apkFile, File sharedLibraryDir) {\n\t\ttry {\n\t\t\tObject handle = NativeLibraryHelper.Handle.create.call(apkFile);\n\t\t\tif (handle == null) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tString abi = null;\n\t\t\tSet<String> abiSet = getABIsFromApk(apkFile.getAbsolutePath());\n\t\t\tif (abiSet == null || abiSet.isEmpty()) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tboolean is64Bit = VMRuntime.is64Bit.call(VMRuntime.getRuntime.call());\n\t\t\tif (is64Bit && isVM64(abiSet)) {\n\t\t\t\tif (Build.SUPPORTED_64_BIT_ABIS.length > 0) {\n\t\t\t\t\tint abiIndex = NativeLibraryHelper.findSupportedAbi.call(handle, Build.SUPPORTED_64_BIT_ABIS);\n\t\t\t\t\tif (abiIndex >= 0) {\n\t\t\t\t\t\tabi = Build.SUPPORTED_64_BIT_ABIS[abiIndex];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (Build.SUPPORTED_32_BIT_ABIS.length > 0) {\n\t\t\t\t\tint abiIndex = NativeLibraryHelper.findSupportedAbi.call(handle, Build.SUPPORTED_32_BIT_ABIS);\n\t\t\t\t\tif (abiIndex >= 0) {\n\t\t\t\t\t\tabi = Build.SUPPORTED_32_BIT_ABIS[abiIndex];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (abi == null) {\n\t\t\t\tVLog.e(TAG, \"Not match any abi [%s].\", apkFile.getPath());\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn NativeLibraryHelper.copyNativeBinaries.call(handle, sharedLibraryDir, abi);\n\t\t} catch (Throwable e) {\n\t\t\tVLog.d(TAG, \"copyNativeBinaries with error : %s\", e.getLocalizedMessage());\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@TargetApi(Build.VERSION_CODES.LOLLIPOP)\n\tprivate static boolean isVM64(Set<String> supportedABIs) {\n\t\tif (Build.SUPPORTED_64_BIT_ABIS.length == 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (supportedABIs == null || supportedABIs.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (String supportedAbi : supportedABIs) {\n\t\t\tif (\"arm64-v8a\".endsWith(supportedAbi) || \"x86_64\".equals(supportedAbi) || \"mips64\".equals(supportedAbi)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static Set<String> getABIsFromApk(String apk) {\n\t\ttry {\n\t\t\tZipFile apkFile = new ZipFile(apk);\n\t\t\tEnumeration<? extends ZipEntry> entries = apkFile.entries();\n\t\t\tSet<String> supportedABIs = new HashSet<String>();\n\t\t\twhile (entries.hasMoreElements()) {\n\t\t\t\tZipEntry entry = entries.nextElement();\n\t\t\t\tString name = entry.getName();\n\t\t\t\tif (name.contains(\"../\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (name.startsWith(\"lib/\") && !entry.isDirectory() && name.endsWith(\".so\")) {\n\t\t\t\t\tString supportedAbi = name.substring(name.indexOf(\"/\") + 1, name.lastIndexOf(\"/\"));\n\t\t\t\t\tsupportedABIs.add(supportedAbi);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn supportedABIs;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/ObjectsCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\n/**\n * @author Lody\n */\n\npublic class ObjectsCompat {\n\n\t/**\n\t * Null-safe equivalent of {@code a.equals(b)}.\n\t */\n\tpublic static boolean equals(Object a, Object b) {\n\t\treturn (a == null) ? (b == null) : a.equals(b);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/PackageParserCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageParser;\nimport android.content.pm.PackageParser.Activity;\nimport android.content.pm.PackageParser.Package;\nimport android.content.pm.PackageParser.Provider;\nimport android.content.pm.PackageParser.Service;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ServiceInfo;\nimport android.os.Build;\nimport android.os.Process;\nimport android.util.DisplayMetrics;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.os.VUserHandle;\n\nimport java.io.File;\n\nimport mirror.android.content.pm.PackageParserJellyBean;\nimport mirror.android.content.pm.PackageParserJellyBean17;\nimport mirror.android.content.pm.PackageParserLollipop;\nimport mirror.android.content.pm.PackageParserLollipop22;\nimport mirror.android.content.pm.PackageParserMarshmallow;\nimport mirror.android.content.pm.PackageParserNougat;\nimport mirror.android.content.pm.PackageUserState;\n\nimport static android.os.Build.VERSION_CODES.JELLY_BEAN;\nimport static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;\nimport static android.os.Build.VERSION_CODES.LOLLIPOP;\nimport static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;\nimport static android.os.Build.VERSION_CODES.M;\nimport static android.os.Build.VERSION_CODES.N;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserCompat {\n\n    public static final int[] GIDS = VirtualCore.get().getGids();\n    private static final int API_LEVEL = Build.VERSION.SDK_INT;\n    private static final int myUserId = VUserHandle.getUserId(Process.myUid());\n    private static final Object sUserState = API_LEVEL >= JELLY_BEAN_MR1 ? PackageUserState.ctor.newInstance() : null;\n\n    // apk 解析器工厂\n    public static PackageParser createParser(File packageFile) {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.ctor.newInstance();\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            return PackageParserLollipop22.ctor.newInstance();\n        } else if (API_LEVEL >= LOLLIPOP) {\n            return PackageParserLollipop.ctor.newInstance();\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.ctor.newInstance(packageFile.getAbsolutePath());\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.ctor.newInstance(packageFile.getAbsolutePath());\n        } else {\n            return mirror.android.content.pm.PackageParser.ctor.newInstance(packageFile.getAbsolutePath());\n        }\n    }\n\n    public static Package parsePackage(PackageParser parser, File packageFile, int flags) throws Throwable {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.parsePackage.callWithException(parser, packageFile, flags);\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            return PackageParserLollipop22.parsePackage.callWithException(parser, packageFile, flags);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            return PackageParserLollipop.parsePackage.callWithException(parser, packageFile, flags);\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.parsePackage.callWithException(parser, packageFile, null,\n                    new DisplayMetrics(), flags);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.parsePackage.callWithException(parser, packageFile, null,\n                    new DisplayMetrics(), flags);\n        } else {\n            return mirror.android.content.pm.PackageParser.parsePackage.callWithException(parser, packageFile, null,\n                    new DisplayMetrics(), flags);\n        }\n    }\n\n    public static ServiceInfo generateServiceInfo(Service service, int flags) {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.generateServiceInfo.call(service, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            return PackageParserLollipop22.generateServiceInfo.call(service, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            return PackageParserLollipop.generateServiceInfo.call(service, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.generateServiceInfo.call(service, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.generateServiceInfo.call(service, flags, false, 1, myUserId);\n        } else {\n            return mirror.android.content.pm.PackageParser.generateServiceInfo.call(service, flags);\n        }\n    }\n\n    public static ApplicationInfo generateApplicationInfo(Package p, int flags) {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.generateApplicationInfo.call(p, flags, sUserState);\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            return PackageParserLollipop22.generateApplicationInfo.call(p, flags, sUserState);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            return PackageParserLollipop.generateApplicationInfo.call(p, flags, sUserState);\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.generateApplicationInfo.call(p, flags, sUserState);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.generateApplicationInfo.call(p, flags, false, 1);\n        } else {\n            return mirror.android.content.pm.PackageParser.generateApplicationInfo.call(p, flags);\n        }\n    }\n\n    public static ActivityInfo generateActivityInfo(Activity activity, int flags) {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.generateActivityInfo.call(activity, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            return PackageParserLollipop22.generateActivityInfo.call(activity, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            return PackageParserLollipop.generateActivityInfo.call(activity, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.generateActivityInfo.call(activity, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.generateActivityInfo.call(activity, flags, false, 1, myUserId);\n        } else {\n            return mirror.android.content.pm.PackageParser.generateActivityInfo.call(activity, flags);\n        }\n    }\n\n    public static ProviderInfo generateProviderInfo(Provider provider, int flags) {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.generateProviderInfo.call(provider, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            return PackageParserLollipop22.generateProviderInfo.call(provider, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            return PackageParserLollipop.generateProviderInfo.call(provider, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.generateProviderInfo.call(provider, flags, sUserState, myUserId);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.generateProviderInfo.call(provider, flags, false, 1, myUserId);\n        } else {\n            return mirror.android.content.pm.PackageParser.generateProviderInfo.call(provider, flags);\n        }\n    }\n\n    public static PackageInfo generatePackageInfo(Package p, int flags, long firstInstallTime, long lastUpdateTime) {\n        if (API_LEVEL >= M) {\n            return PackageParserMarshmallow.generatePackageInfo.call(p, GIDS, flags, firstInstallTime, lastUpdateTime,\n                    null, sUserState);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            if (PackageParserLollipop22.generatePackageInfo != null) {\n                return PackageParserLollipop22.generatePackageInfo.call(p, GIDS, flags, firstInstallTime, lastUpdateTime,\n                        null, sUserState);\n            } else {\n                return PackageParserLollipop.generatePackageInfo.call(p, GIDS, flags, firstInstallTime, lastUpdateTime,\n                        null, sUserState);\n            }\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            return PackageParserJellyBean17.generatePackageInfo.call(p, GIDS, flags, firstInstallTime, lastUpdateTime,\n                    null, sUserState);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            return PackageParserJellyBean.generatePackageInfo.call(p, GIDS, flags, firstInstallTime, lastUpdateTime,\n                    null);\n        } else {\n            return mirror.android.content.pm.PackageParser.generatePackageInfo.call(p, GIDS, flags, firstInstallTime,\n                    lastUpdateTime);\n        }\n    }\n\n    public static void collectCertificates(PackageParser parser, Package p, int flags) throws Throwable {\n        if (API_LEVEL >= N) {\n            PackageParserNougat.collectCertificates.callWithException(p, flags);\n        } else if (API_LEVEL >= M) {\n            PackageParserMarshmallow.collectCertificates.callWithException(parser, p, flags);\n        } else if (API_LEVEL >= LOLLIPOP_MR1) {\n            PackageParserLollipop22.collectCertificates.callWithException(parser, p, flags);\n        } else if (API_LEVEL >= LOLLIPOP) {\n            PackageParserLollipop.collectCertificates.callWithException(parser, p, flags);\n        } else if (API_LEVEL >= JELLY_BEAN_MR1) {\n            PackageParserJellyBean17.collectCertificates.callWithException(parser, p, flags);\n        } else if (API_LEVEL >= JELLY_BEAN) {\n            PackageParserJellyBean.collectCertificates.callWithException(parser, p, flags);\n        } else {\n            mirror.android.content.pm.PackageParser.collectCertificates.call(parser, p, flags);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/ParceledListSliceCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport mirror.android.content.pm.ParceledListSlice;\nimport mirror.android.content.pm.ParceledListSliceJBMR2;\n\n/**\n * @author Lody\n *\n */\npublic class ParceledListSliceCompat {\n\n\tpublic static boolean isReturnParceledListSlice(Method method) {\n\t\treturn method != null && method.getReturnType() == ParceledListSlice.TYPE;\n\t}\n\n\tpublic static  Object create(List list) {\n\t\tif (ParceledListSliceJBMR2.ctor != null) {\n\t\t\treturn ParceledListSliceJBMR2.ctor.newInstance(list);\n\t\t} else {\n\t\t\tObject slice = ParceledListSlice.ctor.newInstance();\n\t\t\tfor (Object item : list) {\n\t\t\t\tParceledListSlice.append.call(slice, item);\n\t\t\t}\n\t\t\tParceledListSlice.setLastSlice.call(slice, true);\n\t\t\treturn slice;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/StorageManagerCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.os.Environment;\nimport android.os.storage.StorageManager;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\n\npublic class StorageManagerCompat {\n\n    private StorageManagerCompat() {\n    }\n\n    public static String[] getAllPoints(Context context) {\n        StorageManager manager = (StorageManager)\n                context.getSystemService(Activity.STORAGE_SERVICE);\n        String[] points = null;\n        try {\n            Method method = manager.getClass().getMethod(\"getVolumePaths\");\n            points = (String[]) method.invoke(manager);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return points;\n    }\n\n    public static boolean isMounted(Context context, String point) {\n        if (point == null)\n            return false;\n        StorageManager manager = (StorageManager)\n                context.getSystemService(Activity.STORAGE_SERVICE);\n        try {\n            Method method = manager.getClass().getMethod(\"getVolumeState\", String.class);\n            String state = (String) method.invoke(manager, point);\n            return Environment.MEDIA_MOUNTED.equals(state);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    public static ArrayList<String> getMountedPoints(Context context) {\n        StorageManager manager = (StorageManager)\n                context.getSystemService(Activity.STORAGE_SERVICE);\n        ArrayList<String> mountedPoints = new ArrayList<String>();\n        try {\n            Method getVolumePaths = manager.getClass().getMethod(\"getVolumePaths\");\n            String[] points = (String[]) getVolumePaths.invoke(manager);\n            if (points != null && points.length > 0) {\n                Method getVolumeState = manager.getClass().getMethod(\"getVolumeState\", String.class);\n                for (String point : points) {\n                    String state = (String) getVolumeState.invoke(manager, point);\n                    if (Environment.MEDIA_MOUNTED.equals(state))\n                        mountedPoints.add(point);\n                }\n                return mountedPoints;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/compat/SystemPropertiesCompat.java",
    "content": "package com.lody.virtual.helper.compat;\n\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport java.lang.reflect.InvocationTargetException;\n\npublic class SystemPropertiesCompat {\n\n    private static Class<?> sClass;\n\n    public SystemPropertiesCompat() {\n    }\n\n    private static Class getSystemPropertiesClass() throws ClassNotFoundException {\n        if (sClass == null) {\n            sClass = Class.forName(\"android.os.SystemProperties\");\n        }\n        return sClass;\n    }\n\n    private static String getInner(String key, String defaultValue)\n            throws NoSuchMethodException, IllegalAccessException,\n            InvocationTargetException, ClassNotFoundException {\n        Class clazz = getSystemPropertiesClass();\n        return (String) Reflect.on(clazz).call(\"get\", key, defaultValue).get();\n    }\n\n    public static String get(String key, String defaultValue) {\n        try {\n            return getInner(key, defaultValue);\n        } catch (Exception var3) {\n            var3.printStackTrace();\n            return defaultValue;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/ArrayUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport com.lody.virtual.helper.compat.ObjectsCompat;\n\n/**\n * @author Lody\n *\n */\npublic class ArrayUtils {\n\n\tpublic static Object[] push(Object[] array, Object item)\n\t{\n\t\tObject[] longer = new Object[array.length + 1];\n\t\tSystem.arraycopy(array, 0, longer, 0, array.length);\n\t\tlonger[array.length] = item;\n\t\treturn longer;\n\t}\n\n\tpublic static <T> boolean contains(T[] array, T value) {\n\t\treturn indexOf(array, value) != -1;\n\t}\n\tpublic static boolean contains(int[] array, int value) {\n\t\tif (array == null) return false;\n\t\tfor (int element : array) {\n\t\t\tif (element == value) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return first index of {@code value} in {@code array}, or {@code -1} if\n\t * not found.\n\t */\n\tpublic static <T> int indexOf(T[] array, T value) {\n\t\tif (array == null) return -1;\n\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\tif (ObjectsCompat.equals(array[i], value)) return i;\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static int protoIndexOf(Class<?>[] array, Class<?> type) {\n\t\tif (array == null) return -1;\n\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\tif (array[i] == type) return i;\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static int indexOfFirst(Object[] array, Class<?> type) {\n\t\tif (!isEmpty(array)) {\n\t\t\tint N = -1;\n\t\t\tfor (Object one : array) {\n\t\t\t\tN++;\n\t\t\t\tif (one != null && type == one.getClass()) {\n\t\t\t\t\treturn N;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static int protoIndexOf(Class<?>[] array, Class<?> type, int sequence) {\n\t\tif (array == null) {\n\t\t\treturn -1;\n\t\t}\n\t\twhile (sequence < array.length) {\n\t\t\tif (type == array[sequence]) {\n\t\t\t\treturn sequence;\n\t\t\t}\n\t\t\tsequence++;\n\t\t}\n\t\treturn -1;\n\t}\n\n\n\tpublic static int indexOfObject(Object[] array, Class<?> type, int sequence) {\n\t\tif (array == null) {\n\t\t\treturn -1;\n\t\t}\n\t\twhile (sequence < array.length) {\n\t\t\tif (type.isInstance(array[sequence])) {\n\t\t\t\treturn sequence;\n\t\t\t}\n\t\t\tsequence++;\n\t\t}\n\t\treturn -1;\n\t}\n\n\n\tpublic static int indexOf(Object[] array, Class<?> type, int sequence) {\n\t\tif (!isEmpty(array)) {\n\t\t\tint N = -1;\n\t\t\tfor (Object one : array) {\n\t\t\t\tN++;\n\t\t\t\tif (one != null && one.getClass() == type) {\n\t\t\t\t\tif (--sequence <= 0) {\n\t\t\t\t\t\treturn N;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static int indexOfLast(Object[] array, Class<?> type) {\n\t\tif (!isEmpty(array)) {\n\t\t\tfor (int N = array.length; N > 0; N--) {\n\t\t\t\tObject one = array[N - 1];\n\t\t\t\tif (one != null && one.getClass() == type) {\n\t\t\t\t\treturn N - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static <T> boolean isEmpty(T[] array) {\n\t\treturn array == null || array.length == 0;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> T getFirst(Object[] args, Class<?> clazz) {\n\t\tint index = indexOfFirst(args, clazz);\n\t\tif (index != -1) {\n\t\t\treturn (T) args[index];\n\t\t}\n\t\treturn null;\n\t}\n\n\n\tpublic static void checkOffsetAndCount(int arrayLength, int offset, int count) throws ArrayIndexOutOfBoundsException {\n\t\tif ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {\n\t\t\tthrow new ArrayIndexOutOfBoundsException(offset);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/AtomicFile.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\n/**\n * Static library support version of the framework's {@link android.util.AtomicFile},\n * a helper class for performing atomic operations on a file by creating a\n * backup file until a write has successfully completed.\n * <p>\n * Atomic file guarantees file integrity by ensuring that a file has\n * been completely written and sync'd to disk before removing its backup.\n * As long as the backup file exists, the original file is considered\n * to be invalid (left over from a previous attempt to write the file).\n * </p><p>\n * Atomic file does not confer any file locking semantics.\n * Do not use this class when the file may be accessed or modified concurrently\n * by multiple threads or processes.  The caller is responsible for ensuring\n * appropriate mutual exclusion invariants whenever it accesses the file.\n * </p>\n */\npublic class AtomicFile {\n    private final File mBaseName;\n    private final File mBackupName;\n\n    /**\n     * Create a new AtomicFile for a file located at the given File path.\n     * The secondary backup file will be the same file path with \".bak\" appended.\n     */\n    public AtomicFile(File baseName) {\n        mBaseName = baseName;\n        mBackupName = new File(baseName.getPath() + \".bak\");\n    }\n\n    /**\n     * Return the path to the base file.  You should not generally use this,\n     * as the data at that path may not be valid.\n     */\n    public File getBaseFile() {\n        return mBaseName;\n    }\n\n    /**\n     * Delete the atomic file.  This deletes both the base and backup files.\n     */\n    public void delete() {\n        mBaseName.delete();\n        mBackupName.delete();\n    }\n\n    /**\n     * Start a new write operation on the file.  This returns a FileOutputStream\n     * to which you can write the new file data.  The existing file is replaced\n     * with the new data.  You <em>must not</em> directly close the given\n     * FileOutputStream; instead call either {@link #finishWrite(FileOutputStream)}\n     * or {@link #failWrite(FileOutputStream)}.\n     * <p>\n     * <p>Note that if another thread is currently performing\n     * a write, this will simply replace whatever that thread is writing\n     * with the new file being written by this thread, and when the other\n     * thread finishes the write the new write operation will no longer be\n     * safe (or will be lost).  You must do your own threading protection for\n     * access to AtomicFile.\n     */\n    public FileOutputStream startWrite() throws IOException {\n        // Rename the current file so it may be used as a backup during the next read\n        if (mBaseName.exists()) {\n            if (!mBackupName.exists()) {\n                if (!mBaseName.renameTo(mBackupName)) {\n                    Log.w(\"AtomicFile\", \"Couldn't rename file \" + mBaseName\n                            + \" to backup file \" + mBackupName);\n                }\n            } else {\n                mBaseName.delete();\n            }\n        }\n        FileOutputStream str = null;\n        try {\n            str = new FileOutputStream(mBaseName);\n        } catch (FileNotFoundException e) {\n            File parent = mBaseName.getParentFile();\n            if (!parent.mkdir()) {\n                throw new IOException(\"Couldn't create directory \" + mBaseName);\n            }\n            try {\n                str = new FileOutputStream(mBaseName);\n            } catch (FileNotFoundException e2) {\n                throw new IOException(\"Couldn't create \" + mBaseName);\n            }\n        }\n        return str;\n    }\n\n    /**\n     * Call when you have successfully finished writing to the stream\n     * returned by {@link #startWrite()}.  This will close, sync, and\n     * commit the new data.  The next attempt to read the atomic file\n     * will return the new file stream.\n     */\n    public void finishWrite(FileOutputStream str) {\n        if (str != null) {\n            sync(str);\n            try {\n                str.close();\n                mBackupName.delete();\n            } catch (IOException e) {\n                Log.w(\"AtomicFile\", \"finishWrite: Got exception:\", e);\n            }\n        }\n    }\n\n    /**\n     * Call when you have failed for some reason at writing to the stream\n     * returned by {@link #startWrite()}.  This will close the current\n     * write stream, and roll back to the previous state of the file.\n     */\n    public void failWrite(FileOutputStream str) {\n        if (str != null) {\n            sync(str);\n            try {\n                str.close();\n                mBaseName.delete();\n                mBackupName.renameTo(mBaseName);\n            } catch (IOException e) {\n                Log.w(\"AtomicFile\", \"failWrite: Got exception:\", e);\n            }\n        }\n    }\n\n    /**\n     * Open the atomic file for reading.  If there previously was an\n     * incomplete write, this will roll back to the last good data before\n     * opening for read.  You should call close() on the FileInputStream when\n     * you are done reading from it.\n     * <p>\n     * <p>Note that if another thread is currently performing\n     * a write, this will incorrectly consider it to be in the state of a bad\n     * write and roll back, causing the new data currently being written to\n     * be dropped.  You must do your own threading protection for access to\n     * AtomicFile.\n     */\n    public FileInputStream openRead() throws FileNotFoundException {\n        if (mBackupName.exists()) {\n            mBaseName.delete();\n            mBackupName.renameTo(mBaseName);\n        }\n        return new FileInputStream(mBaseName);\n    }\n\n    /**\n     * A convenience for {@link #openRead()} that also reads all of the\n     * file contents into a byte array which is returned.\n     */\n    public byte[] readFully() throws IOException {\n        FileInputStream stream = openRead();\n        try {\n            int pos = 0;\n            int avail = stream.available();\n            byte[] data = new byte[avail];\n            while (true) {\n                int amt = stream.read(data, pos, data.length - pos);\n                //Log.i(\"foo\", \"Read \" + amt + \" bytes at \" + pos\n                //        + \" of avail \" + data.length);\n                if (amt <= 0) {\n                    //Log.i(\"foo\", \"**** FINISHED READING: pos=\" + pos\n                    //        + \" len=\" + data.length);\n                    return data;\n                }\n                pos += amt;\n                avail = stream.available();\n                if (avail > data.length - pos) {\n                    byte[] newData = new byte[pos + avail];\n                    System.arraycopy(data, 0, newData, 0, pos);\n                    data = newData;\n                }\n            }\n        } finally {\n            stream.close();\n        }\n    }\n\n    /**\n     * @deprecated This is not safe.\n     */\n    public void truncate() throws IOException {\n        try {\n            FileOutputStream fos = new FileOutputStream(mBaseName);\n            fos.getFD().sync();\n            fos.close();\n        } catch (FileNotFoundException e) {\n            throw new IOException(\"Couldn't append \" + mBaseName);\n        } catch (IOException e) {\n        }\n    }\n\n    /**\n     * @deprecated This is not safe.\n     */\n    @Deprecated public FileOutputStream openAppend() throws IOException {\n        try {\n            return new FileOutputStream(mBaseName, true);\n        } catch (FileNotFoundException e) {\n            throw new IOException(\"Couldn't append \" + mBaseName);\n        }\n    }\n\n    static boolean sync(FileOutputStream stream) {\n        try {\n            if (stream != null) {\n                stream.getFD().sync();\n            }\n            return true;\n        } catch (IOException e) {\n        }\n        return false;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/BitmapUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.PixelFormat;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\n\n/**\n * @author Lody\n */\npublic class BitmapUtils {\n\n    public static Bitmap drawableToBitmap(Drawable drawable) {\n        if (drawable instanceof BitmapDrawable) {\n            BitmapDrawable bitmapDrawable = ((BitmapDrawable) drawable);\n            return bitmapDrawable.getBitmap();\n        } else {\n            Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),\n                    drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);\n\n            Canvas canvas = new Canvas(bitmap);\n            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n            drawable.draw(canvas);\n            return bitmap;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/ClassUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\n/**\n * @author Lody\n *\n */\npublic class ClassUtils {\n\n\tpublic static boolean isClassExist(String className) {\n\t\ttry {\n\t\t\tClass.forName(className);\n\t\t\treturn true;\n\t\t} catch (ClassNotFoundException e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static void fixArgs(Class<?>[] types, Object[] args) {\n\t\tfor (int i = 0; i < types.length; i++) {\n\t\t\tif (types[i] == int.class && args[i] == null) {\n\t\t\t\targs[i] = 0;\n\t\t\t} else if (types[i] == boolean.class && args[i] == null) {\n\t\t\t\targs[i] = false;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/ComponentUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ComponentInfo;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.SpecialComponentList;\nimport com.lody.virtual.GmsSupport;\nimport com.lody.virtual.helper.compat.ObjectsCompat;\n\nimport static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;\n\n/**\n * @author Lody\n */\npublic class ComponentUtils {\n\n    public static String getTaskAffinity(ActivityInfo info) {\n        if (info.launchMode == LAUNCH_SINGLE_INSTANCE) {\n            return \"-SingleInstance-\" + info.packageName + \"/\" + info.name;\n        } else if (info.taskAffinity == null && info.applicationInfo.taskAffinity == null) {\n            return info.packageName;\n        } else if (info.taskAffinity != null) {\n            return info.taskAffinity;\n        }\n        return info.applicationInfo.taskAffinity;\n    }\n\n    public static boolean isSameIntent(Intent a, Intent b) {\n        if (a != null && b != null) {\n            if (!ObjectsCompat.equals(a.getAction(), b.getAction())) {\n                return false;\n            }\n            if (!ObjectsCompat.equals(a.getData(), b.getData())) {\n                return false;\n            }\n            if (!ObjectsCompat.equals(a.getType(), b.getType())) {\n                return false;\n            }\n            Object pkgA = a.getPackage();\n            if (pkgA == null && a.getComponent() != null) {\n                pkgA = a.getComponent().getPackageName();\n            }\n            String pkgB = b.getPackage();\n            if (pkgB == null && b.getComponent() != null) {\n                pkgB = b.getComponent().getPackageName();\n            }\n            if (!ObjectsCompat.equals(pkgA, pkgB)) {\n                return false;\n            }\n            if (!ObjectsCompat.equals(a.getComponent(), b.getComponent())) {\n                return false;\n            }\n            if (!ObjectsCompat.equals(a.getCategories(), b.getCategories())) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public static String getProcessName(ComponentInfo componentInfo) {\n        String processName = componentInfo.processName;\n        if (processName == null) {\n            processName = componentInfo.packageName;\n            componentInfo.processName = processName;\n        }\n        return processName;\n    }\n\n    public static boolean isSameComponent(ComponentInfo first, ComponentInfo second) {\n\n        if (first != null && second != null) {\n            String pkg1 = first.packageName + \"\";\n            String pkg2 = second.packageName + \"\";\n            String name1 = first.name + \"\";\n            String name2 = second.name + \"\";\n            return pkg1.equals(pkg2) && name1.equals(name2);\n        }\n        return false;\n    }\n\n    public static ComponentName toComponentName(ComponentInfo componentInfo) {\n        return new ComponentName(componentInfo.packageName, componentInfo.name);\n    }\n\n    public static boolean isSystemApp(ApplicationInfo applicationInfo) {\n        return !GmsSupport.isGmsFamilyPackage(applicationInfo.packageName)\n                && ((ApplicationInfo.FLAG_SYSTEM & applicationInfo.flags) != 0\n                || SpecialComponentList.isSpecSystemPackage(applicationInfo.packageName));\n    }\n\n    public static boolean isStubComponent(Intent intent) {\n        return intent != null\n                && intent.getComponent() != null\n                && VirtualCore.get().getHostPkg().equals(intent.getComponent().getPackageName());\n    }\n\n    public static Intent redirectBroadcastIntent(Intent intent, int userId) {\n        Intent newIntent = intent.cloneFilter();\n        newIntent.setComponent(null);\n        newIntent.setPackage(null);\n        ComponentName component = intent.getComponent();\n        String pkg = intent.getPackage();\n        if (component != null) {\n            newIntent.putExtra(\"_VA_|_user_id_\", userId);\n            // 这里显式意图被重定位成 _VA_PKGNAME_CLASSNAME 的格式，与前面注册的时候对应\n            newIntent.setAction(String.format(\"_VA_%s_%s\", component.getPackageName(), component.getClassName()));\n            newIntent.putExtra(\"_VA_|_component_\", component);\n            newIntent.putExtra(\"_VA_|_intent_\", new Intent(intent));\n        } else if (pkg != null) {\n            newIntent.putExtra(\"_VA_|_user_id_\", userId);\n            newIntent.putExtra(\"_VA_|_creator_\", pkg);\n            newIntent.putExtra(\"_VA_|_intent_\", new Intent(intent));\n            String protectedAction = SpecialComponentList.protectAction(intent.getAction());\n            if (protectedAction != null) {\n                newIntent.setAction(protectedAction);\n            }\n        } else {\n            newIntent.putExtra(\"_VA_|_user_id_\", userId);\n            newIntent.putExtra(\"_VA_|_intent_\", new Intent(intent));\n            String protectedAction = SpecialComponentList.protectAction(intent.getAction());\n            if (protectedAction != null) {\n                newIntent.setAction(protectedAction);\n            }\n        }\n        return newIntent;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/DrawableUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.PixelFormat;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\n\npublic class DrawableUtils {\n    public static Bitmap drawableToBitMap(Drawable drawable) {\n        if (drawable == null) {\n            return null;\n        }\n        if (drawable instanceof BitmapDrawable) {\n            BitmapDrawable bitmapDrawable = ((BitmapDrawable) drawable);\n            return bitmapDrawable.getBitmap();\n        } else {\n            Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),\n                    drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);\n            Canvas canvas = new Canvas(bitmap);\n            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n            drawable.draw(canvas);\n            return bitmap;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/FastXmlSerializer.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.helper.utils;\n\nimport org.xmlpull.v1.XmlSerializer;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.io.Writer;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.CharsetEncoder;\nimport java.nio.charset.CoderResult;\nimport java.nio.charset.IllegalCharsetNameException;\nimport java.nio.charset.UnsupportedCharsetException;\n\n/**\n * This is a quick and dirty implementation of XmlSerializer that isn't horribly\n * painfully slow like the normal one.  It only does what is needed for the\n * specific XML files being written with it.\n */\npublic class FastXmlSerializer implements XmlSerializer {\n    private static final String ESCAPE_TABLE[] = new String[] {\n        null,     null,     null,     null,     null,     null,     null,     null,  // 0-7\n        null,     null,     null,     null,     null,     null,     null,     null,  // 8-15\n        null,     null,     null,     null,     null,     null,     null,     null,  // 16-23\n        null,     null,     null,     null,     null,     null,     null,     null,  // 24-31\n        null,     null,     \"&quot;\", null,     null,     null,     \"&amp;\",  null,  // 32-39\n        null,     null,     null,     null,     null,     null,     null,     null,  // 40-47\n        null,     null,     null,     null,     null,     null,     null,     null,  // 48-55\n        null,     null,     null,     null,     \"&lt;\",   null,     \"&gt;\",   null,  // 56-63\n    };\n\n    private static final int BUFFER_LEN = 8192;\n\n    private static String sSpace = \"                                                              \";\n\n    private final char[] mText = new char[BUFFER_LEN];\n    private int mPos;\n\n    private Writer mWriter;\n\n    private OutputStream mOutputStream;\n    private CharsetEncoder mCharset;\n    private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN);\n\n    private boolean mIndent = false;\n    private boolean mInTag;\n\n    private int mNesting = 0;\n    private boolean mLineStart = true;\n\n    private void append(char c) throws IOException {\n        int pos = mPos;\n        if (pos >= (BUFFER_LEN-1)) {\n            flush();\n            pos = mPos;\n        }\n        mText[pos] = c;\n        mPos = pos+1;\n    }\n\n    private void append(String str, int i, final int length) throws IOException {\n        if (length > BUFFER_LEN) {\n            final int end = i + length;\n            while (i < end) {\n                int next = i + BUFFER_LEN;\n                append(str, i, next<end ? BUFFER_LEN : (end-i));\n                i = next;\n            }\n            return;\n        }\n        int pos = mPos;\n        if ((pos+length) > BUFFER_LEN) {\n            flush();\n            pos = mPos;\n        }\n        str.getChars(i, i+length, mText, pos);\n        mPos = pos + length;\n    }\n\n    private void append(char[] buf, int i, final int length) throws IOException {\n        if (length > BUFFER_LEN) {\n            final int end = i + length;\n            while (i < end) {\n                int next = i + BUFFER_LEN;\n                append(buf, i, next<end ? BUFFER_LEN : (end-i));\n                i = next;\n            }\n            return;\n        }\n        int pos = mPos;\n        if ((pos+length) > BUFFER_LEN) {\n            flush();\n            pos = mPos;\n        }\n        System.arraycopy(buf, i, mText, pos, length);\n        mPos = pos + length;\n    }\n\n    private void append(String str) throws IOException {\n        append(str, 0, str.length());\n    }\n\n    private void appendIndent(int indent) throws IOException {\n        indent *= 4;\n        if (indent > sSpace.length()) {\n            indent = sSpace.length();\n        }\n        append(sSpace, 0, indent);\n    }\n\n    private void escapeAndAppendString(final String string) throws IOException {\n        final int N = string.length();\n        final char NE = (char)ESCAPE_TABLE.length;\n        final String[] escapes = ESCAPE_TABLE;\n        int lastPos = 0;\n        int pos;\n        for (pos=0; pos<N; pos++) {\n            char c = string.charAt(pos);\n            if (c >= NE) continue;\n            String escape = escapes[c];\n            if (escape == null) continue;\n            if (lastPos < pos) append(string, lastPos, pos-lastPos);\n            lastPos = pos + 1;\n            append(escape);\n        }\n        if (lastPos < pos) append(string, lastPos, pos-lastPos);\n    }\n\n    private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {\n        final char NE = (char)ESCAPE_TABLE.length;\n        final String[] escapes = ESCAPE_TABLE;\n        int end = start+len;\n        int lastPos = start;\n        int pos;\n        for (pos=start; pos<end; pos++) {\n            char c = buf[pos];\n            if (c >= NE) continue;\n            String escape = escapes[c];\n            if (escape == null) continue;\n            if (lastPos < pos) append(buf, lastPos, pos-lastPos);\n            lastPos = pos + 1;\n            append(escape);\n        }\n        if (lastPos < pos) append(buf, lastPos, pos-lastPos);\n    }\n\n    public XmlSerializer attribute(String namespace, String name, String value) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        append(' ');\n        if (namespace != null) {\n            append(namespace);\n            append(':');\n        }\n        append(name);\n        append(\"=\\\"\");\n\n        escapeAndAppendString(value);\n        append('\"');\n        mLineStart = false;\n        return this;\n    }\n\n    public void cdsect(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void comment(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void docdecl(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {\n        flush();\n    }\n\n    public XmlSerializer endTag(String namespace, String name) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        mNesting--;\n        if (mInTag) {\n            append(\" />\\n\");\n        } else {\n            if (mIndent && mLineStart) {\n                appendIndent(mNesting);\n            }\n            append(\"</\");\n            if (namespace != null) {\n                append(namespace);\n                append(':');\n            }\n            append(name);\n            append(\">\\n\");\n        }\n        mLineStart = true;\n        mInTag = false;\n        return this;\n    }\n\n    public void entityRef(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    private void flushBytes() throws IOException {\n        int position;\n        if ((position = mBytes.position()) > 0) {\n            mBytes.flip();\n            mOutputStream.write(mBytes.array(), 0, position);\n            mBytes.clear();\n        }\n    }\n\n    public void flush() throws IOException {\n        //Log.i(\"PackageManager\", \"flush mPos=\" + mPos);\n        if (mPos > 0) {\n            if (mOutputStream != null) {\n                CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);\n                CoderResult result = mCharset.encode(charBuffer, mBytes, true);\n                while (true) {\n                    if (result.isError()) {\n                        throw new IOException(result.toString());\n                    } else if (result.isOverflow()) {\n                        flushBytes();\n                        result = mCharset.encode(charBuffer, mBytes, true);\n                        continue;\n                    }\n                    break;\n                }\n                flushBytes();\n                mOutputStream.flush();\n            } else {\n                mWriter.write(mText, 0, mPos);\n                mWriter.flush();\n            }\n            mPos = 0;\n        }\n    }\n\n    public int getDepth() {\n        throw new UnsupportedOperationException();\n    }\n\n    public boolean getFeature(String name) {\n        throw new UnsupportedOperationException();\n    }\n\n    public String getName() {\n        throw new UnsupportedOperationException();\n    }\n\n    public String getNamespace() {\n        throw new UnsupportedOperationException();\n    }\n\n    public String getPrefix(String namespace, boolean generatePrefix)\n            throws IllegalArgumentException {\n        throw new UnsupportedOperationException();\n    }\n\n    public Object getProperty(String name) {\n        throw new UnsupportedOperationException();\n    }\n\n    public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void processingInstruction(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void setFeature(String name, boolean state) throws IllegalArgumentException,\n            IllegalStateException {\n        if (name.equals(\"http://xmlpull.org/v1/doc/features.html#indent-output\")) {\n            mIndent = true;\n            return;\n        }\n        throw new UnsupportedOperationException();\n    }\n\n    public void setOutput(OutputStream os, String encoding) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        if (os == null)\n            throw new IllegalArgumentException();\n        try {\n            mCharset = Charset.forName(encoding).newEncoder();\n        } catch (IllegalCharsetNameException e) {\n            throw (UnsupportedEncodingException) (new UnsupportedEncodingException(\n                    encoding).initCause(e));\n        } catch (UnsupportedCharsetException e) {\n            throw (UnsupportedEncodingException) (new UnsupportedEncodingException(\n                    encoding).initCause(e));\n        }\n        mOutputStream = os;\n    }\n\n    public void setOutput(Writer writer) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        mWriter = writer;\n    }\n\n    public void setPrefix(String prefix, String namespace) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void setProperty(String name, Object value) throws IllegalArgumentException,\n            IllegalStateException {\n        throw new UnsupportedOperationException();\n    }\n\n    public void startDocument(String encoding, Boolean standalone) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        append(\"<?xml version='1.0' encoding='utf-8' standalone='\"\n                + (standalone ? \"yes\" : \"no\") + \"' ?>\\n\");\n        mLineStart = true;\n    }\n\n    public XmlSerializer startTag(String namespace, String name) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        if (mInTag) {\n            append(\">\\n\");\n        }\n        if (mIndent) {\n            appendIndent(mNesting);\n        }\n        mNesting++;\n        append('<');\n        if (namespace != null) {\n            append(namespace);\n            append(':');\n        }\n        append(name);\n        mInTag = true;\n        mLineStart = false;\n        return this;\n    }\n\n    public XmlSerializer text(char[] buf, int start, int len) throws IOException,\n            IllegalArgumentException, IllegalStateException {\n        if (mInTag) {\n            append(\">\");\n            mInTag = false;\n        }\n        escapeAndAppendString(buf, start, len);\n        if (mIndent) {\n            mLineStart = buf[start+len-1] == '\\n';\n        }\n        return this;\n    }\n\n    public XmlSerializer text(String text) throws IOException, IllegalArgumentException,\n            IllegalStateException {\n        if (mInTag) {\n            append(\">\");\n            mInTag = false;\n        }\n        escapeAndAppendString(text);\n        if (mIndent) {\n            mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\\n');\n        }\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/FileUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.system.Os;\nimport android.text.TextUtils;\n\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.RandomAccessFile;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.channels.Channels;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.ReadableByteChannel;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author Lody\n */\npublic class FileUtils {\n    /**\n     * @param path\n     * @param mode {@link FileMode}\n     * @throws Exception\n     */\n    public static void chmod(String path, int mode) throws Exception {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            try {\n                Os.chmod(path, mode);\n                return;\n            } catch (Exception e) {\n                // ignore\n            }\n        }\n\n        File file = new File(path);\n        String cmd = \"chmod \";\n        if (file.isDirectory()) {\n            cmd += \" -R \";\n        }\n        String cmode = String.format(\"%o\", mode);\n        Runtime.getRuntime().exec(cmd + cmode + \" \" + path).waitFor();\n    }\n\n    public static void createSymlink(String oldPath, String newPath) throws Exception {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            Os.symlink(oldPath, newPath);\n        } else {\n            Runtime.getRuntime().exec(\"ln -s \" + oldPath + \" \" + newPath).waitFor();\n        }\n    }\n\n    public static boolean isSymlink(File file) throws IOException {\n        if (file == null)\n            throw new NullPointerException(\"File must not be null\");\n        File canon;\n        if (file.getParent() == null) {\n            canon = file;\n        } else {\n            File canonDir = file.getParentFile().getCanonicalFile();\n            canon = new File(canonDir, file.getName());\n        }\n        return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());\n    }\n\n    public static void writeParcelToFile(Parcel p, File file) throws IOException {\n        FileOutputStream fos = new FileOutputStream(file);\n        fos.write(p.marshall());\n        fos.close();\n    }\n\n    public static byte[] toByteArray(InputStream inStream) throws IOException {\n        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();\n        byte[] buff = new byte[100];\n        int rc;\n        while ((rc = inStream.read(buff, 0, 100)) > 0) {\n            swapStream.write(buff, 0, rc);\n        }\n        return swapStream.toByteArray();\n    }\n\n    public static boolean deleteDir(File dir) {\n        if (dir.isDirectory()) {\n            String[] children = dir.list();\n            for (String file : children) {\n                boolean success = deleteDir(new File(dir, file));\n                if (!success) {\n                    return false;\n                }\n            }\n        }\n        return dir.delete();\n    }\n\n    public static boolean deleteDir(String dir) {\n        return deleteDir(new File(dir));\n    }\n\n    public static void writeToFile(InputStream dataIns, File target) throws IOException {\n        final int BUFFER = 1024;\n        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target));\n        int count;\n        byte data[] = new byte[BUFFER];\n        while ((count = dataIns.read(data, 0, BUFFER)) != -1) {\n            bos.write(data, 0, count);\n        }\n        bos.close();\n    }\n\n    public static void writeToFile(byte[] data, File target) throws IOException {\n        FileOutputStream fo = null;\n        ReadableByteChannel src = null;\n        FileChannel out = null;\n        try {\n            src = Channels.newChannel(new ByteArrayInputStream(data));\n            fo = new FileOutputStream(target);\n            out = fo.getChannel();\n            out.transferFrom(src, 0, data.length);\n        } finally {\n            if (fo != null) {\n                fo.close();\n            }\n            if (src != null) {\n                src.close();\n            }\n            if (out != null) {\n                out.close();\n            }\n        }\n    }\n\n    public static void copyFile(File source, File target) throws IOException {\n\n        FileInputStream inputStream = null;\n        FileOutputStream outputStream = null;\n        try {\n            inputStream = new FileInputStream(source);\n            outputStream = new FileOutputStream(target);\n            FileChannel iChannel = inputStream.getChannel();\n            FileChannel oChannel = outputStream.getChannel();\n\n            ByteBuffer buffer = ByteBuffer.allocate(1024);\n            while (true) {\n                buffer.clear();\n                int r = iChannel.read(buffer);\n                if (r == -1)\n                    break;\n                buffer.limit(buffer.position());\n                buffer.position(0);\n                oChannel.write(buffer);\n            }\n        } finally {\n            closeQuietly(inputStream);\n            closeQuietly(outputStream);\n        }\n    }\n\n    public static void closeQuietly(Closeable closeable) {\n        if (closeable != null) {\n            try {\n                closeable.close();\n            } catch (Exception ignored) {\n            }\n        }\n    }\n\n    public static int peekInt(byte[] bytes, int value, ByteOrder endian) {\n        int v2;\n        int v0;\n        if (endian == ByteOrder.BIG_ENDIAN) {\n            v0 = value + 1;\n            v2 = v0 + 1;\n            v0 = (bytes[v0] & 255) << 16 | (bytes[value] & 255) << 24 | (bytes[v2] & 255) << 8 | bytes[v2 + 1] & 255;\n        } else {\n            v0 = value + 1;\n            v2 = v0 + 1;\n            v0 = (bytes[v0] & 255) << 8 | bytes[value] & 255 | (bytes[v2] & 255) << 16 | (bytes[v2 + 1] & 255) << 24;\n        }\n\n        return v0;\n    }\n\n    private static boolean isValidExtFilenameChar(char c) {\n        switch (c) {\n            case '\\0':\n            case '/':\n                return false;\n            default:\n                return true;\n        }\n    }\n\n    /**\n     * Check if given filename is valid for an ext4 filesystem.\n     */\n    public static boolean isValidExtFilename(String name) {\n        return (name != null) && name.equals(buildValidExtFilename(name));\n    }\n\n    /**\n     * Mutate the given filename to make it valid for an ext4 filesystem,\n     * replacing any invalid characters with \"_\".\n     */\n    public static String buildValidExtFilename(String name) {\n        if (TextUtils.isEmpty(name) || \".\".equals(name) || \"..\".equals(name)) {\n            return \"(invalid)\";\n        }\n        final StringBuilder res = new StringBuilder(name.length());\n        for (int i = 0; i < name.length(); i++) {\n            final char c = name.charAt(i);\n            if (isValidExtFilenameChar(c)) {\n                res.append(c);\n            } else {\n                res.append('_');\n            }\n        }\n        return res.toString();\n    }\n\n    public interface FileMode {\n        int MODE_ISUID = 04000;\n        int MODE_ISGID = 02000;\n        int MODE_ISVTX = 01000;\n        int MODE_IRUSR = 00400;\n        int MODE_IWUSR = 00200;\n        int MODE_IXUSR = 00100;\n        int MODE_IRGRP = 00040;\n        int MODE_IWGRP = 00020;\n        int MODE_IXGRP = 00010;\n        int MODE_IROTH = 00004;\n        int MODE_IWOTH = 00002;\n        int MODE_IXOTH = 00001;\n\n        int MODE_755 = MODE_IRUSR | MODE_IWUSR | MODE_IXUSR\n                | MODE_IRGRP | MODE_IXGRP\n                | MODE_IROTH | MODE_IXOTH;\n    }\n\n    /**\n     * Lock the specified fle\n     */\n    public static class FileLock {\n        private static FileLock singleton;\n        private Map<String, FileLockCount> mRefCountMap = new ConcurrentHashMap<String, FileLockCount>();\n\n        public static FileLock getInstance() {\n            if (singleton == null) {\n                singleton = new FileLock();\n            }\n            return singleton;\n        }\n\n        private int RefCntInc(String filePath, java.nio.channels.FileLock fileLock, RandomAccessFile randomAccessFile,\n                              FileChannel fileChannel) {\n            int refCount;\n            if (this.mRefCountMap.containsKey(filePath)) {\n                FileLockCount fileLockCount = this.mRefCountMap.get(filePath);\n                int i = fileLockCount.mRefCount;\n                fileLockCount.mRefCount = i + 1;\n                refCount = i;\n            } else {\n                refCount = 1;\n                this.mRefCountMap.put(filePath, new FileLockCount(fileLock, refCount, randomAccessFile, fileChannel));\n\n            }\n            return refCount;\n        }\n\n        private int RefCntDec(String filePath) {\n            int refCount = 0;\n            if (this.mRefCountMap.containsKey(filePath)) {\n                FileLockCount fileLockCount = this.mRefCountMap.get(filePath);\n                int i = fileLockCount.mRefCount - 1;\n                fileLockCount.mRefCount = i;\n                refCount = i;\n                if (refCount <= 0) {\n                    this.mRefCountMap.remove(filePath);\n                }\n            }\n            return refCount;\n        }\n\n        public boolean LockExclusive(File targetFile) {\n\n            if (targetFile == null) {\n                return false;\n            }\n            try {\n                File lockFile = new File(targetFile.getParentFile().getAbsolutePath().concat(\"/lock\"));\n                if (!lockFile.exists()) {\n                    lockFile.createNewFile();\n                }\n                RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile.getAbsolutePath(), \"rw\");\n                FileChannel channel = randomAccessFile.getChannel();\n                java.nio.channels.FileLock lock = channel.lock();\n                if (!lock.isValid()) {\n                    return false;\n                }\n                RefCntInc(lockFile.getAbsolutePath(), lock, randomAccessFile, channel);\n                return true;\n            } catch (Exception e) {\n                return false;\n            }\n        }\n\n        /**\n         * unlock odex file\n         **/\n        public void unLock(File targetFile) {\n\n            File lockFile = new File(targetFile.getParentFile().getAbsolutePath().concat(\"/lock\"));\n            if (!lockFile.exists()) {\n                return;\n            }\n            if (this.mRefCountMap.containsKey(lockFile.getAbsolutePath())) {\n                FileLockCount fileLockCount = this.mRefCountMap.get(lockFile.getAbsolutePath());\n                if (fileLockCount != null) {\n                    java.nio.channels.FileLock fileLock = fileLockCount.mFileLock;\n                    RandomAccessFile randomAccessFile = fileLockCount.fOs;\n                    FileChannel fileChannel = fileLockCount.fChannel;\n                    try {\n                        if (RefCntDec(lockFile.getAbsolutePath()) <= 0) {\n                            if (fileLock != null && fileLock.isValid()) {\n                                fileLock.release();\n                            }\n                            if (randomAccessFile != null) {\n                                randomAccessFile.close();\n                            }\n                            if (fileChannel != null) {\n                                fileChannel.close();\n                            }\n                        }\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n\n        private class FileLockCount {\n            FileChannel fChannel;\n            RandomAccessFile fOs;\n            java.nio.channels.FileLock mFileLock;\n            int mRefCount;\n\n            FileLockCount(java.nio.channels.FileLock fileLock, int mRefCount, RandomAccessFile fOs,\n                          FileChannel fChannel) {\n                this.mFileLock = fileLock;\n                this.mRefCount = mRefCount;\n                this.fOs = fOs;\n                this.fChannel = fChannel;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/MD5Utils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\nimport android.text.TextUtils;\n\n/**\n * @author Lody\n *\n *\n */\npublic class MD5Utils {\n\n\t/**\n\t * 默认的密码字符串组合，用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合\n\t */\n\tprotected static char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',\n\t\t\t'f'};\n\tprotected static MessageDigest MESSAGE_DIGEST_5 = null;\n\n\tstatic {\n\t\ttry {\n\t\t\tMESSAGE_DIGEST_5 = MessageDigest.getInstance(\"MD5\");\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\tpublic static String getFileMD5String(File file) throws IOException {\n\t\tInputStream fis;\n\t\tfis = new FileInputStream(file);\n\t\tbyte[] buffer = new byte[1024];\n\t\tint numRead;\n\t\twhile ((numRead = fis.read(buffer)) > 0) {\n\t\t\tMESSAGE_DIGEST_5.update(buffer, 0, numRead);\n\t\t}\n\t\tfis.close();\n\t\treturn bufferToHex(MESSAGE_DIGEST_5.digest());\n\t}\n\n\tpublic static String getFileMD5String(InputStream in) throws IOException {\n\t\tbyte[] buffer = new byte[1024];\n\t\tint numRead;\n\t\twhile ((numRead = in.read(buffer)) > 0) {\n\t\t\tMESSAGE_DIGEST_5.update(buffer, 0, numRead);\n\t\t}\n\t\tin.close();\n\t\treturn bufferToHex(MESSAGE_DIGEST_5.digest());\n\t}\n\tprivate static String bufferToHex(byte bytes[]) {\n\t\treturn bufferToHex(bytes, 0, bytes.length);\n\t}\n\tprivate static String bufferToHex(byte bytes[], int m, int n) {\n\t\tStringBuffer stringbuffer = new StringBuffer(2 * n);\n\t\tint k = m + n;\n\t\tfor (int l = m; l < k; l++) {\n\t\t\tappendHexPair(bytes[l], stringbuffer);\n\t\t}\n\t\treturn stringbuffer.toString();\n\t}\n\tprivate static void appendHexPair(byte bt, StringBuffer stringbuffer) {\n\t\tchar c0 = HEX_DIGITS[(bt & 0xf0) >> 4];\n\t\tchar c1 = HEX_DIGITS[bt & 0xf];\n\t\tstringbuffer.append(c0);\n\t\tstringbuffer.append(c1);\n\t}\n\n\tpublic static boolean compareFiles(File one, File two) throws IOException {\n\n\t\tif (one.getAbsolutePath().equals(two.getAbsolutePath())) {\n\t\t\t// 是同一个文件\n\t\t\treturn true;\n\t\t}\n\t\tString md5_1 = getFileMD5String(one);\n\t\tString md5_2 = getFileMD5String(two);\n\t\treturn TextUtils.equals(md5_1, md5_2);\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/Mark.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.SOURCE)\npublic @interface Mark {\n    String value() default \"\";\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/OSUtils.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Properties;\n\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\n\n/**\n * Created by 247321453 on 2016/7/17.\n */\npublic class OSUtils {\n\tprivate static final String KEY_EMUI_VERSION_CODE = \"ro.build.version.emui\";\n\tprivate static final String KEY_MIUI_VERSION_CODE = \"ro.miui.ui.version.code\";\n\tprivate static final String KEY_MIUI_VERSION_NAME = \"ro.miui.ui.version.name\";\n\tprivate static final String KEY_MIUI_INTERNAL_STORAGE = \"ro.miui.internal.storage\";\n\n\tprivate static final OSUtils sOSUtils = new OSUtils();\n\tprivate boolean emui;\n\tprivate boolean miui;\n\tprivate boolean flyme;\n\tprivate String miuiVersion;\n\tprivate OSUtils() {\n\t\tProperties properties;\n\t\ttry {\n\t\t\tproperties = new Properties();\n\t\t\tproperties.load(new FileInputStream(new File(Environment.getRootDirectory(), \"build.prop\")));\n\t\t} catch (IOException e) {\n\t\t\tproperties = null;\n\t\t}\n\t\tif (properties != null) {\n\t\t\temui = !TextUtils.isEmpty(properties.getProperty(KEY_EMUI_VERSION_CODE));\n\t\t\tmiuiVersion = properties.getProperty(KEY_MIUI_VERSION_CODE);\n\t\t\tmiui = !TextUtils.isEmpty(miuiVersion) || !TextUtils.isEmpty(properties.getProperty(KEY_MIUI_VERSION_NAME))\n\t\t\t\t\t|| !TextUtils.isEmpty(properties.getProperty(KEY_MIUI_INTERNAL_STORAGE));\n\t\t}\n\t\tflyme = hasFlyme();\n\t}\n\n\tpublic static OSUtils getInstance() {\n\t\treturn sOSUtils;\n\t}\n\n\tpublic String getMiuiVersion() {\n\t\treturn miuiVersion;\n\t}\n\n\tpublic boolean isEmui() {\n\t\treturn emui;\n\t}\n\n\tpublic boolean isMiui() {\n\t\treturn miui;\n\t}\n\n\tpublic boolean isFlyme() {\n\t\treturn flyme;\n\t}\n\n\tprivate boolean hasFlyme() {\n\t\ttry {\n\t\t\tfinal Method method = Build.class.getMethod(\"hasSmartBar\");\n\t\t\treturn method != null;\n\t\t} catch (final Exception e) {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/Reflect.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * 一个拥有流畅特性(Fluent-API)的反射工具类, 使用起来就像直接调用一样流畅易懂.\n *\n * @author Lody\n */\npublic class Reflect {\n\n    private final Object object;\n    private final boolean isClass;\n\n    private Reflect(Class<?> type) {\n        this.object = type;\n        this.isClass = true;\n    }\n\n    private Reflect(Object object) {\n        this.object = object;\n        this.isClass = false;\n    }\n\n    /**\n     * 根据指定的类名构建反射工具类\n     *\n     * @param name 类的全名\n     * @return 反射工具类\n     * @throws ReflectException 如果反射出现意外\n     * @see #on(Class)\n     */\n    public static Reflect on(String name) throws ReflectException {\n        return on(forName(name));\n    }\n\n    /**\n     * 从指定的类加载起寻找类,并构建反射工具类\n     *\n     * @param name        类的全名\n     * @param classLoader 需要构建工具类的类的类加载器 loaded.\n     * @return 反射工具类\n     * @throws ReflectException 如果反射出现意外\n     * @see #on(Class)\n     */\n    public static Reflect on(String name, ClassLoader classLoader) throws ReflectException {\n        return on(forName(name, classLoader));\n    }\n\n    /**\n     * 根据指定的类构建反射工具类\n     * <p>\n     * 当你需要访问静态字段的时候本方法适合你, 你还可以通过调用 {@link #create(Object...)} 创建一个对象.\n     *\n     * @param clazz 需要构建反射工具类的类\n     * @return 反射工具类\n     */\n    public static Reflect on(Class<?> clazz) {\n        return new Reflect(clazz);\n    }\n\n    // ---------------------------------------------------------------------\n    // 构造器\n    // ---------------------------------------------------------------------\n\n    /**\n     * Wrap an object.\n     * <p>\n     * Use this when you want to access instance fields and methods on any\n     * {@link Object}\n     *\n     * @param object The object to be wrapped\n     * @return A wrapped object, to be used for further reflection.\n     */\n    public static Reflect on(Object object) {\n        return new Reflect(object);\n    }\n\n    /**\n     * 让一个{@link AccessibleObject}可访问.\n     *\n     * @param accessible\n     * @param <T>\n     * @return\n     */\n    public static <T extends AccessibleObject> T accessible(T accessible) {\n        if (accessible == null) {\n            return null;\n        }\n\n        if (accessible instanceof Member) {\n            Member member = (Member) accessible;\n\n            if (Modifier.isPublic(member.getModifiers())\n                    && Modifier.isPublic(member.getDeclaringClass().getModifiers())) {\n\n                return accessible;\n            }\n        }\n        if (!accessible.isAccessible()) {\n            accessible.setAccessible(true);\n        }\n\n        return accessible;\n    }\n\n    // ---------------------------------------------------------------------\n    // Fluent Reflection API\n    // ---------------------------------------------------------------------\n\n    /**\n     * 将给定字符串的开头改为小写.\n     *\n     * @param string\n     * @return\n     */\n    private static String property(String string) {\n        int length = string.length();\n\n        if (length == 0) {\n            return \"\";\n        } else if (length == 1) {\n            return string.toLowerCase();\n        } else {\n            return string.substring(0, 1).toLowerCase() + string.substring(1);\n        }\n    }\n\n    private static Reflect on(Constructor<?> constructor, Object... args) throws ReflectException {\n        try {\n            return on(accessible(constructor).newInstance(args));\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    private static Reflect on(Method method, Object object, Object... args) throws ReflectException {\n        try {\n            accessible(method);\n\n            if (method.getReturnType() == void.class) {\n                method.invoke(object, args);\n                return on(object);\n            } else {\n                return on(method.invoke(object, args));\n            }\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    /**\n     * 取得内部维护的对象.\n     */\n    private static Object unwrap(Object object) {\n        if (object instanceof Reflect) {\n            return ((Reflect) object).get();\n        }\n\n        return object;\n    }\n\n    /**\n     * 将Object数组转换为其类型的数组. 如果对象中包含null,我们用NULL.class代替.\n     *\n     * @see Object#getClass()\n     */\n    private static Class<?>[] types(Object... values) {\n        if (values == null) {\n            return new Class[0];\n        }\n\n        Class<?>[] result = new Class[values.length];\n\n        for (int i = 0; i < values.length; i++) {\n            Object value = values[i];\n            result[i] = value == null ? NULL.class : value.getClass();\n        }\n\n        return result;\n    }\n\n    /**\n     * 取得一个类,此操作会初始化类的static区域.\n     *\n     * @see Class#forName(String)\n     */\n    private static Class<?> forName(String name) throws ReflectException {\n        try {\n            return Class.forName(name);\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    private static Class<?> forName(String name, ClassLoader classLoader) throws ReflectException {\n        try {\n            return Class.forName(name, true, classLoader);\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    /**\n     * 如果给定的Class是原始类型,那么将其包装为对象类型, 否则返回本身.\n     */\n    public static Class<?> wrapper(Class<?> type) {\n        if (type == null) {\n            return null;\n        } else if (type.isPrimitive()) {\n            if (boolean.class == type) {\n                return Boolean.class;\n            } else if (int.class == type) {\n                return Integer.class;\n            } else if (long.class == type) {\n                return Long.class;\n            } else if (short.class == type) {\n                return Short.class;\n            } else if (byte.class == type) {\n                return Byte.class;\n            } else if (double.class == type) {\n                return Double.class;\n            } else if (float.class == type) {\n                return Float.class;\n            } else if (char.class == type) {\n                return Character.class;\n            } else if (void.class == type) {\n                return Void.class;\n            }\n        }\n\n        return type;\n    }\n\n    /**\n     * 取得内部维护的实际对象\n     *\n     * @param <T>\n     * @return\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T> T get() {\n        return (T) object;\n    }\n\n    /**\n     * 设置指定字段为指定值\n     *\n     * @param name\n     * @param value\n     * @return\n     * @throws ReflectException\n     */\n    public Reflect set(String name, Object value) throws ReflectException {\n        try {\n            Field field = field0(name);\n            field.setAccessible(true);\n            field.set(object, unwrap(value));\n            return this;\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    /**\n     * @param name name\n     * @param <T>  type\n     * @return object\n     * @throws ReflectException\n     */\n    public <T> T get(String name) throws ReflectException {\n        return field(name).get();\n    }\n\n    /**\n     * 取得指定名称的字段\n     *\n     * @param name name\n     * @return reflect\n     * @throws ReflectException\n     */\n    public Reflect field(String name) throws ReflectException {\n        try {\n            Field field = field0(name);\n            return on(field.get(object));\n        } catch (Exception e) {\n            throw new ReflectException(object.getClass().getName(), e);\n        }\n    }\n\n    private Field field0(String name) throws ReflectException {\n        Class<?> type = type();\n\n        // 先尝试取得公有字段\n        try {\n            return type.getField(name);\n        }\n\n        // 此时尝试非公有字段\n        catch (NoSuchFieldException e) {\n            do {\n                try {\n                    return accessible(type.getDeclaredField(name));\n                } catch (NoSuchFieldException ignore) {\n                }\n\n                type = type.getSuperclass();\n            } while (type != null);\n\n            throw new ReflectException(e);\n        }\n    }\n\n    /**\n     * 取得一个Map,map中的key为字段名,value为字段对应的反射工具类\n     *\n     * @return Map\n     */\n    public Map<String, Reflect> fields() {\n        Map<String, Reflect> result = new LinkedHashMap<String, Reflect>();\n        Class<?> type = type();\n\n        do {\n            for (Field field : type.getDeclaredFields()) {\n                if (!isClass ^ Modifier.isStatic(field.getModifiers())) {\n                    String name = field.getName();\n\n                    if (!result.containsKey(name))\n                        result.put(name, field(name));\n                }\n            }\n\n            type = type.getSuperclass();\n        } while (type != null);\n\n        return result;\n    }\n\n    /**\n     * 调用指定的无参数方法\n     *\n     * @param name\n     * @return\n     * @throws ReflectException\n     */\n    public Reflect call(String name) throws ReflectException {\n        return call(name, new Object[0]);\n    }\n\n    /**\n     * 调用方法根据传入的参数\n     *\n     * @param name\n     * @param args\n     * @return\n     * @throws ReflectException\n     */\n    public Reflect call(String name, Object... args) throws ReflectException {\n        Class<?>[] types = types(args);\n\n        try {\n            Method method = exactMethod(name, types);\n            return on(method, object, args);\n        } catch (NoSuchMethodException e) {\n            try {\n                Method method = similarMethod(name, types);\n                return on(method, object, args);\n            } catch (NoSuchMethodException e1) {\n                throw new ReflectException(e1);\n            }\n        }\n    }\n\n    public Method exactMethod(String name, Class<?>[] types) throws NoSuchMethodException {\n        Class<?> type = type();\n\n        try {\n            return type.getMethod(name, types);\n        } catch (NoSuchMethodException e) {\n            do {\n                try {\n                    return type.getDeclaredMethod(name, types);\n                } catch (NoSuchMethodException ignore) {\n                }\n\n                type = type.getSuperclass();\n            } while (type != null);\n\n            throw new NoSuchMethodException();\n        }\n    }\n\n    /**\n     * 根据参数和名称匹配方法,如果找不到方法,\n     */\n    private Method similarMethod(String name, Class<?>[] types) throws NoSuchMethodException {\n        Class<?> type = type();\n\n        for (Method method : type.getMethods()) {\n            if (isSimilarSignature(method, name, types)) {\n                return method;\n            }\n        }\n\n        do {\n            for (Method method : type.getDeclaredMethods()) {\n                if (isSimilarSignature(method, name, types)) {\n                    return method;\n                }\n            }\n\n            type = type.getSuperclass();\n        } while (type != null);\n\n        throw new NoSuchMethodException(\"No similar method \" + name + \" with params \" + Arrays.toString(types)\n                + \" could be found on type \" + type() + \".\");\n    }\n\n    private boolean isSimilarSignature(Method possiblyMatchingMethod, String desiredMethodName,\n                                       Class<?>[] desiredParamTypes) {\n        return possiblyMatchingMethod.getName().equals(desiredMethodName)\n                && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes);\n    }\n\n    /**\n     * 创建一个实例通过默认构造器\n     *\n     * @return Reflect\n     * @throws ReflectException\n     */\n    public Reflect create() throws ReflectException {\n        return create(new Object[0]);\n    }\n\n    /**\n     * 创建一个实例根据传入的参数\n     *\n     * @param args 参数\n     * @return Reflect\n     * @throws ReflectException\n     */\n    public Reflect create(Object... args) throws ReflectException {\n        Class<?>[] types = types(args);\n\n        try {\n            Constructor<?> constructor = type().getDeclaredConstructor(types);\n            return on(constructor, args);\n        } catch (NoSuchMethodException e) {\n            for (Constructor<?> constructor : type().getDeclaredConstructors()) {\n                if (match(constructor.getParameterTypes(), types)) {\n                    return on(constructor, args);\n                }\n            }\n\n            throw new ReflectException(e);\n        }\n    }\n\n    /**\n     * 创建一个动态代理根据传入的类型. 如果我们正在维护的是一个Map,那么当调用出现异常时我们将从Map中取值.\n     *\n     * @param proxyType 需要动态代理的类型\n     * @return 动态代理生成的对象\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <P> P as(Class<P> proxyType) {\n        final boolean isMap = (object instanceof Map);\n        final InvocationHandler handler = new InvocationHandler() {\n            \n            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                String name = method.getName();\n                try {\n                    return on(object).call(name, args).get();\n                } catch (ReflectException e) {\n                    if (isMap) {\n                        Map<String, Object> map = (Map<String, Object>) object;\n                        int length = (args == null ? 0 : args.length);\n\n                        if (length == 0 && name.startsWith(\"get\")) {\n                            return map.get(property(name.substring(3)));\n                        } else if (length == 0 && name.startsWith(\"is\")) {\n                            return map.get(property(name.substring(2)));\n                        } else if (length == 1 && name.startsWith(\"set\")) {\n                            map.put(property(name.substring(3)), args[0]);\n                            return null;\n                        }\n                    }\n\n                    throw e;\n                }\n            }\n        };\n\n        return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[]{proxyType}, handler);\n    }\n\n    /**\n     * 检查两个数组的类型是否匹配,如果数组中包含原始类型,将它们转换为对应的包装类型.\n     */\n    private boolean match(Class<?>[] declaredTypes, Class<?>[] actualTypes) {\n        if (declaredTypes.length == actualTypes.length) {\n            for (int i = 0; i < actualTypes.length; i++) {\n                if (actualTypes[i] == NULL.class)\n                    continue;\n\n                if (wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i])))\n                    continue;\n\n                return false;\n            }\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public int hashCode() {\n        return object.hashCode();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public boolean equals(Object obj) {\n        return obj instanceof Reflect && object.equals(((Reflect) obj).get());\n\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public String toString() {\n        return object.toString();\n    }\n\n    /**\n     * 取得我们正在反射的对象的类型.\n     *\n     * @see Object#getClass()\n     */\n    public Class<?> type() {\n        if (isClass) {\n            return (Class<?>) object;\n        } else {\n            return object.getClass();\n        }\n    }\n\n    public static String getMethodDetails(Method method) {\n        StringBuilder sb = new StringBuilder(40);\n        sb.append(Modifier.toString(method.getModifiers()))\n                .append(\" \")\n                .append(method.getReturnType().getName())\n                .append(\" \")\n                .append(method.getName())\n                .append(\"(\");\n        Class<?>[] parameters = method.getParameterTypes();\n        for (Class<?> parameter : parameters) {\n            sb.append(parameter.getName()).append(\", \");\n        }\n        if (parameters.length > 0) {\n            sb.delete(sb.length() - 2, sb.length());\n        }\n        sb.append(\")\");\n        return sb.toString();\n    }\n\n\n    /**\n     * 用来表示null的类.\n     *\n     * @author Lody\n     */\n    private static class NULL {\n    }\n\n    /**\n     * 智能调用 但是只调用类本身声明方法 按照优先级 匹配\n     * <p>\n     * 1.完全匹配\n     * 2.形参 Object...\n     * 3.名字相同 无参数\n     *\n     * @param name\n     * @param args\n     * @return\n     * @throws ReflectException\n     */\n    public Reflect callBest(String name, Object... args) throws ReflectException {\n        Class<?>[] types = types(args);\n        Class<?> type = type();\n\n        Method bestMethod = null;\n        int level = 0;\n        for (Method method : type.getDeclaredMethods()) {\n            if (isSimilarSignature(method, name, types)) {\n                bestMethod = method;\n                level = 2;\n                break;\n            }\n            if (matchObjectMethod(method, name, types)) {\n                bestMethod = method;\n                level = 1;\n                continue;\n            }\n            if (method.getName().equals(name) && method.getParameterTypes().length == 0 && level == 0) {\n                bestMethod = method;\n            }\n        }\n        if (bestMethod != null) {\n            if (level == 0) {\n                args = new Object[0];\n            }\n            if (level == 1) {\n                Object[] args2 = {args};\n                args = args2;\n            }\n            return on(bestMethod, object, args);\n        } else {\n            throw new ReflectException(\"no method found for \" + name, new NoSuchMethodException(\"No best method \" + name + \" with params \" + Arrays.toString(types)\n                    + \" could be found on type \" + type() + \".\"));\n        }\n    }\n\n    private boolean matchObjectMethod(Method possiblyMatchingMethod, String desiredMethodName,\n                                      Class<?>[] desiredParamTypes) {\n        return possiblyMatchingMethod.getName().equals(desiredMethodName)\n                && matchObject(possiblyMatchingMethod.getParameterTypes());\n    }\n\n    private boolean matchObject(Class<?>[] parameterTypes) {\n        Class<Object[]> c = Object[].class;\n        return parameterTypes.length > 0 && parameterTypes[0].isAssignableFrom(c);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/ReflectException.java",
    "content": "package com.lody.virtual.helper.utils;\n\n/**\n * @author Lody\n */\npublic class ReflectException extends RuntimeException {\n\n\tpublic ReflectException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic ReflectException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/Singleton.java",
    "content": "package com.lody.virtual.helper.utils;\n\n/**\n * Singleton helper class for lazily initialization.\n *\n * Modeled after frameworks/base/include/utils/Singleton.h\n *\n * @hide\n */\npublic abstract class Singleton<T> {\n    private T mInstance;\n\n    protected abstract T create();\n\n    public final T get() {\n        synchronized (this) {\n            if (mInstance == null) {\n                mInstance = create();\n            }\n            return mInstance;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/VLog.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport java.util.Set;\n\n/**\n * @author Lody\n *\n */\npublic class VLog {\n\n\tpublic static boolean OPEN_LOG = true;\n\n\tpublic static void i(String tag, String msg, Object... format) {\n\t\tif (OPEN_LOG) {\n\t\t\tLog.i(tag, String.format(msg, format));\n\t\t}\n\t}\n\n\tpublic static void d(String tag, String msg, Object... format) {\n\t\tif (OPEN_LOG) {\n\t\t\tLog.d(tag, String.format(msg, format));\n\t\t}\n\t}\n\n\tpublic static void w(String tag, String msg, Object... format) {\n\t\tif (OPEN_LOG) {\n\t\t\tLog.w(tag, String.format(msg, format));\n\t\t}\n\t}\n\n\tpublic static void e(String tag, String msg, Object... format) {\n\t\tif (OPEN_LOG) {\n\t\t\tLog.e(tag, String.format(msg, format));\n\t\t}\n\t}\n\n\tpublic static void v(String tag, String msg, Object... format) {\n\t\tif (OPEN_LOG) {\n\t\t\tLog.v(tag, String.format(msg, format));\n\t\t}\n\t}\n\n\tpublic static String toString(Bundle bundle){\n\t\tif(bundle==null)return null;\n\t\tif(Reflect.on(bundle).get(\"mParcelledData\")!=null){\n\t\t\tSet<String> keys=bundle.keySet();\n\t\t\tStringBuilder stringBuilder=new StringBuilder(\"Bundle[\");\n\t\t\tif(keys!=null) {\n\t\t\t\tfor (String key : keys) {\n\t\t\t\t\tstringBuilder.append(key);\n\t\t\t\t\tstringBuilder.append(\"=\");\n\t\t\t\t\tstringBuilder.append(bundle.get(key));\n\t\t\t\t\tstringBuilder.append(\",\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tstringBuilder.append(\"]\");\n\t\t\treturn stringBuilder.toString();\n\t\t}\n\t\treturn bundle.toString();\n\t}\n\n\tpublic static String getStackTraceString(Throwable tr) {\n\t\treturn Log.getStackTraceString(tr);\n\t}\n\n\tpublic static void printStackTrace(String tag) {\n\t\tLog.e(tag, getStackTraceString(new Exception()));\n\t}\n\n\tpublic static void e(String tag, Throwable e) {\n\t\tLog.e(tag, getStackTraceString(e));\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/helper/utils/XmlSerializerAndParser.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport org.xmlpull.v1.XmlPullParser;\nimport org.xmlpull.v1.XmlPullParserException;\nimport org.xmlpull.v1.XmlSerializer;\n\nimport java.io.IOException;\n\npublic interface XmlSerializerAndParser<T> {\n    void writeAsXml(T item, XmlSerializer out) throws IOException;\n    T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException;\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/os/VBinder.java",
    "content": "package com.lody.virtual.os;\n\nimport android.os.Binder;\n\nimport com.lody.virtual.client.ipc.VActivityManager;\n\n/**\n * @author Lody\n */\n\npublic class VBinder {\n\n    public static int getCallingUid() {\n        return VActivityManager.get().getUidByPid(Binder.getCallingPid());\n    }\n\n    public static int getBaseCallingUid() {\n        return VUserHandle.getAppId(getCallingUid());\n    }\n\n    public static int getCallingPid() {\n        return Binder.getCallingPid();\n    }\n\n    public static VUserHandle getCallingUserHandle() {\n        return new VUserHandle(VUserHandle.getUserId(getCallingUid()));\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/os/VEnvironment.java",
    "content": "package com.lody.virtual.os;\n\nimport android.content.Context;\nimport android.os.Build;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.utils.FileUtils;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.io.File;\n\n/**\n * @author Lody\n */\n\npublic class VEnvironment {\n\n    private static final String TAG = VEnvironment.class.getSimpleName();\n\n    private static final File ROOT;\n    private static final File DATA_DIRECTORY;\n    private static final File USER_DIRECTORY;\n    private static final File DALVIK_CACHE_DIRECTORY;\n\n    static {\n        File host = new File(getContext().getApplicationInfo().dataDir);\n        // Point to: /\n        ROOT = ensureCreated(new File(host, \"virtual\"));\n        // Point to: /data/\n        DATA_DIRECTORY = ensureCreated(new File(ROOT, \"data\"));\n        // Point to: /data/user/\n        USER_DIRECTORY = ensureCreated(new File(DATA_DIRECTORY, \"user\"));\n        // Point to: /opt/\n        DALVIK_CACHE_DIRECTORY = ensureCreated(new File(ROOT, \"opt\"));\n    }\n\n    public static void systemReady(){\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            try {\n                FileUtils.chmod(ROOT.getAbsolutePath(), FileUtils.FileMode.MODE_755);\n                FileUtils.chmod(DATA_DIRECTORY.getAbsolutePath(), FileUtils.FileMode.MODE_755);\n                FileUtils.chmod(getDataAppDirectory().getAbsolutePath(), FileUtils.FileMode.MODE_755);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n\n    private static Context getContext() {\n        return VirtualCore.get().getContext();\n    }\n\n    private static File ensureCreated(File folder) {\n        if (!folder.exists() && !folder.mkdirs()) {\n            VLog.w(TAG, \"Unable to create the directory: %s.\", folder.getPath());\n        }\n        return folder;\n    }\n\n    public static File getDataUserPackageDirectory(int userId,\n                                                   String packageName) {\n        return ensureCreated(new File(getUserSystemDirectory(userId), packageName));\n    }\n\n    public static File getPackageResourcePath(String packgeName) {\n        return new File(getDataAppPackageDirectory(packgeName), \"base.apk\");\n    }\n\n    public static File getDataAppDirectory() {\n        return ensureCreated(new File(getDataDirectory(), \"app\"));\n    }\n\n    public static File getUidListFile() {\n        return new File(getSystemSecureDirectory(), \"uid-list.ini\");\n    }\n\n    public static File getBakUidListFile() {\n        return new File(getSystemSecureDirectory(), \"uid-list.ini.bak\");\n    }\n\n    public static File getAccountConfigFile() {\n        return new File(getSystemSecureDirectory(), \"account-list.ini\");\n    }\n\n    public static File getDeviceInfoFile() {\n        return new File(getSystemSecureDirectory(), \"device-info.ini\");\n    }\n\n    public static File getPackageListFile() {\n        return new File(getSystemSecureDirectory(), \"packages.ini\");\n    }\n\n    /**\n     *\n     * @return Virtual storage config file\n     */\n    public static File getVSConfigFile() {\n        return new File(getSystemSecureDirectory(), \"vss.ini\");\n    }\n\n    public static File getBakPackageListFile() {\n        return new File(getSystemSecureDirectory(), \"packages.ini.bak\");\n    }\n\n\n    public static File getJobConfigFile() {\n        return new File(getSystemSecureDirectory(), \"job-list.ini\");\n    }\n\n    public static File getDalvikCacheDirectory() {\n        return DALVIK_CACHE_DIRECTORY;\n    }\n\n    public static File getOdexFile(String packageName) {\n        return new File(DALVIK_CACHE_DIRECTORY, \"data@app@\" + packageName + \"-1@base.apk@classes.dex\");\n    }\n\n    public static File getDataAppPackageDirectory(String packageName) {\n        return ensureCreated(new File(getDataAppDirectory(), packageName));\n    }\n\n    public static File getPackageCacheFile(String packageName) {\n        return new File(getDataAppPackageDirectory(packageName), \"package.ini\");\n    }\n\n    public static File getSignatureFile(String packageName) {\n        return new File(getDataAppPackageDirectory(packageName), \"signature.ini\");\n    }\n\n    public static File getUserSystemDirectory() {\n        return USER_DIRECTORY;\n    }\n\n    public static File getUserSystemDirectory(int userId) {\n        return new File(USER_DIRECTORY, String.valueOf(userId));\n    }\n\n    public static File getWifiMacFile(int userId) {\n        return new File(getUserSystemDirectory(userId), \"wifiMacAddress\");\n    }\n\n    public static File getDataDirectory() {\n        return DATA_DIRECTORY;\n    }\n\n    public static File getSystemSecureDirectory() {\n        return ensureCreated(new File(getDataAppDirectory(), \"system\"));\n    }\n\n    public static File getPackageInstallerStageDir() {\n        return ensureCreated(new File(DATA_DIRECTORY, \".session_dir\"));\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/os/VUserHandle.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.os;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.Process;\nimport android.util.SparseArray;\n\nimport com.lody.virtual.client.VClientImpl;\n\nimport java.io.PrintWriter;\n\n/**\n * Representation of a user on the device.\n */\npublic final class VUserHandle implements Parcelable {\n    /**\n     * @hide Range of uids allocated for a user.\n     */\n    public static final int PER_USER_RANGE = 100000;\n\n    /** @hide A user id to indicate all users on the device */\n    public static final int USER_ALL = -1;\n\n    /** @hide A user handle to indicate all users on the device */\n    public static final VUserHandle ALL = new VUserHandle(USER_ALL);\n\n    /** @hide A user id to indicate the currently active user */\n    public static final int USER_CURRENT = -2;\n\n    /** @hide A user handle to indicate the current user of the device */\n    public static final VUserHandle CURRENT = new VUserHandle(USER_CURRENT);\n\n    /** @hide A user id to indicate that we would like to send to the current\n     *  user, but if this is calling from a user process then we will send it\n     *  to the caller's user instead of failing with a security exception */\n    public static final int USER_CURRENT_OR_SELF = -3;\n\n    /** @hide A user handle to indicate that we would like to send to the current\n     *  user, but if this is calling from a user process then we will send it\n     *  to the caller's user instead of failing with a security exception */\n    public static final VUserHandle CURRENT_OR_SELF = new VUserHandle(USER_CURRENT_OR_SELF);\n\n    /** @hide An undefined user id */\n    public static final int USER_NULL = -10000;\n\n    /** @hide A user id constant to indicate the \"owner\" user of the device */\n    public static final int USER_OWNER = 0;\n\n    /** @hide A user handle to indicate the primary/owner user of the device */\n    public static final VUserHandle OWNER = new VUserHandle(USER_OWNER);\n\n    /**\n     * @hide Enable multi-user related side effects. Set this to false if\n     * there are problems with single user use-cases.\n     */\n    public static final boolean MU_ENABLED = true;\n    /**\n     * First gid for applications to share resources. Used when forward-locking\n     * is enabled but all UserHandles need to be able to read the resources.\n     * @hide\n     */\n    public static final int FIRST_SHARED_APPLICATION_GID = 50000;\n    /**\n     * Last gid for applications to share resources. Used when forward-locking\n     * is enabled but all UserHandles need to be able to read the resources.\n     * @hide\n     */\n    public static final int LAST_SHARED_APPLICATION_GID = 59999;\n    /**\n     * First vuid used for fully isolated sandboxed processes (with no permissions of their own)\n     * @hide\n     */\n    public static final int FIRST_ISOLATED_UID = 99000;\n    /**\n     * Last vuid used for fully isolated sandboxed processes (with no permissions of their own)\n     * @hide\n     */\n    public static final int LAST_ISOLATED_UID = 99999;\n    public static final Parcelable.Creator<VUserHandle> CREATOR\n            = new Parcelable.Creator<VUserHandle>() {\n        public VUserHandle createFromParcel(Parcel in) {\n            return new VUserHandle(in);\n        }\n\n        public VUserHandle[] newArray(int size) {\n            return new VUserHandle[size];\n        }\n    };\n    private static final SparseArray<VUserHandle> userHandles = new SparseArray<VUserHandle>();\n    final int mHandle;\n\n    /** @hide */\n    public VUserHandle(int h) {\n        mHandle = h;\n    }\n\n\n    /**\n     * Instantiate a new VUserHandle from the data in a Parcel that was\n     * previously written with {@link #writeToParcel(Parcel, int)}.  Note that you\n     * must not use this with data written by\n     * {@link #writeToParcel(VUserHandle, Parcel)} since it is not possible\n     * to handle a null VUserHandle here.\n     *\n     * @param in The Parcel containing the previously written VUserHandle,\n     * positioned at the location in the buffer where it was written.\n     */\n    public VUserHandle(Parcel in) {\n        mHandle = in.readInt();\n    }\n\n    /**\n     * Checks to see if the user id is the same for the two uids, i.e., they belong to the same\n     * user.\n     * @hide\n     */\n    public static boolean isSameUser(int uid1, int uid2) {\n        return getUserId(uid1) == getUserId(uid2);\n    }\n\n    public static boolean accept(int userId) {\n        if (userId == USER_ALL || userId == myUserId()) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Checks to see if both uids are referring to the same app id, ignoring the user id part of the\n     * uids.\n     * @param uid1 vuid to compare\n     * @param uid2 other vuid to compare\n     * @return whether the appId is the same for both uids\n     * @hide\n     */\n    public static final boolean isSameApp(int uid1, int uid2) {\n        return getAppId(uid1) == getAppId(uid2);\n    }\n\n    /** @hide */\n    public static final boolean isIsolated(int uid) {\n        if (uid > 0) {\n            final int appId = getAppId(uid);\n            return appId >= FIRST_ISOLATED_UID && appId <= LAST_ISOLATED_UID;\n        } else {\n            return false;\n        }\n    }\n\n    /** @hide */\n    public static boolean isApp(int uid) {\n        if (uid > 0) {\n            final int appId = getAppId(uid);\n            return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * Returns the user id for a given vuid.\n     * @hide\n     */\n    public static int getUserId(int uid) {\n        if (MU_ENABLED) {\n            return uid / PER_USER_RANGE;\n        } else {\n            return 0;\n        }\n    }\n\n    /** @hide */\n    public static int getCallingUserId() {\n        return getUserId(VBinder.getCallingUid());\n    }\n\n    /**\n     * Generate a text representation of the vuid, breaking out its individual\n     * components -- user, app, isolated, etc.\n     * @hide\n     */\n\n    /** @hide */\n    public static VUserHandle getCallingUserHandle() {\n        int userId = getUserId(VBinder.getCallingUid());\n        VUserHandle userHandle = userHandles.get(userId);\n        // Intentionally not synchronized to save time\n        if (userHandle == null) {\n            userHandle = new VUserHandle(userId);\n            userHandles.put(userId, userHandle);\n        }\n        return userHandle;\n    }\n\n    /**\n     * Returns the vuid that is composed from the userId and the appId.\n     * @hide\n     */\n    public static int getUid(int userId, int appId) {\n        if (MU_ENABLED) {\n            return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);\n        } else {\n            return appId;\n        }\n    }\n\n    /**\n     * Returns the app id (or base vuid) for a given vuid, stripping out the user id from it.\n     * @hide\n     */\n    public static int getAppId(int uid) {\n        return uid % PER_USER_RANGE;\n    }\n\n    public static int myAppId() {\n        return getAppId(VClientImpl.get().getVUid());\n    }\n\n    /**\n     * Returns the app id for a given shared app gid.\n     * @hide\n     */\n    public static int getAppIdFromSharedAppGid(int gid) {\n        final int noUserGid = getAppId(gid);\n        if (noUserGid < FIRST_SHARED_APPLICATION_GID ||\n                noUserGid > LAST_SHARED_APPLICATION_GID) {\n            throw new IllegalArgumentException(Integer.toString(gid) + \" is not a shared app gid\");\n        }\n        return (noUserGid + Process.FIRST_APPLICATION_UID) - FIRST_SHARED_APPLICATION_GID;\n    }\n\n    public static void formatUid(StringBuilder sb, int uid) {\n        if (uid < Process.FIRST_APPLICATION_UID) {\n            sb.append(uid);\n        } else {\n            sb.append('u');\n            sb.append(getUserId(uid));\n            final int appId = getAppId(uid);\n            if (appId >= FIRST_ISOLATED_UID && appId <= LAST_ISOLATED_UID) {\n                sb.append('i');\n                sb.append(appId - FIRST_ISOLATED_UID);\n            } else if (appId >= Process.FIRST_APPLICATION_UID) {\n                sb.append('a');\n                sb.append(appId - Process.FIRST_APPLICATION_UID);\n            } else {\n                sb.append('s');\n                sb.append(appId);\n            }\n        }\n    }\n\n    /**\n     * Generate a text representation of the vuid, breaking out its individual\n     * components -- user, app, isolated, etc.\n     * @hide\n     */\n    public static String formatUid(int uid) {\n        StringBuilder sb = new StringBuilder();\n        formatUid(sb, uid);\n        return sb.toString();\n    }\n\n    /**\n     * Generate a text representation of the vuid, breaking out its individual\n     * components -- user, app, isolated, etc.\n     * @hide\n     */\n    public static void formatUid(PrintWriter pw, int uid) {\n        if (uid < Process.FIRST_APPLICATION_UID) {\n            pw.print(uid);\n        } else {\n            pw.print('u');\n            pw.print(getUserId(uid));\n            final int appId = getAppId(uid);\n            if (appId >= FIRST_ISOLATED_UID && appId <= LAST_ISOLATED_UID) {\n                pw.print('i');\n                pw.print(appId - FIRST_ISOLATED_UID);\n            } else if (appId >= Process.FIRST_APPLICATION_UID) {\n                pw.print('a');\n                pw.print(appId - Process.FIRST_APPLICATION_UID);\n            } else {\n                pw.print('s');\n                pw.print(appId);\n            }\n        }\n    }\n\n    /**\n     * Returns the user id of the current process\n     * @return user id of the current process\n     * @hide\n     */\n    public static int myUserId() {\n        return getUserId(VClientImpl.get().getVUid());\n    }\n\n    /**\n     * Write a VUserHandle to a Parcel, handling null pointers.  Must be\n     * read with {@link #readFromParcel(Parcel)}.\n     *\n     * @param h The VUserHandle to be written.\n     * @param out The Parcel in which the VUserHandle will be placed.\n     *\n     * @see #readFromParcel(Parcel)\n     */\n    public static void writeToParcel(VUserHandle h, Parcel out) {\n        if (h != null) {\n            h.writeToParcel(out, 0);\n        } else {\n            out.writeInt(USER_NULL);\n        }\n    }\n\n    /**\n     * Read a VUserHandle from a Parcel that was previously written\n     * with {@link #writeToParcel(VUserHandle, Parcel)}, returning either\n     * a null or new object as appropriate.\n     *\n     * @param in The Parcel from which to read the VUserHandle\n     * @return Returns a new VUserHandle matching the previously written\n     * object, or null if a null had been written.\n     *\n     * @see #writeToParcel(VUserHandle, Parcel)\n     */\n    public static VUserHandle readFromParcel(Parcel in) {\n        int h = in.readInt();\n        return h != USER_NULL ? new VUserHandle(h) : null;\n    }\n\n    public static VUserHandle myUserHandle() {\n        return new VUserHandle(myUserId());\n    }\n    \n    /**\n     * Returns true if this VUserHandle refers to the owner user; false otherwise.\n     * @return true if this VUserHandle refers to the owner user; false otherwise.\n     * @hide\n     */\n    public final boolean isOwner() {\n        return this.equals(OWNER);\n    }\n\n    /**\n     * Returns the userId stored in this VUserHandle.\n     * @hide\n     */\n    public int getIdentifier() {\n        return mHandle;\n    }\n\n    @Override\n    public String toString() {\n        return \"VUserHandle{\" + mHandle + \"}\";\n    }\n    \n    @Override\n    public boolean equals(Object obj) {\n        try {\n            if (obj != null) {\n                VUserHandle other = (VUserHandle)obj;\n                return mHandle == other.mHandle;\n            }\n        } catch (ClassCastException e) {\n        }\n        return false;\n    }\n    \n    @Override\n    public int hashCode() {\n        return mHandle;\n    }\n\n    public int describeContents() {\n        return 0;\n    }\n\n    public void writeToParcel(Parcel out, int flags) {\n        out.writeInt(mHandle);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/os/VUserInfo.java",
    "content": "package com.lody.virtual.os;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * Per-user information.\n */\npublic class VUserInfo implements Parcelable {\n\n    /** 8 bits for user type */\n    public static final int FLAG_MASK_USER_TYPE = 0x000000FF;\n\n    /**\n     * *************************** NOTE ***************************\n     * These flag values CAN NOT CHANGE because they are written\n     * directly to storage.\n     */\n\n    /**\n     * Primary user. Only one user can have this flag set. Meaning of this\n     * flag TBD.\n     */\n    public static final int FLAG_PRIMARY = 0x00000001;\n\n    /**\n     * User with administrative privileges. Such a user can create and\n     * delete users.\n     */\n    public static final int FLAG_ADMIN   = 0x00000002;\n\n    /**\n     * Indicates a guest user that may be transient.\n     */\n    public static final int FLAG_GUEST   = 0x00000004;\n\n    /**\n     * Indicates the user has restrictions in privileges, in addition to those for normal users.\n     * Exact meaning TBD. For instance, maybe they can't install apps or administer WiFi access pts.\n     */\n    public static final int FLAG_RESTRICTED = 0x00000008;\n\n    /**\n     * Indicates that this user has gone through its first-time initialization.\n     */\n    public static final int FLAG_INITIALIZED = 0x00000010;\n\n    /**\n     * Indicates that this user is a profile of another user, for example holding a users\n     * corporate data.\n     */\n    public static final int FLAG_MANAGED_PROFILE = 0x00000020;\n\n    /**\n     * Indicates that this user is disabled.\n     */\n    public static final int FLAG_DISABLED = 0x00000040;\n\n\n    public static final int NO_PROFILE_GROUP_ID = -1;\n\n    public int id;\n    public int serialNumber;\n    public String name;\n    public String iconPath;\n    public int flags;\n    public long creationTime;\n    public long lastLoggedInTime;\n    public int profileGroupId;\n\n    /** User is only partially created. */\n    public boolean partial;\n    public boolean guestToRemove;\n\n    public VUserInfo(int id, String name, int flags) {\n        this(id, name, null, flags);\n    }\n\n    public VUserInfo(int id, String name, String iconPath, int flags) {\n        this.id = id;\n        this.name = name;\n        this.flags = flags;\n        this.iconPath = iconPath;\n        this.profileGroupId = NO_PROFILE_GROUP_ID;\n    }\n\n    public boolean isPrimary() {\n        return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;\n    }\n\n    public boolean isAdmin() {\n        return (flags & FLAG_ADMIN) == FLAG_ADMIN;\n    }\n\n    public boolean isGuest() {\n        return (flags & FLAG_GUEST) == FLAG_GUEST;\n    }\n\n    public boolean isRestricted() {\n        return (flags & FLAG_RESTRICTED) == FLAG_RESTRICTED;\n    }\n\n    public boolean isManagedProfile() {\n        return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;\n    }\n\n    public boolean isEnabled() {\n        return (flags & FLAG_DISABLED) != FLAG_DISABLED;\n    }\n\n    public VUserInfo() {\n    }\n\n    public VUserInfo(VUserInfo orig) {\n        name = orig.name;\n        iconPath = orig.iconPath;\n        id = orig.id;\n        flags = orig.flags;\n        serialNumber = orig.serialNumber;\n        creationTime = orig.creationTime;\n        lastLoggedInTime = orig.lastLoggedInTime;\n        partial = orig.partial;\n        profileGroupId = orig.profileGroupId;\n        guestToRemove = orig.guestToRemove;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserInfo{\" + id + \":\" + name + \":\" + Integer.toHexString(flags) + \"}\";\n    }\n\n    public int describeContents() {\n        return 0;\n    }\n\n    public void writeToParcel(Parcel dest, int parcelableFlags) {\n        dest.writeInt(id);\n        dest.writeString(name);\n        dest.writeString(iconPath);\n        dest.writeInt(flags);\n        dest.writeInt(serialNumber);\n        dest.writeLong(creationTime);\n        dest.writeLong(lastLoggedInTime);\n        dest.writeInt(partial ? 1 : 0);\n        dest.writeInt(profileGroupId);\n        dest.writeInt(guestToRemove ? 1 : 0);\n    }\n\n    public static final Parcelable.Creator<VUserInfo> CREATOR\n            = new Parcelable.Creator<VUserInfo>() {\n        public VUserInfo createFromParcel(Parcel source) {\n            return new VUserInfo(source);\n        }\n        public VUserInfo[] newArray(int size) {\n            return new VUserInfo[size];\n        }\n    };\n\n    private VUserInfo(Parcel source) {\n        id = source.readInt();\n        name = source.readString();\n        iconPath = source.readString();\n        flags = source.readInt();\n        serialNumber = source.readInt();\n        creationTime = source.readLong();\n        lastLoggedInTime = source.readLong();\n        partial = source.readInt() != 0;\n        profileGroupId = source.readInt();\n        guestToRemove = source.readInt() != 0;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/os/VUserManager.java",
    "content": "package com.lody.virtual.os;\n\nimport android.graphics.Bitmap;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport com.lody.virtual.client.ipc.ServiceManagerNative;\nimport com.lody.virtual.server.IUserManager;\n\nimport java.util.List;\n\nimport static com.lody.virtual.client.ipc.ServiceManagerNative.USER;\n\n/**\n * Manages users and user details on a multi-user system.\n */\npublic class VUserManager {\n\n    private static String TAG = \"VUserManager\";\n    private final IUserManager mService;\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from adding and removing\n     * accounts.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_MODIFY_ACCOUNTS = \"no_modify_accounts\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from changing Wi-Fi\n     * access points.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_CONFIG_WIFI = \"no_config_wifi\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from installing applications.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_INSTALL_APPS = \"no_install_apps\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from uninstalling applications.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_UNINSTALL_APPS = \"no_uninstall_apps\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from toggling location sharing.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n\n    public static final String DISALLOW_SHARE_LOCATION = \"no_share_location\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from enabling the\n     * \"Unknown Sources\" setting, that allows installation of apps from unknown sources.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = \"no_install_unknown_sources\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from configuring bluetooth.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_CONFIG_BLUETOOTH = \"no_config_bluetooth\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from transferring files over\n     * USB. The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_USB_FILE_TRANSFER = \"no_usb_file_transfer\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from configuring user\n     * credentials. The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_CONFIG_CREDENTIALS = \"no_config_credentials\";\n\n    /**\n     * Key for user restrictions. Specifies if a user is disallowed from removing users.\n     * The default value is <code>false</code>.\n     * <p/>\n     * Type: Boolean\n     */\n    public static final String DISALLOW_REMOVE_USER = \"no_remove_user\";\n\n    private static VUserManager sInstance = null;\n\n    /** @hide */\n    public synchronized static VUserManager get() {\n        if (sInstance == null) {\n            IUserManager remote = IUserManager.Stub.asInterface(ServiceManagerNative.getService(USER));\n            sInstance = new VUserManager(remote);\n        }\n        return sInstance;\n    }\n\n    /** @hide */\n    public VUserManager(IUserManager service) {\n        mService = service;\n    }\n\n    /**\n     * Returns whether the system supports multiple users.\n     * @return true if multiple users can be created, false if it is a single user device.\n     * @hide\n     */\n    public static boolean supportsMultipleUsers() {\n        return getMaxSupportedUsers() > 1;\n    }\n\n    /**\n     * Returns the user handle for the user that this application is running for.\n     * @return the user handle of the user making this call.\n     * @hide\n     */\n    public int getUserHandle() {\n        return VUserHandle.myUserId();\n    }\n\n    /**\n     * Returns the user name of the user making this call.  This call is only\n     * available to applications on the system image; it requires the\n     * MANAGE_USERS permission.\n     * @return the user name\n     */\n    public String getUserName() {\n        try {\n            return mService.getUserInfo(getUserHandle()).name;\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get user name\", re);\n            return \"\";\n        }\n    }\n\n   /**\n     * Used to determine whether the user making this call is subject to\n     * teleportations.\n     * @return whether the user making this call is a goat\n     */\n    public boolean isUserAGoat() {\n        return false;\n    }\n\n    /**\n     * Returns the UserInfo object describing a specific user.\n     * @param handle the user handle of the user whose information is being requested.\n     * @return the UserInfo object for a specific user.\n     * @hide\n     */\n    public VUserInfo getUserInfo(int handle) {\n        try {\n            return mService.getUserInfo(handle);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get user info\", re);\n            return null;\n        }\n    }\n\n    /**\n     * Return the serial number for a user.  This is a device-unique\n     * number assigned to that user; if the user is deleted and then a new\n     * user created, the new users will not be given the same serial number.\n     * @param user The user whose serial number is to be retrieved.\n     * @return The serial number of the given user; returns -1 if the\n     * given VUserHandle does not exist.\n     * @see #getUserForSerialNumber(long)\n     */\n    public long getSerialNumberForUser(VUserHandle user) {\n        return getUserSerialNumber(user.getIdentifier());\n    }\n\n    /**\n     * Return the user associated with a serial number previously\n     * returned by {@link #getSerialNumberForUser(VUserHandle)}.\n     * @param serialNumber The serial number of the user that is being\n     * retrieved.\n     * @return Return the user associated with the serial number, or null\n     * if there is not one.\n     * @see #getSerialNumberForUser(VUserHandle)\n     */\n    public VUserHandle getUserForSerialNumber(long serialNumber) {\n        int ident = getUserHandle((int)serialNumber);\n        return ident >= 0 ? new VUserHandle(ident) : null;\n    }\n\n    /**\n     * Creates a user with the specified name and options.\n     *\n     * @param name the user's name\n     * @param flags flags that identify the type of user and other properties.\n     * @see VUserInfo\n     *\n     * @return the UserInfo object for the created user, or null if the user could not be created.\n     * @hide\n     */\n    public VUserInfo createUser(String name, int flags) {\n        try {\n            return mService.createUser(name, flags);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not create a user\", re);\n            return null;\n        }\n    }\n\n    /**\n     * Return the number of users currently created on the device.\n     */\n    public int getUserCount() {\n        List<VUserInfo> users = getUsers();\n        return users != null ? users.size() : 1;\n    }\n\n    /**\n     * Returns information for all users on this device.\n     * @return the list of users that were created.\n     * @hide\n     */\n    public List<VUserInfo> getUsers() {\n        try {\n            return mService.getUsers(false);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get user list\", re);\n            return null;\n        }\n    }\n\n    /**\n     * Returns information for all users on this device.\n     * @param excludeDying specify if the list should exclude users being removed.\n     * @return the list of users that were created.\n     * @hide\n     */\n    public List<VUserInfo> getUsers(boolean excludeDying) {\n        try {\n            return mService.getUsers(excludeDying);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get user list\", re);\n            return null;\n        }\n    }\n\n    /**\n     * Removes a user and all associated data.\n     * @param handle the integer handle of the user, where 0 is the primary user.\n     * @hide\n     */\n    public boolean removeUser(int handle) {\n        try {\n            return mService.removeUser(handle);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not remove user \", re);\n            return false;\n        }\n    }\n\n    /**\n     * Updates the user's name.\n     *\n     * @param handle the user's integer handle\n     * @param name the new name for the user\n     * @hide\n     */\n    public void setUserName(int handle, String name) {\n        try {\n            mService.setUserName(handle, name);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not set the user name \", re);\n        }\n    }\n\n    /**\n     * Sets the user's photo.\n     * @param handle the user for whom to change the photo.\n     * @param icon the bitmap to set as the photo.\n     * @hide\n     */\n    public void setUserIcon(int handle, Bitmap icon) {\n        try {\n            mService.setUserIcon(handle, icon);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not set the user icon \", re);\n        }\n    }\n\n    /**\n     * Returns a file descriptor for the user's photo. PNG data can be read from this file.\n     * @param handle the user whose photo we want to read.\n     * @return a {@link Bitmap} of the user's photo, or null if there's no photo.\n     * @hide\n     */\n    public Bitmap getUserIcon(int handle) {\n        try {\n            return mService.getUserIcon(handle);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get the user icon \", re);\n            return null;\n        }\n    }\n\n    /**\n     * Enable or disable the use of a guest account. If disabled, the existing guest account\n     * will be wiped.\n     * @param enable whether to enable a guest account.\n     * @hide\n     */\n    public void setGuestEnabled(boolean enable) {\n        try {\n            mService.setGuestEnabled(enable);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not change guest account availability to \" + enable);\n        }\n    }\n\n    /**\n     * Checks if a guest user is enabled for this device.\n     * @return whether a guest user is enabled\n     * @hide\n     */\n    public boolean isGuestEnabled() {\n        try {\n            return mService.isGuestEnabled();\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not retrieve guest enabled state\");\n            return false;\n        }\n    }\n\n    /**\n     * Wipes all the data for a user, but doesn't remove the user.\n     * @param handle\n     * @hide\n     */\n    public void wipeUser(int handle) {\n        try {\n            mService.wipeUser(handle);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not wipe user \" + handle);\n        }\n    }\n\n    /**\n     * Returns the maximum number of users that can be created on this device. A return value\n     * of 1 means that it is a single user device.\n     * @hide\n     * @return a value greater than or equal to 1\n     */\n    public static int getMaxSupportedUsers() {\n        return Integer.MAX_VALUE;\n    }\n\n    /**\n     * Returns a serial number on this device for a given VUserHandle. User handles can be recycled\n     * when deleting and creating users, but serial numbers are not reused until the device is wiped.\n     * @param handle\n     * @return a serial number associated with that user, or -1 if the VUserHandle is not valid.\n     * @hide\n     */\n    public int getUserSerialNumber(int handle) {\n        try {\n            return mService.getUserSerialNumber(handle);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get serial number for user \" + handle);\n        }\n        return -1;\n    }\n\n    /**\n     * Returns a VUserHandle on this device for a given user serial number. User handles can be\n     * recycled when deleting and creating users, but serial numbers are not reused until the device\n     * is wiped.\n     * @param userSerialNumber\n     * @return the VUserHandle associated with that user serial number, or -1 if the serial number\n     * is not valid.\n     * @hide\n     */\n    public int getUserHandle(int userSerialNumber) {\n        try {\n            return mService.getUserHandle(userSerialNumber);\n        } catch (RemoteException re) {\n            Log.w(TAG, \"Could not get VUserHandle for user \" + userSerialNumber);\n        }\n        return -1;\n    }\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/AppTaskInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n */\n\npublic class AppTaskInfo implements Parcelable {\n\tpublic static final Parcelable.Creator<AppTaskInfo> CREATOR = new Parcelable.Creator<AppTaskInfo>() {\n\t\t@Override\n\t\tpublic AppTaskInfo createFromParcel(Parcel source) {\n\t\t\treturn new AppTaskInfo(source);\n\t\t}\n\n\t\t@Override\n\t\tpublic AppTaskInfo[] newArray(int size) {\n\t\t\treturn new AppTaskInfo[size];\n\t\t}\n\t};\n\tpublic int taskId;\n\tpublic Intent baseIntent;\n\tpublic ComponentName baseActivity;\n\tpublic ComponentName topActivity;\n\n\n\tpublic AppTaskInfo(int taskId, Intent baseIntent, ComponentName baseActivity, ComponentName topActivity) {\n\t\tthis.taskId = taskId;\n\t\tthis.baseIntent = baseIntent;\n\t\tthis.baseActivity = baseActivity;\n\t\tthis.topActivity = topActivity;\n\t}\n\n\tprotected AppTaskInfo(Parcel in) {\n\t\ttaskId = in.readInt();\n\t\tbaseIntent = in.readParcelable(Intent.class.getClassLoader());\n\t\tbaseActivity = in.readParcelable(ComponentName.class.getClassLoader());\n\t\ttopActivity = in.readParcelable(ComponentName.class.getClassLoader());\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeInt(taskId);\n\t\tdest.writeParcelable(baseIntent, flags);\n\t\tdest.writeParcelable(baseActivity, flags);\n\t\tdest.writeParcelable(topActivity, flags);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/InstallResult.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n *\n */\npublic class InstallResult implements Parcelable {\n\n\tpublic static final Creator<InstallResult> CREATOR = new Creator<InstallResult>() {\n\t\t@Override\n\t\tpublic InstallResult createFromParcel(Parcel in) {\n\t\t\treturn new InstallResult(in);\n\t\t}\n\n\t\t@Override\n\t\tpublic InstallResult[] newArray(int size) {\n\t\t\treturn new InstallResult[size];\n\t\t}\n\t};\n\tpublic boolean isSuccess;\n\tpublic boolean isUpdate;\n\tpublic String packageName;\n\tpublic String error;\n\n\tpublic InstallResult() {\n\t}\n\n\tprotected InstallResult(Parcel in) {\n\t\tthis.isSuccess = in.readByte() != 0;\n\t\tthis.isUpdate = in.readByte() != 0;\n\t\tthis.packageName = in.readString();\n\t\tthis.error = in.readString();\n\t}\n\n\tpublic static InstallResult makeFailure(String error) {\n\t\tInstallResult res = new InstallResult();\n\t\tres.error = error;\n\t\treturn res;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeByte((byte) (isSuccess ? 1 : 0));\n\t\tdest.writeByte((byte) (isUpdate ? 1 : 0));\n\t\tdest.writeString(packageName);\n\t\tdest.writeString(error);\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/InstalledAppInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.content.pm.ApplicationInfo;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.VPackageManager;\nimport com.lody.virtual.os.VEnvironment;\n\nimport java.io.File;\n\n/**\n * @author Lody\n */\npublic final class InstalledAppInfo implements Parcelable {\n\n    public String packageName;\n    public String apkPath;\n    public String libPath;\n    public boolean dependSystem;\n    public boolean skipDexOpt;\n    public int appId;\n\n    public InstalledAppInfo(String packageName, String apkPath, String libPath, boolean dependSystem, boolean skipDexOpt, int appId) {\n        this.packageName = packageName;\n        this.apkPath = apkPath;\n        this.libPath = libPath;\n        this.dependSystem = dependSystem;\n        this.skipDexOpt = skipDexOpt;\n        this.appId = appId;\n    }\n\n    public File getOdexFile() {\n        return VEnvironment.getOdexFile(packageName);\n    }\n\n    public ApplicationInfo getApplicationInfo(int userId) {\n        return VPackageManager.get().getApplicationInfo(packageName, 0, userId);\n    }\n\n    public int[] getInstalledUsers() {\n        return VirtualCore.get().getPackageInstalledUsers(packageName);\n    }\n\n    public boolean isLaunched(int userId) {\n        return VirtualCore.get().isPackageLaunched(userId, packageName);\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(this.packageName);\n        dest.writeString(this.apkPath);\n        dest.writeString(this.libPath);\n        dest.writeByte(this.dependSystem ? (byte) 1 : (byte) 0);\n        dest.writeByte(this.skipDexOpt ? (byte) 1 : (byte) 0);\n        dest.writeInt(this.appId);\n    }\n\n    protected InstalledAppInfo(Parcel in) {\n        this.packageName = in.readString();\n        this.apkPath = in.readString();\n        this.libPath = in.readString();\n        this.dependSystem = in.readByte() != 0;\n        this.skipDexOpt = in.readByte() != 0;\n        this.appId = in.readInt();\n    }\n\n    public static final Creator<InstalledAppInfo> CREATOR = new Creator<InstalledAppInfo>() {\n        @Override\n        public InstalledAppInfo createFromParcel(Parcel source) {\n            return new InstalledAppInfo(source);\n        }\n\n        @Override\n        public InstalledAppInfo[] newArray(int size) {\n            return new InstalledAppInfo[size];\n        }\n    };\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/PendingIntentData.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.app.PendingIntent;\nimport android.os.IBinder;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\npublic class PendingIntentData implements Parcelable {\n\n    public static final Creator<PendingIntentData> CREATOR = new Creator<PendingIntentData>() {\n        public final PendingIntentData createFromParcel(Parcel source) {\n            return new PendingIntentData(source);\n        }\n\n        public final PendingIntentData[] newArray(int size) {\n            return new PendingIntentData[size];\n        }\n    };\n    public String creator;\n    public PendingIntent pendingIntent;\n\n    protected PendingIntentData(Parcel source) {\n        this.creator = source.readString();\n        this.pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source);\n    }\n\n    public PendingIntentData(String creator, IBinder binder) {\n        this.creator = creator;\n        this.pendingIntent = readPendingIntent(binder);\n    }\n\n    public static PendingIntent readPendingIntent(IBinder binder) {\n        Parcel parcel = Parcel.obtain();\n        parcel.writeStrongBinder(binder);\n        parcel.setDataPosition(0);\n        try {\n            return PendingIntent.readPendingIntentOrNullFromParcel(parcel);\n        } finally {\n            parcel.recycle();\n        }\n    }\n\n    public int describeContents() {\n        return 0;\n    }\n\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(this.creator);\n        this.pendingIntent.writeToParcel(dest, flags);\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/PendingResultData.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.content.BroadcastReceiver;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n\n/**\n * @author Lody\n */\n\npublic class PendingResultData implements Parcelable {\n    public static final Creator<PendingResultData> CREATOR = new Creator<PendingResultData>() {\n        @Override\n        public PendingResultData createFromParcel(Parcel source) {\n            return new PendingResultData(source);\n        }\n\n        @Override\n        public PendingResultData[] newArray(int size) {\n            return new PendingResultData[size];\n        }\n    };\n    public int mType;\n    public boolean mOrderedHint;\n    public boolean mInitialStickyHint;\n    public IBinder mToken;\n    public int mSendingUser;\n    public int mFlags;\n    public int mResultCode;\n    public String mResultData;\n    public Bundle mResultExtras;\n    public boolean mAbortBroadcast;\n    public boolean mFinished;\n\n    public PendingResultData(BroadcastReceiver.PendingResult result) {\n        if (mirror.android.content.BroadcastReceiver.PendingResultMNC.ctor != null) {\n            mType = mirror.android.content.BroadcastReceiver.PendingResultMNC.mType.get(result);\n            mOrderedHint = mirror.android.content.BroadcastReceiver.PendingResultMNC.mOrderedHint.get(result);\n            mInitialStickyHint = mirror.android.content.BroadcastReceiver.PendingResultMNC.mInitialStickyHint.get(result);\n            mToken = mirror.android.content.BroadcastReceiver.PendingResultMNC.mToken.get(result);\n            mSendingUser = mirror.android.content.BroadcastReceiver.PendingResultMNC.mSendingUser.get(result);\n            mFlags = mirror.android.content.BroadcastReceiver.PendingResultMNC.mFlags.get(result);\n            mResultCode = mirror.android.content.BroadcastReceiver.PendingResultMNC.mResultCode.get(result);\n            mResultData = mirror.android.content.BroadcastReceiver.PendingResultMNC.mResultData.get(result);\n            mResultExtras = mirror.android.content.BroadcastReceiver.PendingResultMNC.mResultExtras.get(result);\n            mAbortBroadcast = mirror.android.content.BroadcastReceiver.PendingResultMNC.mAbortBroadcast.get(result);\n            mFinished = mirror.android.content.BroadcastReceiver.PendingResultMNC.mFinished.get(result);\n        } else if (mirror.android.content.BroadcastReceiver.PendingResultJBMR1.ctor != null) {\n            mType = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mType.get(result);\n            mOrderedHint = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mOrderedHint.get(result);\n            mInitialStickyHint = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mInitialStickyHint.get(result);\n            mToken = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mToken.get(result);\n            mSendingUser = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mSendingUser.get(result);\n            mResultCode = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mResultCode.get(result);\n            mResultData = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mResultData.get(result);\n            mResultExtras = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mResultExtras.get(result);\n            mAbortBroadcast = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mAbortBroadcast.get(result);\n            mFinished = mirror.android.content.BroadcastReceiver.PendingResultJBMR1.mFinished.get(result);\n        } else {\n            mType = mirror.android.content.BroadcastReceiver.PendingResult.mType.get(result);\n            mOrderedHint = mirror.android.content.BroadcastReceiver.PendingResult.mOrderedHint.get(result);\n            mInitialStickyHint = mirror.android.content.BroadcastReceiver.PendingResult.mInitialStickyHint.get(result);\n            mToken = mirror.android.content.BroadcastReceiver.PendingResult.mToken.get(result);\n            mResultCode = mirror.android.content.BroadcastReceiver.PendingResult.mResultCode.get(result);\n            mResultData = mirror.android.content.BroadcastReceiver.PendingResult.mResultData.get(result);\n            mResultExtras = mirror.android.content.BroadcastReceiver.PendingResult.mResultExtras.get(result);\n            mAbortBroadcast = mirror.android.content.BroadcastReceiver.PendingResult.mAbortBroadcast.get(result);\n            mFinished = mirror.android.content.BroadcastReceiver.PendingResult.mFinished.get(result);\n        }\n    }\n\n\n    protected PendingResultData(Parcel in) {\n        this.mType = in.readInt();\n        this.mOrderedHint = in.readByte() != 0;\n        this.mInitialStickyHint = in.readByte() != 0;\n        this.mToken = in.readStrongBinder();\n        this.mSendingUser = in.readInt();\n        this.mFlags = in.readInt();\n        this.mResultCode = in.readInt();\n        this.mResultData = in.readString();\n        this.mResultExtras = in.readBundle();\n        this.mAbortBroadcast = in.readByte() != 0;\n        this.mFinished = in.readByte() != 0;\n    }\n\n    public BroadcastReceiver.PendingResult build() {\n        if (mirror.android.content.BroadcastReceiver.PendingResultMNC.ctor != null) {\n            return mirror.android.content.BroadcastReceiver.PendingResultMNC.ctor.newInstance(mResultCode, mResultData, mResultExtras, mType, mOrderedHint, mInitialStickyHint, mToken, mSendingUser, mFlags);\n        }\n        if (mirror.android.content.BroadcastReceiver.PendingResultJBMR1.ctor != null) {\n            return mirror.android.content.BroadcastReceiver.PendingResultJBMR1.ctor.newInstance(mResultCode, mResultData, mResultExtras, mType, mOrderedHint, mInitialStickyHint, mToken, mSendingUser);\n        }\n        return mirror.android.content.BroadcastReceiver.PendingResult.ctor.newInstance(mResultCode, mResultData, mResultExtras, mType, mOrderedHint, mInitialStickyHint, mToken);\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(this.mType);\n        dest.writeByte(this.mOrderedHint ? (byte) 1 : (byte) 0);\n        dest.writeByte(this.mInitialStickyHint ? (byte) 1 : (byte) 0);\n        dest.writeStrongBinder(this.mToken);\n        dest.writeInt(this.mSendingUser);\n        dest.writeInt(this.mFlags);\n        dest.writeInt(this.mResultCode);\n        dest.writeString(this.mResultData);\n        dest.writeBundle(this.mResultExtras);\n        dest.writeByte(this.mAbortBroadcast ? (byte) 1 : (byte) 0);\n        dest.writeByte(this.mFinished ? (byte) 1 : (byte) 0);\n    }\n\n    public void finish() {\n        try {\n            build().finish();\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/Problem.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n *\n */\npublic class Problem implements Parcelable {\n\tpublic static final Creator<Problem> CREATOR = new Creator<Problem>() {\n\t\tpublic Problem createFromParcel(Parcel source) {\n\t\t\treturn new Problem(source);\n\t\t}\n\n\t\tpublic Problem[] newArray(int size) {\n\t\t\treturn new Problem[size];\n\t\t}\n\t};\n\tpublic Throwable e;\n\n\tpublic Problem(Throwable e) {\n\t\tthis.e = e;\n\t}\n\n\tprotected Problem(Parcel in) {\n\t\tthis.e = (Throwable) in.readSerializable();\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeSerializable(this.e);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/ReceiverInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.content.ComponentName;\nimport android.content.IntentFilter;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n */\n\npublic class ReceiverInfo implements Parcelable {\n\tpublic static final Creator<ReceiverInfo> CREATOR = new Creator<ReceiverInfo>() {\n\t\t@Override\n\t\tpublic ReceiverInfo createFromParcel(Parcel source) {\n\t\t\treturn new ReceiverInfo(source);\n\t\t}\n\n\t\t@Override\n\t\tpublic ReceiverInfo[] newArray(int size) {\n\t\t\treturn new ReceiverInfo[size];\n\t\t}\n\t};\n\tpublic ComponentName component;\n\tpublic IntentFilter[] filters;\n\tpublic String permission;\n\n\tpublic ReceiverInfo(ComponentName component, IntentFilter[] filters, String permission) {\n\t\tthis.component = component;\n\t\tthis.filters = filters;\n\t\tthis.permission = permission;\n\t}\n\n\tprotected ReceiverInfo(Parcel in) {\n\t\tthis.component = in.readParcelable(ComponentName.class.getClassLoader());\n\t\tthis.filters = in.createTypedArray(IntentFilter.CREATOR);\n\t\tthis.permission = in.readString();\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeParcelable(this.component, flags);\n\t\tdest.writeTypedArray(this.filters, flags);\n\t\tdest.writeString(this.permission);\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/StubActivityRecord.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\n\n/**\n * @author Lody\n */\n//\npublic class StubActivityRecord  {\n        public Intent intent;\n        public ActivityInfo info;\n        public ComponentName caller;\n        public int userId;\n\n        public StubActivityRecord(Intent intent, ActivityInfo info, ComponentName caller, int userId) {\n            this.intent = intent;\n            this.info = info;\n            this.caller = caller;\n            this.userId = userId;\n        }\n\n        // 获取原版 Intent 和一些其他信息\n        public StubActivityRecord(Intent stub) {\n            this.intent = stub.getParcelableExtra(\"_VA_|_intent_\");\n            this.info = stub.getParcelableExtra(\"_VA_|_info_\");\n            this.caller = stub.getParcelableExtra(\"_VA_|_caller_\");\n            this.userId = stub.getIntExtra(\"_VA_|_user_id_\", 0);\n        }\n\n    // 将原版 Intent 塞到 Stub Intent\n    public void saveToIntent(Intent stub) {\n        stub.putExtra(\"_VA_|_intent_\", intent);\n        stub.putExtra(\"_VA_|_info_\", info);\n        stub.putExtra(\"_VA_|_caller_\", caller);\n        stub.putExtra(\"_VA_|_user_id_\", userId);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/SyncInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.accounts.Account;\nimport android.os.Parcel;\nimport android.os.Parcelable.Creator;\n\n/**\n * Information about the sync operation that is currently underway.\n */\npublic class SyncInfo {\n\n    public final int authorityId;\n\n    /**\n     * The {@link Account} that is currently being synced.\n     */\n    public final Account account;\n\n    /**\n     * The authority of the provider that is currently being synced.\n     */\n    public final String authority;\n\n    /**\n     * The start time of the current sync operation in milliseconds since boot.\n     * This is represented in elapsed real time.\n     * See {@link android.os.SystemClock#elapsedRealtime()}.\n     */\n    public final long startTime;\n\n    public SyncInfo(int authorityId, Account account, String authority,\n                    long startTime) {\n        this.authorityId = authorityId;\n        this.account = account;\n        this.authority = authority;\n        this.startTime = startTime;\n    }\n\n    public int describeContents() {\n        return 0;\n    }\n\n    public void writeToParcel(Parcel parcel, int flags) {\n        parcel.writeInt(authorityId);\n        account.writeToParcel(parcel, 0);\n        parcel.writeString(authority);\n        parcel.writeLong(startTime);\n    }\n\n    SyncInfo(Parcel parcel) {\n        authorityId = parcel.readInt();\n        account = new Account(parcel);\n        authority = parcel.readString();\n        startTime = parcel.readLong();\n    }\n\n    public android.content.SyncInfo create() {\n        return mirror.android.content.SyncInfo.ctor.newInstance(authorityId, account, authority, startTime);\n    }\n\n    public static final Creator<SyncInfo> CREATOR = new Creator<SyncInfo>() {\n        public SyncInfo createFromParcel(Parcel in) {\n            return new SyncInfo(in);\n        }\n\n        public SyncInfo[] newArray(int size) {\n            return new SyncInfo[size];\n        }\n    };\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/VDeviceInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport com.lody.virtual.os.VEnvironment;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\n\n/**\n * @author Lody\n */\npublic class VDeviceInfo implements Parcelable {\n\n    public String deviceId;\n    public String androidId;\n    public String wifiMac;\n    public String bluetoothMac;\n    public String iccId;\n    public String serial;\n    public String gmsAdId;\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(this.deviceId);\n        dest.writeString(this.androidId);\n        dest.writeString(this.wifiMac);\n        dest.writeString(this.bluetoothMac);\n        dest.writeString(this.iccId);\n        dest.writeString(this.serial);\n        dest.writeString(this.gmsAdId);\n    }\n\n    public VDeviceInfo() {}\n\n    public VDeviceInfo(Parcel in) {\n        this.deviceId = in.readString();\n        this.androidId = in.readString();\n        this.wifiMac = in.readString();\n        this.bluetoothMac = in.readString();\n        this.iccId = in.readString();\n        this.serial = in.readString();\n        this.gmsAdId = in.readString();\n    }\n\n    public static final Parcelable.Creator<VDeviceInfo> CREATOR = new Parcelable.Creator<VDeviceInfo>() {\n        @Override\n        public VDeviceInfo createFromParcel(Parcel source) {\n            return new VDeviceInfo(source);\n        }\n\n        @Override\n        public VDeviceInfo[] newArray(int size) {\n            return new VDeviceInfo[size];\n        }\n    };\n\n    public File getWifiFile(int userId) {\n        File wifiMacFie = VEnvironment.getWifiMacFile(userId);\n        if (!wifiMacFie.exists()) {\n            try {\n                RandomAccessFile file = new RandomAccessFile(wifiMacFie, \"rws\");\n                file.write((wifiMac + \"\\n\").getBytes());\n                file.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return wifiMacFie;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/remote/VParceledListSlice.java",
    "content": "package com.lody.virtual.remote;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.RemoteException;\nimport android.util.Log;\n\n/**\n * Transfer a large list of Parcelable objects across an IPC. Splits into\n * multiple transactions if needed.\n *\n * Caveat: for efficiency and security, all elements must be the same concrete\n * type. In order to avoid writing the class name of each object, we must ensure\n * that each object is the same type, or else unparceling then reparceling the\n * data may yield a different result if the class name encoded in the Parcelable\n * is a Base type. See b/17671747.\n *\n * @hide\n *\n * \t\tOpenSilk: Modified to remove the creator optimization which uses hidden\n *       apis. this means we write an extra string for every list item. Class\n *       verification left in place since the Base type issue still applies\n */\npublic class VParceledListSlice<T extends Parcelable> implements Parcelable {\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static final Parcelable.ClassLoaderCreator<VParceledListSlice> CREATOR = new Parcelable.ClassLoaderCreator<VParceledListSlice>() {\n\t\tpublic VParceledListSlice createFromParcel(Parcel in) {\n\t\t\treturn new VParceledListSlice(in, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic VParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {\n\t\t\treturn new VParceledListSlice(in, loader);\n\t\t}\n\n\t\tpublic VParceledListSlice[] newArray(int size) {\n\t\t\treturn new VParceledListSlice[size];\n\t\t}\n\t};\n\t/*\n\t * TODO get this number from somewhere else. For now set it to a quarter of\n\t * the 1MB limit.\n\t */\n\tprivate static final int MAX_IPC_SIZE = 256 * 1024;\n\tprivate static final int MAX_FIRST_IPC_SIZE = MAX_IPC_SIZE / 2;\n\tprivate static String TAG = \"ParceledListSlice\";\n\tprivate static boolean DEBUG = false;\n\tprivate final List<T> mList;\n\n\tpublic VParceledListSlice(List<T> list) {\n\t\tmList = list;\n\t}\n\n\tprivate VParceledListSlice(Parcel p, ClassLoader loader) {\n\t\tfinal int N = p.readInt();\n\t\tmList = new ArrayList<T>(N);\n\t\tif (DEBUG)\n\t\t\tLog.d(TAG, \"Retrieving \" + N + \" items\");\n\t\tif (N <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Parcelable.Creator<T> creator = p.readParcelableCreator(loader);\n\t\tClass<?> listElementClass = null;\n\n\t\tint i = 0;\n\t\twhile (i < N) {\n\t\t\tif (p.readInt() == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// final T parcelable = p.readCreator(creator, loader);\n\t\t\tfinal T parcelable = p.readParcelable(loader);\n\t\t\tif (listElementClass == null) {\n\t\t\t\tlistElementClass = parcelable.getClass();\n\t\t\t} else {\n\t\t\t\tverifySameType(listElementClass, parcelable.getClass());\n\t\t\t}\n\n\t\t\tmList.add(parcelable);\n\n\t\t\tif (DEBUG)\n\t\t\t\tLog.d(TAG, \"Read inline #\" + i + \": \" + mList.get(mList.size() - 1));\n\t\t\ti++;\n\t\t}\n\t\tif (i >= N) {\n\t\t\treturn;\n\t\t}\n\t\tfinal IBinder retriever = p.readStrongBinder();\n\t\twhile (i < N) {\n\t\t\tif (DEBUG)\n\t\t\t\tLog.d(TAG, \"Reading more @\" + i + \" of \" + N + \": retriever=\" + retriever);\n\t\t\tParcel data = Parcel.obtain();\n\t\t\tParcel reply = Parcel.obtain();\n\t\t\tdata.writeInt(i);\n\t\t\ttry {\n\t\t\t\tretriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);\n\t\t\t} catch (RemoteException e) {\n\t\t\t\tLog.w(TAG, \"Failure retrieving array; only received \" + i + \" of \" + N, e);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\twhile (i < N && reply.readInt() != 0) {\n\t\t\t\t// final T parcelable = reply.readCreator(creator, loader);\n\t\t\t\tfinal T parcelable = reply.readParcelable(loader);\n\t\t\t\tverifySameType(listElementClass, parcelable.getClass());\n\n\t\t\t\tmList.add(parcelable);\n\n\t\t\t\tif (DEBUG)\n\t\t\t\t\tLog.d(TAG, \"Read extra #\" + i + \": \" + mList.get(mList.size() - 1));\n\t\t\t\ti++;\n\t\t\t}\n\t\t\treply.recycle();\n\t\t\tdata.recycle();\n\t\t}\n\t}\n\n\tprivate static void verifySameType(final Class<?> expected, final Class<?> actual) {\n\t\tif (!actual.equals(expected)) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Can't unparcel type \" + actual.getName() + \" in list of type \" + expected.getName());\n\t\t}\n\t}\n\n\tpublic List<T> getList() {\n\t\treturn mList;\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\tint contents = 0;\n\t\tfor (int i = 0; i < mList.size(); i++) {\n\t\t\tcontents |= mList.get(i).describeContents();\n\t\t}\n\t\treturn contents;\n\t}\n\n\t/**\n\t * Write this to another Parcel. Note that this discards the internal Parcel\n\t * and should not be used anymore. This is so we can pass this to a Binder\n\t * where we won't have a chance to call recycle on this.\n\t */\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tfinal int N = mList.size();\n\t\tfinal int callFlags = flags;\n\t\tdest.writeInt(N);\n\t\tif (DEBUG)\n\t\t\tLog.d(TAG, \"Writing \" + N + \" items\");\n\t\tif (N > 0) {\n\t\t\tfinal Class<?> listElementClass = mList.get(0).getClass();\n\t\t\t// dest.writeParcelableCreator(mList.get(0));\n\t\t\tint i = 0;\n\t\t\twhile (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) {\n\t\t\t\tdest.writeInt(1);\n\n\t\t\t\tfinal T parcelable = mList.get(i);\n\t\t\t\tverifySameType(listElementClass, parcelable.getClass());\n\t\t\t\t// parcelable.writeToParcel(dest, callFlags);\n\t\t\t\tdest.writeParcelable(parcelable, callFlags);\n\n\t\t\t\tif (DEBUG)\n\t\t\t\t\tLog.d(TAG, \"Wrote inline #\" + i + \": \" + mList.get(i));\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tif (i < N) {\n\t\t\t\tdest.writeInt(0);\n\t\t\t\tBinder retriever = new Binder() {\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected boolean onTransact(int code, Parcel data, Parcel reply, int flags)\n\t\t\t\t\t\t\tthrows RemoteException {\n\t\t\t\t\t\tif (code != FIRST_CALL_TRANSACTION) {\n\t\t\t\t\t\t\treturn super.onTransact(code, data, reply, flags);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tint i = data.readInt();\n\t\t\t\t\t\tif (DEBUG)\n\t\t\t\t\t\t\tLog.d(TAG, \"Writing more @\" + i + \" of \" + N);\n\t\t\t\t\t\twhile (i < N && reply.dataSize() < MAX_IPC_SIZE) {\n\t\t\t\t\t\t\treply.writeInt(1);\n\n\t\t\t\t\t\t\tfinal T parcelable = mList.get(i);\n\t\t\t\t\t\t\tverifySameType(listElementClass, parcelable.getClass());\n\t\t\t\t\t\t\t// parcelable.writeToParcel(reply, callFlags);\n\t\t\t\t\t\t\treply.writeParcelable(parcelable, callFlags);\n\n\t\t\t\t\t\t\tif (DEBUG)\n\t\t\t\t\t\t\t\tLog.d(TAG, \"Wrote extra #\" + i + \": \" + mList.get(i));\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i < N) {\n\t\t\t\t\t\t\tif (DEBUG)\n\t\t\t\t\t\t\t\tLog.d(TAG, \"Breaking @\" + i + \" of \" + N);\n\t\t\t\t\t\t\treply.writeInt(0);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tif (DEBUG)\n\t\t\t\t\tLog.d(TAG, \"Breaking @\" + i + \" of \" + N + \": retriever=\" + retriever);\n\t\t\t\tdest.writeStrongBinder(retriever);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/BinderProvider.java",
    "content": "package com.lody.virtual.server;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.ServiceManagerNative;\nimport com.lody.virtual.client.stub.DaemonService;\nimport com.lody.virtual.helper.compat.BundleCompat;\nimport com.lody.virtual.server.accounts.VAccountManagerService;\nimport com.lody.virtual.server.am.BroadcastSystem;\nimport com.lody.virtual.server.am.VActivityManagerService;\nimport com.lody.virtual.server.device.VDeviceManagerService;\nimport com.lody.virtual.server.interfaces.IServiceFetcher;\nimport com.lody.virtual.server.job.VJobSchedulerService;\nimport com.lody.virtual.server.notification.VNotificationManagerService;\nimport com.lody.virtual.server.pm.VAppManagerService;\nimport com.lody.virtual.server.pm.VPackageManagerService;\nimport com.lody.virtual.server.pm.VUserManagerService;\nimport com.lody.virtual.server.vs.VirtualStorageService;\n\n/**\n * @author Lody\n */\npublic final class BinderProvider extends ContentProvider {\n\n    private final ServiceFetcher mServiceFetcher = new ServiceFetcher();\n\n    @Override\n    public boolean onCreate() {\n        Context context = getContext();\n        // 这是一个空前台服务，目的是为了保活 VAService 进程，即 :x 进程\n        DaemonService.startup(context);\n        if (!VirtualCore.get().isStartup()) {\n            return true;\n        }\n        VPackageManagerService.systemReady();\n        addService(ServiceManagerNative.PACKAGE, VPackageManagerService.get());\n        VActivityManagerService.systemReady(context);\n        addService(ServiceManagerNative.ACTIVITY, VActivityManagerService.get());\n        addService(ServiceManagerNative.USER, VUserManagerService.get());\n        VAppManagerService.systemReady();\n        addService(ServiceManagerNative.APP, VAppManagerService.get());\n        BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get());\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            addService(ServiceManagerNative.JOB, VJobSchedulerService.get());\n        }\n        VNotificationManagerService.systemReady(context);\n        addService(ServiceManagerNative.NOTIFICATION, VNotificationManagerService.get());\n        VAppManagerService.get().scanApps();\n        VAccountManagerService.systemReady();\n        addService(ServiceManagerNative.ACCOUNT, VAccountManagerService.get());\n        addService(ServiceManagerNative.VS, VirtualStorageService.get());\n        addService(ServiceManagerNative.DEVICE, VDeviceManagerService.get());\n        return true;\n    }\n\n\n    private void addService(String name, IBinder service) {\n        ServiceCache.addService(name, service);\n    }\n\n    @Override\n    public Bundle call(String method, String arg, Bundle extras) {\n        if (\"@\".equals(method)) {\n            Bundle bundle = new Bundle();\n            BundleCompat.putBinder(bundle, \"_VA_|_binder_\", mServiceFetcher);\n            return bundle;\n        }\n        return null;\n    }\n\n    @Override\n    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {\n        return null;\n    }\n\n    @Override\n    public String getType(Uri uri) {\n        return null;\n    }\n\n    @Override\n    public Uri insert(Uri uri, ContentValues values) {\n        return null;\n    }\n\n    @Override\n    public int delete(Uri uri, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    @Override\n    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    // ServiceFetcher 实现类 远程调用类\n    private class ServiceFetcher extends IServiceFetcher.Stub {\n        @Override\n        public IBinder getService(String name) throws RemoteException {\n            if (name != null) {\n                return ServiceCache.getService(name);\n            }\n            return null;\n        }\n\n        @Override\n        public void addService(String name, IBinder service) throws RemoteException {\n            if (name != null && service != null) {\n                ServiceCache.addService(name, service);\n            }\n        }\n\n        @Override\n        public void removeService(String name) throws RemoteException {\n            if (name != null) {\n                ServiceCache.removeService(name);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/ServiceCache.java",
    "content": "package com.lody.virtual.server;\n\nimport android.os.IBinder;\n\nimport com.lody.virtual.helper.collection.ArrayMap;\n\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class ServiceCache {\n\n\tprivate static final Map<String, IBinder> sCache = new ArrayMap<>(5);\n\n\tpublic static void addService(String name, IBinder service) {\n\t\tsCache.put(name, service);\n\t}\n\n\tpublic static IBinder removeService(String name) {\n\t\treturn sCache.remove(name);\n\t}\n\n\tpublic static IBinder getService(String name) {\n\t\treturn sCache.get(name);\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/accounts/RegisteredServicesParser.java",
    "content": "package com.lody.virtual.server.accounts;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.res.AssetManager;\nimport android.content.res.Resources;\nimport android.content.res.XmlResourceParser;\nimport android.os.Bundle;\n\nimport com.lody.virtual.server.pm.PackageCacheManager;\nimport com.lody.virtual.server.pm.PackageSetting;\n\npublic class RegisteredServicesParser {\n\n    public XmlResourceParser getParser(Context context, ServiceInfo serviceInfo, String name) {\n        Bundle meta = serviceInfo.metaData;\n        if (meta != null) {\n            int xmlId = meta.getInt(name);\n            if (xmlId != 0) {\n                try {\n                    return getResources(context, serviceInfo.applicationInfo).getXml(xmlId);\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return null;\n    }\n\n    public Resources getResources(Context context, ApplicationInfo appInfo) throws Exception {\n        PackageSetting ps = PackageCacheManager.getSetting(appInfo.packageName);\n        if (ps != null) {\n            AssetManager assets = mirror.android.content.res.AssetManager.ctor.newInstance();\n            mirror.android.content.res.AssetManager.addAssetPath.call(assets, ps.apkPath);\n            Resources hostRes = context.getResources();\n            return new Resources(assets, hostRes.getDisplayMetrics(), hostRes.getConfiguration());\n        }\n        return null;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/accounts/VAccount.java",
    "content": "package com.lody.virtual.server.accounts;\n\nimport android.accounts.Account;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class VAccount implements Parcelable {\n    public static final Parcelable.Creator<VAccount> CREATOR = new Parcelable.Creator<VAccount>() {\n        @Override\n        public VAccount createFromParcel(Parcel source) {\n            return new VAccount(source);\n        }\n\n        @Override\n        public VAccount[] newArray(int size) {\n            return new VAccount[size];\n        }\n    };\n    public int userId;\n    public String name;\n    public String previousName;\n    public String type;\n    public String password;\n    public long lastAuthenticatedTime;\n    public Map<String, String> authTokens;\n    public Map<String, String> userDatas;\n\n\n    public VAccount(int userId, Account account) {\n        this.userId = userId;\n        name = account.name;\n        type = account.type;\n        authTokens = new HashMap<>();\n        userDatas = new HashMap<>();\n    }\n\n    public VAccount(Parcel in) {\n        userId = in.readInt();\n        name = in.readString();\n        previousName = in.readString();\n        type = in.readString();\n        password = in.readString();\n        lastAuthenticatedTime = in.readLong();\n        int authTokensSize = in.readInt();\n        authTokens = new HashMap<>(authTokensSize);\n        for (int i = 0; i < authTokensSize; i++) {\n            String key = in.readString();\n            String value = in.readString();\n            authTokens.put(key, value);\n        }\n        int userDatasSize = in.readInt();\n        userDatas = new HashMap<>(userDatasSize);\n        for (int i = 0; i < userDatasSize; i++) {\n            String key = in.readString();\n            String value = in.readString();\n            userDatas.put(key, value);\n        }\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(userId);\n        dest.writeString(name);\n        dest.writeString(previousName);\n        dest.writeString(type);\n        dest.writeString(password);\n        dest.writeLong(lastAuthenticatedTime);\n        dest.writeInt(authTokens.size());\n        for (Map.Entry<String, String> entry : authTokens.entrySet()) {\n            dest.writeString(entry.getKey());\n            dest.writeString(entry.getValue());\n        }\n        dest.writeInt(userDatas.size());\n        for (Map.Entry<String, String> entry : userDatas.entrySet()) {\n            dest.writeString(entry.getKey());\n            dest.writeString(entry.getValue());\n        }\n    }\n}\n\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/accounts/VAccountManagerService.java",
    "content": "package com.lody.virtual.server.accounts;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManager;\nimport android.accounts.AuthenticatorDescription;\nimport android.accounts.IAccountAuthenticator;\nimport android.accounts.IAccountAuthenticatorResponse;\nimport android.accounts.IAccountManagerResponse;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.content.res.XmlResourceParser;\nimport android.os.Binder;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.Parcel;\nimport android.os.RemoteException;\nimport android.os.SystemClock;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.util.SparseArray;\nimport android.util.Xml;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.compat.AccountManagerCompat;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VBinder;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.server.IAccountManager;\nimport com.lody.virtual.server.am.VActivityManagerService;\nimport com.lody.virtual.server.pm.VPackageManagerService;\n\nimport org.xmlpull.v1.XmlPullParser;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport mirror.com.android.internal.R_Hide;\n\nimport static android.accounts.AccountManager.ERROR_CODE_BAD_ARGUMENTS;\n\n\n/**\n * @author Lody\n */\npublic class VAccountManagerService extends IAccountManager.Stub {\n\n    private static final AtomicReference<VAccountManagerService> sInstance = new AtomicReference<>();\n    private static final long CHECK_IN_TIME = 30 * 24 * 60 * 1000L;\n    private static final String TAG = VAccountManagerService.class.getSimpleName();\n    private final SparseArray<List<VAccount>> accountsByUserId = new SparseArray<>();\n    private final LinkedList<AuthTokenRecord> authTokenRecords = new LinkedList<>();\n    private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<>();\n    private final AuthenticatorCache cache = new AuthenticatorCache();\n    private Context mContext = VirtualCore.get().getContext();\n    private long lastAccountChangeTime = 0;\n\n\n    public static VAccountManagerService get() {\n        return sInstance.get();\n    }\n\n    public static void systemReady() {\n        VAccountManagerService service = new VAccountManagerService();\n        service.readAllAccounts();\n        sInstance.set(service);\n    }\n\n\n    private static AuthenticatorDescription parseAuthenticatorDescription(Resources resources, String packageName,\n                                                                          AttributeSet attributeSet) {\n        TypedArray array = resources.obtainAttributes(attributeSet, R_Hide.styleable.AccountAuthenticator.get());\n        try {\n            String accountType = array.getString(R_Hide.styleable.AccountAuthenticator_accountType.get());\n            int label = array.getResourceId(R_Hide.styleable.AccountAuthenticator_label.get(), 0);\n            int icon = array.getResourceId(R_Hide.styleable.AccountAuthenticator_icon.get(), 0);\n            int smallIcon = array.getResourceId(R_Hide.styleable.AccountAuthenticator_smallIcon.get(), 0);\n            int accountPreferences = array.getResourceId(R_Hide.styleable.AccountAuthenticator_accountPreferences.get(), 0);\n            boolean customTokens = array.getBoolean(R_Hide.styleable.AccountAuthenticator_customTokens.get(), false);\n            if (TextUtils.isEmpty(accountType)) {\n                return null;\n            }\n            return new AuthenticatorDescription(accountType, packageName, label, icon, smallIcon, accountPreferences,\n                    customTokens);\n        } finally {\n            array.recycle();\n        }\n    }\n\n\n    @Override\n    public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {\n        synchronized (cache) {\n            AuthenticatorDescription[] descArray = new AuthenticatorDescription[cache.authenticators.size()];\n            int i = 0;\n            for (AuthenticatorInfo info : cache.authenticators.values()) {\n                descArray[i] = info.desc;\n                i++;\n            }\n            return descArray;\n        }\n    }\n\n    @Override\n    public void getAccountsByFeatures(int userId, IAccountManagerResponse response, String type, String[] features) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (type == null) throw new IllegalArgumentException(\"accountType is null\");\n        AuthenticatorInfo info = getAuthenticatorInfo(type);\n        if (info == null) {\n            Bundle bundle = new Bundle();\n            bundle.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);\n            try {\n                response.onResult(bundle);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n\n        if (features == null || features.length == 0) {\n            Bundle bundle = new Bundle();\n            bundle.putParcelableArray(AccountManager.KEY_ACCOUNTS, getAccounts(userId, type));\n            try {\n                response.onResult(bundle);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        } else {\n            new GetAccountsByTypeAndFeatureSession(response, userId, info, features).bind();\n        }\n    }\n\n    @Override\n    public final String getPreviousName(int userId, Account account) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        synchronized (accountsByUserId) {\n            String previousName = null;\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                previousName = vAccount.previousName;\n            }\n            return previousName;\n        }\n    }\n\n\n    @Override\n    public Account[] getAccounts(int userId, String type) {\n        List<Account> accountList = getAccountList(userId, type);\n        return accountList.toArray(new Account[accountList.size()]);\n    }\n\n\n    private List<Account> getAccountList(int userId, String type) {\n        synchronized (accountsByUserId) {\n            List<Account> accounts = new ArrayList<>();\n            List<VAccount> vAccounts = accountsByUserId.get(userId);\n            if (vAccounts != null) {\n                for (VAccount vAccount : vAccounts) {\n                    if (type == null || vAccount.type.equals(type)) {\n                        accounts.add(new Account(vAccount.name, vAccount.type));\n                    }\n                }\n            }\n            return accounts;\n        }\n    }\n\n    @Override\n    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        try {\n            return super.onTransact(code, data, reply, flags);\n        } catch (Throwable e) {\n            e.printStackTrace();\n            throw e;\n        }\n    }\n\n    @Override\n    public final void getAuthToken(final int userId, final IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, boolean expectActivityLaunch, final Bundle loginOptions) {\n        if (response == null) {\n            throw new IllegalArgumentException(\"response is null\");\n        }\n        try {\n            if (account == null) {\n                VLog.w(TAG, \"getAuthToken called with null account\");\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account is null\");\n                return;\n            }\n            if (authTokenType == null) {\n                VLog.w(TAG, \"getAuthToken called with null authTokenType\");\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"authTokenType is null\");\n                return;\n            }\n        } catch (RemoteException e) {\n            e.printStackTrace();\n            return;\n        }\n        AuthenticatorInfo info = getAuthenticatorInfo(account.type);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        // Get the calling package. We will use it for the purpose of caching.\n        final String callerPkg = loginOptions.getString(AccountManagerCompat.KEY_ANDROID_PACKAGE_NAME);\n        final boolean customTokens = info.desc.customTokens;\n\n        loginOptions.putInt(AccountManager.KEY_CALLER_UID, VBinder.getCallingUid());\n        loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());\n        if (notifyOnAuthFailure) {\n            loginOptions.putBoolean(AccountManagerCompat.KEY_NOTIFY_ON_FAILURE, true);\n        }\n        if (!customTokens) {\n            VAccount vAccount;\n            synchronized (accountsByUserId) {\n                vAccount = getAccount(userId, account);\n            }\n            String authToken = vAccount != null ? vAccount.authTokens.get(authTokenType) : null;\n            if (authToken != null) {\n                Bundle result = new Bundle();\n                result.putString(AccountManager.KEY_AUTHTOKEN, authToken);\n                result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\n                result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);\n                onResult(response, result);\n                return;\n            }\n        }\n        if (customTokens) {\n            String authToken = getCustomAuthToken(userId, account, authTokenType, callerPkg);\n            if (authToken != null) {\n                Bundle result = new Bundle();\n                result.putString(AccountManager.KEY_AUTHTOKEN, authToken);\n                result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\n                result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);\n                onResult(response, result);\n                return;\n            }\n        }\n        new Session(response, userId, info, expectActivityLaunch, false, account.name) {\n\n            @Override\n            protected String toDebugString(long now) {\n                return super.toDebugString(now) + \", getAuthToken\"\n                        + \", \" + account\n                        + \", authTokenType \" + authTokenType\n                        + \", loginOptions \" + loginOptions\n                        + \", notifyOnAuthFailure \" + notifyOnAuthFailure;\n            }\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);\n            }\n\n            @Override\n            public void onResult(Bundle result) throws RemoteException {\n                if (result != null) {\n                    String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);\n                    if (authToken != null) {\n                        String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);\n                        String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);\n                        if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {\n                            onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,\n                                    \"the type and name should not be empty\");\n                            return;\n                        }\n                        if (!customTokens) {\n                            synchronized (accountsByUserId) {\n                                VAccount account = getAccount(userId, name, type);\n                                if (account == null) {\n                                    List<VAccount> accounts = accountsByUserId.get(userId);\n                                    if (accounts == null) {\n                                        accounts = new ArrayList<>();\n                                        accountsByUserId.put(userId, accounts);\n                                    }\n                                    account = new VAccount(userId, new Account(name, type));\n                                    accounts.add(account);\n                                    saveAllAccounts();\n                                }\n                            }\n                        }\n                        long expiryMillis = result.getLong(\n                                AccountManagerCompat.KEY_CUSTOM_TOKEN_EXPIRY, 0L);\n                        if (customTokens\n                                && expiryMillis > System.currentTimeMillis()) {\n                            AuthTokenRecord record = new AuthTokenRecord(userId, account, authTokenType, callerPkg, authToken, expiryMillis);\n                            synchronized (authTokenRecords) {\n                                authTokenRecords.remove(record);\n                                authTokenRecords.add(record);\n                            }\n                        }\n                    }\n                    Intent intent = result.getParcelable(AccountManager.KEY_INTENT);\n                    if (intent != null && notifyOnAuthFailure && !customTokens) {\n                        // TODO: send Signin error Notification\n                    }\n                }\n                super.onResult(result);\n            }\n        }.bind();\n    }\n\n\n    @Override\n    public void setPassword(int userId, Account account, String password) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        setPasswordInternal(userId, account, password);\n    }\n\n    private void setPasswordInternal(int userId, Account account, String password) {\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                vAccount.password = password;\n                vAccount.authTokens.clear();\n                saveAllAccounts();\n                synchronized (authTokenRecords) {\n                    Iterator<AuthTokenRecord> iterator = authTokenRecords.iterator();\n                    while (iterator.hasNext()) {\n                        AuthTokenRecord record = iterator.next();\n                        if (record.userId == userId && record.account.equals(account)) {\n                            iterator.remove();\n                        }\n                    }\n                }\n                sendAccountsChangedBroadcast(userId);\n            }\n        }\n    }\n\n    @Override\n    public void setAuthToken(int userId, Account account, String authTokenType, String authToken) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        if (authTokenType == null) throw new IllegalArgumentException(\"authTokenType is null\");\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                // FIXME: cancelNotification\n                vAccount.authTokens.put(authTokenType, authToken);\n                this.saveAllAccounts();\n            }\n        }\n    }\n\n\n    @Override\n    public void setUserData(int userId, Account account, String key, String value) {\n        if (key == null) throw new IllegalArgumentException(\"key is null\");\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        VAccount vAccount = getAccount(userId, account);\n        if (vAccount != null) {\n            synchronized (accountsByUserId) {\n                vAccount.userDatas.put(key, value);\n                saveAllAccounts();\n            }\n        }\n    }\n\n\n    @Override\n    public void hasFeatures(int userId, IAccountManagerResponse response,\n                            final Account account, final String[] features) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        if (features == null) throw new IllegalArgumentException(\"features is null\");\n        AuthenticatorInfo info = this.getAuthenticatorInfo(account.type);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        new Session(response, userId, info, false, true, account.name) {\n\n            @Override\n            public void run() throws RemoteException {\n                try {\n                    mAuthenticator.hasFeatures(this, account, features);\n                } catch (RemoteException e) {\n                    onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, \"remote exception\");\n                }\n            }\n\n            @Override\n            public void onResult(Bundle result) throws RemoteException {\n                IAccountManagerResponse response = getResponseAndClose();\n                if (response != null) {\n                    try {\n                        if (result == null) {\n                            response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, \"null bundle\");\n                            return;\n                        }\n                        Log.v(TAG, getClass().getSimpleName() + \" calling onResult() on response \"\n                                + response);\n                        final Bundle newResult = new Bundle();\n                        newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,\n                                result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));\n                        response.onResult(newResult);\n                    } catch (RemoteException e) {\n                        // if the caller is dead then there is no one to care about remote exceptions\n                        Log.v(TAG, \"failure while notifying response\", e);\n                    }\n                }\n            }\n        }.bind();\n    }\n\n\n    @Override\n    public void updateCredentials(int userId, final IAccountManagerResponse response, final Account account,\n                                  final String authTokenType, final boolean expectActivityLaunch,\n                                  final Bundle loginOptions) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        if (authTokenType == null) throw new IllegalArgumentException(\"authTokenType is null\");\n        AuthenticatorInfo info = this.getAuthenticatorInfo(account.type);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        new Session(response, userId, info, expectActivityLaunch, false, account.name) {\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);\n            }\n\n            @Override\n            protected String toDebugString(long now) {\n                if (loginOptions != null) loginOptions.keySet();\n                return super.toDebugString(now) + \", updateCredentials\"\n                        + \", \" + account\n                        + \", authTokenType \" + authTokenType\n                        + \", loginOptions \" + loginOptions;\n            }\n\n        }.bind();\n    }\n\n    @Override\n    public String getPassword(int userId, Account account) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                return vAccount.password;\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public String getUserData(int userId, Account account, String key) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        if (key == null) throw new IllegalArgumentException(\"key is null\");\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                return vAccount.userDatas.get(key);\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public void editProperties(int userId, IAccountManagerResponse response, final String accountType,\n                               final boolean expectActivityLaunch) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (accountType == null) throw new IllegalArgumentException(\"accountType is null\");\n        AuthenticatorInfo info = this.getAuthenticatorInfo(accountType);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        new Session(response, userId, info, expectActivityLaunch, true, null) {\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.editProperties(this, mAuthenticatorInfo.desc.type);\n            }\n\n            @Override\n            protected String toDebugString(long now) {\n                return super.toDebugString(now) + \", editProperties\"\n                        + \", accountType \" + accountType;\n            }\n\n        }.bind();\n\n    }\n\n\n    @Override\n    public void getAuthTokenLabel(int userId, IAccountManagerResponse response, final String accountType,\n                                  final String authTokenType) {\n        if (accountType == null) throw new IllegalArgumentException(\"accountType is null\");\n        if (authTokenType == null) throw new IllegalArgumentException(\"authTokenType is null\");\n        AuthenticatorInfo info = getAuthenticatorInfo(accountType);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        new Session(response, userId, info, false, false, null) {\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.getAuthTokenLabel(this, authTokenType);\n            }\n\n            @Override\n            public void onResult(Bundle result) throws RemoteException {\n                if (result != null) {\n                    String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);\n                    Bundle bundle = new Bundle();\n                    bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);\n                    super.onResult(bundle);\n                } else {\n                    super.onResult(null);\n                }\n            }\n        }.bind();\n    }\n\n    public void confirmCredentials(int userId, IAccountManagerResponse response, final Account account, final Bundle options, final boolean expectActivityLaunch) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        AuthenticatorInfo info = getAuthenticatorInfo(account.type);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        new Session(response, userId, info, expectActivityLaunch, true, account.name, true, true) {\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.confirmCredentials(this, account, options);\n            }\n\n        }.bind();\n\n    }\n\n    @Override\n    public void addAccount(int userId, final IAccountManagerResponse response, final String accountType,\n                           final String authTokenType, final String[] requiredFeatures,\n                           final boolean expectActivityLaunch, final Bundle optionsIn) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (accountType == null) throw new IllegalArgumentException(\"accountType is null\");\n        AuthenticatorInfo info = getAuthenticatorInfo(accountType);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        new Session(response, userId, info, expectActivityLaunch, true, null, false, true) {\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.addAccount(this, mAuthenticatorInfo.desc.type, authTokenType, requiredFeatures,\n                        optionsIn);\n            }\n\n            @Override\n            protected String toDebugString(long now) {\n                return super.toDebugString(now) + \", addAccount\"\n                        + \", accountType \" + accountType\n                        + \", requiredFeatures \"\n                        + (requiredFeatures != null\n                        ? TextUtils.join(\",\", requiredFeatures)\n                        : null);\n            }\n\n        }.bind();\n\n    }\n\n    @Override\n    public boolean addAccountExplicitly(int userId, Account account, String password, Bundle extras) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        return insertAccountIntoDatabase(userId, account, password, extras);\n    }\n\n    @Override\n    public boolean removeAccountExplicitly(int userId, Account account) {\n        return account != null && removeAccountInternal(userId, account);\n    }\n\n    @Override\n    public void renameAccount(int userId, IAccountManagerResponse response, Account accountToRename, String newName) {\n        if (accountToRename == null) throw new IllegalArgumentException(\"account is null\");\n        Account resultingAccount = renameAccountInternal(userId, accountToRename, newName);\n        Bundle result = new Bundle();\n        result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);\n        result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);\n        try {\n            response.onResult(result);\n        } catch (RemoteException e) {\n            Log.w(TAG, e.getMessage());\n        }\n    }\n\n    @Override\n    public void removeAccount(final int userId, IAccountManagerResponse response, final Account account,\n                              boolean expectActivityLaunch) {\n        if (response == null) throw new IllegalArgumentException(\"response is null\");\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        AuthenticatorInfo info = this.getAuthenticatorInfo(account.type);\n        if (info == null) {\n            try {\n                response.onError(ERROR_CODE_BAD_ARGUMENTS, \"account.type does not exist\");\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            return;\n        }\n        // FIXME: Cancel Notification\n\n        new Session(response, userId, info, expectActivityLaunch, true, account.name) {\n            @Override\n            protected String toDebugString(long now) {\n                return super.toDebugString(now) + \", removeAccount\"\n                        + \", account \" + account;\n            }\n\n            @Override\n            public void run() throws RemoteException {\n                mAuthenticator.getAccountRemovalAllowed(this, account);\n            }\n\n            @Override\n            public void onResult(Bundle result) throws RemoteException {\n                if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)\n                        && !result.containsKey(AccountManager.KEY_INTENT)) {\n                    final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);\n                    if (removalAllowed) {\n                        removeAccountInternal(userId, account);\n                    }\n                    IAccountManagerResponse response = getResponseAndClose();\n                    if (response != null) {\n                        Log.v(TAG, getClass().getSimpleName() + \" calling onResult() on response \"\n                                + response);\n                        Bundle result2 = new Bundle();\n                        result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);\n                        try {\n                            response.onResult(result2);\n                        } catch (RemoteException e) {\n                            e.printStackTrace();\n                        }\n                    }\n                }\n                super.onResult(result);\n            }\n\n        }.bind();\n\n    }\n\n    @Override\n    public void clearPassword(int userId, Account account) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        setPasswordInternal(userId, account, null);\n    }\n\n    private boolean removeAccountInternal(int userId, Account account) {\n        List<VAccount> accounts = accountsByUserId.get(userId);\n        if (accounts != null) {\n            Iterator<VAccount> iterator = accounts.iterator();\n            while (iterator.hasNext()) {\n                VAccount vAccount = iterator.next();\n                if (userId == vAccount.userId\n                        && TextUtils.equals(vAccount.name, account.name)\n                        && TextUtils.equals(account.type, vAccount.type)) {\n                    iterator.remove();\n                    saveAllAccounts();\n                    sendAccountsChangedBroadcast(userId);\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n\n    @Override\n    public boolean accountAuthenticated(int userId, final Account account) {\n        if (account == null) {\n            throw new IllegalArgumentException(\"account is null\");\n        }\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                vAccount.lastAuthenticatedTime = System.currentTimeMillis();\n                saveAllAccounts();\n                return true;\n            }\n            return false;\n        }\n    }\n\n    @Override\n    public void invalidateAuthToken(int userId, String accountType, String authToken) {\n        if (accountType == null) throw new IllegalArgumentException(\"accountType is null\");\n        if (authToken == null) throw new IllegalArgumentException(\"authToken is null\");\n        synchronized (accountsByUserId) {\n            List<VAccount> accounts = accountsByUserId.get(userId);\n            if (accounts != null) {\n                boolean changed = false;\n                for (VAccount account : accounts) {\n                    if (account.type.equals(accountType)) {\n                        account.authTokens.values().remove(authToken);\n                        changed = true;\n                    }\n                }\n                if (changed) {\n                    saveAllAccounts();\n                }\n            }\n            synchronized (authTokenRecords) {\n                Iterator<AuthTokenRecord> iterator = authTokenRecords.iterator();\n                while (iterator.hasNext()) {\n                    AuthTokenRecord record = iterator.next();\n                    if (record.userId == userId && record.authTokenType.equals(accountType)\n                            && record.authToken.equals(authToken)) {\n                        iterator.remove();\n                    }\n                }\n            }\n        }\n    }\n\n\n    private Account renameAccountInternal(int userId, Account accountToRename, String newName) {\n        // TODO: Cancel Notification\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, accountToRename);\n            if (vAccount != null) {\n                vAccount.previousName = vAccount.name;\n                vAccount.name = newName;\n                saveAllAccounts();\n                Account newAccount = new Account(vAccount.name, vAccount.type);\n                synchronized (authTokenRecords) {\n                    for (AuthTokenRecord record : authTokenRecords) {\n                        if (record.userId == userId && record.account.equals(accountToRename)) {\n                            record.account = newAccount;\n                        }\n                    }\n                }\n                sendAccountsChangedBroadcast(userId);\n                return newAccount;\n            }\n        }\n        return accountToRename;\n    }\n\n    @Override\n    public String peekAuthToken(int userId, Account account, String authTokenType) {\n        if (account == null) throw new IllegalArgumentException(\"account is null\");\n        if (authTokenType == null) throw new IllegalArgumentException(\"authTokenType is null\");\n        synchronized (accountsByUserId) {\n            VAccount vAccount = getAccount(userId, account);\n            if (vAccount != null) {\n                return vAccount.authTokens.get(authTokenType);\n            }\n            return null;\n        }\n    }\n\n\n    private String getCustomAuthToken(int userId, Account account, String authTokenType, String packageName) {\n        AuthTokenRecord record = new AuthTokenRecord(userId, account, authTokenType, packageName);\n        String authToken = null;\n        long now = System.currentTimeMillis();\n        synchronized (authTokenRecords) {\n            Iterator<AuthTokenRecord> iterator = authTokenRecords.iterator();\n            while (iterator.hasNext()) {\n                AuthTokenRecord one = iterator.next();\n                if (one.expiryEpochMillis > 0 && one.expiryEpochMillis < now) {\n                    iterator.remove();\n                } else if (record.equals(one)) {\n                    authToken = record.authToken;\n                }\n            }\n        }\n        return authToken;\n    }\n\n    private void onResult(IAccountManagerResponse response, Bundle result) {\n        try {\n            response.onResult(result);\n        } catch (RemoteException e) {\n            // if the caller is dead then there is no one to care about remote\n            // exceptions\n            e.printStackTrace();\n        }\n    }\n\n    private AuthenticatorInfo getAuthenticatorInfo(String type) {\n        synchronized (cache) {\n            return type == null ? null : cache.authenticators.get(type);\n        }\n    }\n\n\n    private VAccount getAccount(int userId, Account account) {\n        return this.getAccount(userId, account.name, account.type);\n    }\n\n    private boolean insertAccountIntoDatabase(int userId, Account account, String password, Bundle extras) {\n        if (account == null) {\n            return false;\n        }\n        synchronized (accountsByUserId) {\n            VAccount vAccount = new VAccount(userId, account);\n            vAccount.password = password;\n            // convert the [Bundle] to [Map<String, String>]\n            if (extras != null) {\n                for (String key : extras.keySet()) {\n                    Object value = extras.get(key);\n                    if (value instanceof String) {\n                        vAccount.userDatas.put(key, (String) value);\n                    }\n                }\n            }\n            List<VAccount> accounts = accountsByUserId.get(userId);\n            if (accounts == null) {\n                accounts = new ArrayList<>();\n                accountsByUserId.put(userId, accounts);\n            }\n            accounts.add(vAccount);\n            saveAllAccounts();\n            sendAccountsChangedBroadcast(vAccount.userId);\n            return true;\n        }\n    }\n\n    private void sendAccountsChangedBroadcast(int userId) {\n        Intent intent = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);\n        VActivityManagerService.get().sendBroadcastAsUser(intent, new VUserHandle(userId));\n        broadcastCheckInNowIfNeed(userId);\n    }\n\n    private void broadcastCheckInNowIfNeed(int userId) {\n        long time = System.currentTimeMillis();\n        if (Math.abs(time - lastAccountChangeTime) > CHECK_IN_TIME) {\n            lastAccountChangeTime = time;\n            saveAllAccounts();\n            Intent intent = new Intent(\"android.server.checkin.CHECKIN_NOW\");\n            VActivityManagerService.get().sendBroadcastAsUser(intent, new VUserHandle(userId));\n        }\n    }\n\n    /**\n     * Serializing all accounts\n     */\n    private void saveAllAccounts() {\n        File accountFile = VEnvironment.getAccountConfigFile();\n        Parcel dest = Parcel.obtain();\n        try {\n            dest.writeInt(1);\n            List<VAccount> accounts = new ArrayList<>();\n            for (int i = 0; i < this.accountsByUserId.size(); i++) {\n                List<VAccount> list = this.accountsByUserId.valueAt(i);\n                if (list != null) {\n                    accounts.addAll(list);\n                }\n            }\n            dest.writeInt(accounts.size());\n            for (VAccount account : accounts) {\n                account.writeToParcel(dest, 0);\n            }\n            dest.writeLong(lastAccountChangeTime);\n            FileOutputStream fileOutputStream = new FileOutputStream(accountFile);\n            fileOutputStream.write(dest.marshall());\n            fileOutputStream.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        dest.recycle();\n    }\n\n    /**\n     * Read all accounts from file.\n     */\n    private void readAllAccounts() {\n        File accountFile = VEnvironment.getAccountConfigFile();\n        refreshAuthenticatorCache(null);\n        if (accountFile.exists()) {\n            accountsByUserId.clear();\n            Parcel dest = Parcel.obtain();\n            try {\n                FileInputStream is = new FileInputStream(accountFile);\n                byte[] bytes = new byte[(int) accountFile.length()];\n                int readLength = is.read(bytes);\n                is.close();\n                if (readLength != bytes.length) {\n                    throw new IOException(String.format(Locale.ENGLISH, \"Expect length %d, but got %d.\", bytes.length, readLength));\n                }\n                dest.unmarshall(bytes, 0, bytes.length);\n                dest.setDataPosition(0);\n                dest.readInt(); // skip the magic\n                int size = dest.readInt(); // the VAccount's size we need to read\n                boolean invalid = false;\n                while (size-- > 0) {\n                    VAccount account = new VAccount(dest);\n                    VLog.d(TAG, \"Reading account : \" + account.type);\n                    AuthenticatorInfo info = cache.authenticators.get(account.type);\n                    if (info != null) {\n                        List<VAccount> accounts = accountsByUserId.get(account.userId);\n                        if (accounts == null) {\n                            accounts = new ArrayList<>();\n                            accountsByUserId.put(account.userId, accounts);\n                        }\n                        accounts.add(account);\n                    } else {\n                        invalid = true;\n                    }\n                }\n                lastAccountChangeTime = dest.readLong();\n                if (invalid) {\n                    saveAllAccounts();\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            } finally {\n                dest.recycle();\n            }\n        }\n    }\n\n\n    private VAccount getAccount(int userId, String accountName, String accountType) {\n        List<VAccount> accounts = accountsByUserId.get(userId);\n        if (accounts != null) {\n            for (VAccount account : accounts) {\n                if (TextUtils.equals(account.name, accountName) && TextUtils.equals(account.type, accountType)) {\n                    return account;\n                }\n            }\n        }\n        return null;\n    }\n\n\n    public void refreshAuthenticatorCache(String packageName) {\n        cache.authenticators.clear();\n        Intent intent = new Intent(AccountManager.ACTION_AUTHENTICATOR_INTENT);\n        if (packageName != null) {\n            intent.setPackage(packageName);\n        }\n        generateServicesMap(\n                VPackageManagerService.get().queryIntentServices(intent, null, PackageManager.GET_META_DATA, 0),\n                cache.authenticators, new RegisteredServicesParser());\n    }\n\n    private void generateServicesMap(List<ResolveInfo> services, Map<String, AuthenticatorInfo> map,\n                                     RegisteredServicesParser accountParser) {\n        for (ResolveInfo info : services) {\n            XmlResourceParser parser = accountParser.getParser(mContext, info.serviceInfo,\n                    AccountManager.AUTHENTICATOR_META_DATA_NAME);\n            if (parser != null) {\n                try {\n                    AttributeSet attributeSet = Xml.asAttributeSet(parser);\n                    int type;\n                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {\n                        // Nothing to do\n                    }\n                    if (AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME.equals(parser.getName())) {\n                        AuthenticatorDescription desc = parseAuthenticatorDescription(\n                                accountParser.getResources(mContext, info.serviceInfo.applicationInfo),\n                                info.serviceInfo.packageName, attributeSet);\n                        if (desc != null) {\n                            map.put(desc.type, new AuthenticatorInfo(desc, info.serviceInfo));\n                        }\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    final static class AuthTokenRecord {\n        public int userId;\n        public Account account;\n        public long expiryEpochMillis;\n        public String authToken;\n        private String authTokenType;\n        private String packageName;\n\n        AuthTokenRecord(int userId, Account account, String authTokenType, String packageName, String authToken,\n                        long expiryEpochMillis) {\n            this.userId = userId;\n            this.account = account;\n            this.authTokenType = authTokenType;\n            this.packageName = packageName;\n            this.authToken = authToken;\n            this.expiryEpochMillis = expiryEpochMillis;\n        }\n\n        AuthTokenRecord(int userId, Account account, String authTokenType, String packageName) {\n            this.userId = userId;\n            this.account = account;\n            this.authTokenType = authTokenType;\n            this.packageName = packageName;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o)\n                return true;\n            if (o == null || getClass() != o.getClass())\n                return false;\n            AuthTokenRecord that = (AuthTokenRecord) o;\n            return userId == that.userId\n                    && account.equals(that.account)\n                    && authTokenType.equals(that.authTokenType)\n                    && packageName.equals(that.packageName);\n        }\n\n        @Override\n        public int hashCode() {\n            return ((this.userId * 31 + this.account.hashCode()) * 31\n                    + this.authTokenType.hashCode()) * 31\n                    + this.packageName.hashCode();\n        }\n    }\n\n    private final class AuthenticatorInfo {\n        final AuthenticatorDescription desc;\n        final ServiceInfo serviceInfo;\n\n        AuthenticatorInfo(AuthenticatorDescription desc, ServiceInfo info) {\n            this.desc = desc;\n            this.serviceInfo = info;\n        }\n    }\n\n    private final class AuthenticatorCache {\n        final Map<String, AuthenticatorInfo> authenticators = new HashMap<>();\n    }\n\n    private abstract class Session extends IAccountAuthenticatorResponse.Stub\n            implements IBinder.DeathRecipient, ServiceConnection {\n        final int mUserId;\n        final AuthenticatorInfo mAuthenticatorInfo;\n        private final boolean mStripAuthTokenFromResult;\n        public int mNumResults;\n        IAccountAuthenticator mAuthenticator;\n        private IAccountManagerResponse mResponse;\n        private boolean mExpectActivityLaunch;\n        private long mCreationTime;\n        private String mAccountName;\n        private boolean mAuthDetailsRequired;\n        private boolean mUpdateLastAuthenticatedTime;\n        private int mNumRequestContinued;\n        private int mNumErrors;\n\n\n        Session(IAccountManagerResponse response, int userId, AuthenticatorInfo info, boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName, boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {\n            if (info == null) throw new IllegalArgumentException(\"accountType is null\");\n            this.mStripAuthTokenFromResult = stripAuthTokenFromResult;\n            this.mResponse = response;\n            this.mUserId = userId;\n            this.mAuthenticatorInfo = info;\n            this.mExpectActivityLaunch = expectActivityLaunch;\n            this.mCreationTime = SystemClock.elapsedRealtime();\n            this.mAccountName = accountName;\n            this.mAuthDetailsRequired = authDetailsRequired;\n            this.mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;\n            synchronized (mSessions) {\n                mSessions.put(toString(), this);\n            }\n            if (response != null) {\n                try {\n                    response.asBinder().linkToDeath(this, 0 /* flags */);\n                } catch (RemoteException e) {\n                    mResponse = null;\n                    binderDied();\n                }\n            }\n        }\n\n        Session(IAccountManagerResponse response, int userId, AuthenticatorInfo info, boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName) {\n            this(response, userId, info, expectActivityLaunch, stripAuthTokenFromResult, accountName, false, false);\n        }\n\n        IAccountManagerResponse getResponseAndClose() {\n            if (mResponse == null) {\n                // this session has already been closed\n                return null;\n            }\n            IAccountManagerResponse response = mResponse;\n            close(); // this clears mResponse so we need to save the response before this call\n            return response;\n        }\n\n        private void close() {\n            synchronized (mSessions) {\n                if (mSessions.remove(toString()) == null) {\n                    // the session was already closed, so bail out now\n                    return;\n                }\n            }\n            if (mResponse != null) {\n                // stop listening for response deaths\n                mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);\n\n                // clear this so that we don't accidentally send any further results\n                mResponse = null;\n            }\n            unbind();\n        }\n\n        @Override\n        public void onServiceConnected(ComponentName name, IBinder service) {\n            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);\n            try {\n                run();\n            } catch (RemoteException e) {\n                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,\n                        \"remote exception\");\n            }\n        }\n\n        @Override\n        public void onRequestContinued() {\n            mNumRequestContinued++;\n        }\n\n        @Override\n        public void onError(int errorCode, String errorMessage) {\n            mNumErrors++;\n            IAccountManagerResponse response = getResponseAndClose();\n            if (response != null) {\n                Log.v(TAG, getClass().getSimpleName()\n                        + \" calling onError() on response \" + response);\n                try {\n                    response.onError(errorCode, errorMessage);\n                } catch (RemoteException e) {\n                    Log.v(TAG, \"Session.onError: caught RemoteException while responding\", e);\n                }\n            } else {\n                Log.v(TAG, \"Session.onError: already closed\");\n            }\n        }\n\n        @Override\n        public void onServiceDisconnected(ComponentName name) {\n            mAuthenticator = null;\n            IAccountManagerResponse response = getResponseAndClose();\n            if (response != null) {\n                try {\n                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,\n                            \"disconnected\");\n                } catch (RemoteException e) {\n                    Log.v(TAG, \"Session.onServiceDisconnected: \"\n                            + \"caught RemoteException while responding\", e);\n                }\n            }\n        }\n\n        @Override\n        public void onResult(Bundle result) throws RemoteException {\n            mNumResults++;\n            if (result != null) {\n                boolean isSuccessfulConfirmCreds = result.getBoolean(\n                        AccountManager.KEY_BOOLEAN_RESULT, false);\n                boolean isSuccessfulUpdateCredsOrAddAccount =\n                        result.containsKey(AccountManager.KEY_ACCOUNT_NAME)\n                                && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);\n                // We should only update lastAuthenticated time, if\n                // mUpdateLastAuthenticatedTime is true and the confirmRequest\n                // or updateRequest was successful\n                boolean needUpdate = mUpdateLastAuthenticatedTime\n                        && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);\n                if (needUpdate || mAuthDetailsRequired) {\n                    synchronized (accountsByUserId) {\n                        VAccount account = getAccount(mUserId, mAccountName, mAuthenticatorInfo.desc.type);\n                        if (needUpdate && account != null) {\n                            account.lastAuthenticatedTime = System.currentTimeMillis();\n                            saveAllAccounts();\n                        }\n                        if (mAuthDetailsRequired) {\n                            long lastAuthenticatedTime = -1;\n                            if (account != null) {\n                                lastAuthenticatedTime = account.lastAuthenticatedTime;\n                            }\n                            result.putLong(AccountManagerCompat.KEY_LAST_AUTHENTICATED_TIME, lastAuthenticatedTime);\n                        }\n                    }\n                }\n            }\n            if (result != null\n                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {\n//\t\t\t\tString accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);\n//\t\t\t\tString accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);\n//\t\t\t\tif (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {\n//\t\t\t\t\tAccount account = new Account(accountName, accountType);\n//\t\t\t\t\tFIXME: Cancel Notification\n//\t\t\t\t}\n            }\n            Intent intent = null;\n            if (result != null) {\n                intent = result.getParcelable(AccountManager.KEY_INTENT);\n            }\n            IAccountManagerResponse response;\n            if (mExpectActivityLaunch && result != null\n                    && result.containsKey(AccountManager.KEY_INTENT)) {\n                response = mResponse;\n            } else {\n                response = getResponseAndClose();\n            }\n            if (response != null) {\n                try {\n                    if (result == null) {\n                        Log.v(TAG, getClass().getSimpleName()\n                                + \" calling onError() on response \" + response);\n                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,\n                                \"null bundle returned\");\n                    } else {\n                        if (mStripAuthTokenFromResult) {\n                            result.remove(AccountManager.KEY_AUTHTOKEN);\n                        }\n                        Log.v(TAG, getClass().getSimpleName()\n                                + \" calling onResult() on response \" + response);\n                        if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&\n                                (intent == null)) {\n                            // All AccountManager error codes are greater than 0\n                            response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),\n                                    result.getString(AccountManager.KEY_ERROR_MESSAGE));\n                        } else {\n                            response.onResult(result);\n                        }\n                    }\n                } catch (RemoteException e) {\n                    // if the caller is dead then there is no one to care about remote exceptions\n                    Log.v(TAG, \"failure while notifying response\", e);\n                }\n            }\n        }\n\n        public abstract void run() throws RemoteException;\n\n        void bind() {\n            Log.v(TAG, \"initiating bind to authenticator type \" + mAuthenticatorInfo.desc.type);\n            Intent intent = new Intent();\n            intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);\n            intent.setClassName(mAuthenticatorInfo.serviceInfo.packageName, mAuthenticatorInfo.serviceInfo.name);\n            intent.putExtra(\"_VA_|_user_id_\", mUserId);\n\n            if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) {\n                Log.d(TAG, \"bind attempt failed for \" + toDebugString());\n                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, \"bind failure\");\n            }\n        }\n\n        protected String toDebugString() {\n            return toDebugString(SystemClock.elapsedRealtime());\n        }\n\n        protected String toDebugString(long now) {\n            return \"Session: expectLaunch \" + mExpectActivityLaunch\n                    + \", connected \" + (mAuthenticator != null)\n                    + \", stats (\" + mNumResults + \"/\" + mNumRequestContinued\n                    + \"/\" + mNumErrors + \")\"\n                    + \", lifetime \" + ((now - mCreationTime) / 1000.0);\n        }\n\n        private void unbind() {\n            if (mAuthenticator != null) {\n                mAuthenticator = null;\n                mContext.unbindService(this);\n            }\n        }\n\n        @Override\n        public void binderDied() {\n            mResponse = null;\n            close();\n        }\n    }\n\n    private class GetAccountsByTypeAndFeatureSession extends Session {\n        private final String[] mFeatures;\n        private volatile Account[] mAccountsOfType = null;\n        private volatile ArrayList<Account> mAccountsWithFeatures = null;\n        private volatile int mCurrentAccount = 0;\n\n        public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response, int userId, AuthenticatorInfo info, String[] features) {\n            super(response, userId, info, false /* expectActivityLaunch */,\n                    true /* stripAuthTokenFromResult */, null /* accountName */);\n            mFeatures = features;\n        }\n\n        @Override\n        public void run() throws RemoteException {\n            mAccountsOfType = getAccounts(mUserId, mAuthenticatorInfo.desc.type);\n            // check whether each account matches the requested features\n            mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);\n            mCurrentAccount = 0;\n\n            checkAccount();\n        }\n\n        public void checkAccount() {\n            if (mCurrentAccount >= mAccountsOfType.length) {\n                sendResult();\n                return;\n            }\n\n            final IAccountAuthenticator accountAuthenticator = mAuthenticator;\n            if (accountAuthenticator == null) {\n                // It is possible that the authenticator has died, which is indicated by\n                // mAuthenticator being set to null. If this happens then just abort.\n                // There is no need to send back a result or error in this case since\n                // that already happened when mAuthenticator was cleared.\n                Log.v(TAG, \"checkAccount: aborting session since we are no longer\"\n                        + \" connected to the authenticator, \" + toDebugString());\n                return;\n            }\n            try {\n                accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);\n            } catch (RemoteException e) {\n                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, \"remote exception\");\n            }\n        }\n\n        @Override\n        public void onResult(Bundle result) {\n            mNumResults++;\n            if (result == null) {\n                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, \"null bundle\");\n                return;\n            }\n            if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {\n                mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);\n            }\n            mCurrentAccount++;\n            checkAccount();\n        }\n\n        public void sendResult() {\n            IAccountManagerResponse response = getResponseAndClose();\n            if (response != null) {\n                try {\n                    Account[] accounts = new Account[mAccountsWithFeatures.size()];\n                    for (int i = 0; i < accounts.length; i++) {\n                        accounts[i] = mAccountsWithFeatures.get(i);\n                    }\n                    if (Log.isLoggable(TAG, Log.VERBOSE)) {\n                        Log.v(TAG, getClass().getSimpleName() + \" calling onResult() on response \"\n                                + response);\n                    }\n                    Bundle result = new Bundle();\n                    result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);\n                    response.onResult(result);\n                } catch (RemoteException e) {\n                    // if the caller is dead then there is no one to care about remote exceptions\n                    Log.v(TAG, \"failure while notifying response\", e);\n                }\n            }\n        }\n\n\n        @Override\n        protected String toDebugString(long now) {\n            return super.toDebugString(now) + \", getAccountsByTypeAndFeatures\"\n                    + \", \" + (mFeatures != null ? TextUtils.join(\",\", mFeatures) : null);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/accounts/VContentService.java",
    "content": "package com.lody.virtual.server.accounts;\n\nimport android.accounts.Account;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SyncAdapterType;\nimport android.content.SyncRequest;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.content.res.XmlResourceParser;\nimport android.os.Bundle;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.util.Xml;\n\nimport com.lody.virtual.server.pm.VAppManagerService;\nimport com.lody.virtual.server.pm.VPackageManagerService;\n\nimport org.xmlpull.v1.XmlPullParser;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport mirror.android.content.SyncAdapterTypeN;\nimport mirror.com.android.internal.R_Hide;\n\n/**\n * @author Lody\n */\n\npublic class VContentService {\n\n    private Context mContext;\n    private final SparseArray<Map<VSyncRecord.SyncRecordKey, VSyncRecord>> mRecords = new SparseArray<>();\n    private final Map<String, SyncAdapterInfo> mAppSyncAdapterInfos = new HashMap<>();\n\n    private class SyncAdapterInfo {\n        SyncAdapterType adapterType;\n        ServiceInfo serviceInfo;\n\n        SyncAdapterInfo(SyncAdapterType adapterType, ServiceInfo serviceInfo) {\n            this.adapterType = adapterType;\n            this.serviceInfo = serviceInfo;\n        }\n    }\n\n    public void refreshServiceCache(String packageName) {\n        Intent intent = new Intent(\"android.content.SyncAdapter\");\n        if (packageName != null) {\n            intent.setPackage(packageName);\n        }\n        generateServicesMap(\n                VPackageManagerService.get().queryIntentServices(\n                        intent, null, PackageManager.GET_META_DATA, 0\n                ),\n                mAppSyncAdapterInfos,\n                new RegisteredServicesParser()\n        );\n    }\n\n    public void syncAsUser(SyncRequest request, int userId) {\n        Account account = mirror.android.content.SyncRequest.mAccountToSync.get(request);\n        String authority = mirror.android.content.SyncRequest.mAuthority.get(request);\n        Bundle extras = mirror.android.content.SyncRequest.mExtras.get(request);\n        boolean isPeriodic = mirror.android.content.SyncRequest.mIsPeriodic.get(request);\n        long syncRunTimeSecs = mirror.android.content.SyncRequest.mSyncRunTimeSecs.get(request);\n        if (!isAccountExist(userId, account, authority)) {\n            return;\n        }\n        VSyncRecord.SyncRecordKey key = new VSyncRecord.SyncRecordKey(account, authority);\n        VSyncRecord.SyncExtras syncExtras = new VSyncRecord.SyncExtras(extras);\n        int isSyncable = getIsSyncableAsUser(account, authority, userId);\n        synchronized (mRecords) {\n            Map<VSyncRecord.SyncRecordKey, VSyncRecord> map = mRecords.get(userId);\n            if (map == null) {\n                map = new HashMap<>();\n                mRecords.put(userId, map);\n            }\n            VSyncRecord record = map.get(key);\n            if (record == null) {\n                record = new VSyncRecord(userId, account, authority);\n                map.put(key, record);\n            }\n            if (isSyncable < 0) {\n                // Initialisation sync.\n                Bundle newExtras = new Bundle();\n                newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);\n                record.extras.add(new VSyncRecord.SyncExtras(newExtras));\n            }\n            if (isPeriodic) {\n                VSyncRecord.PeriodicSyncConfig periodicSyncConfig = new VSyncRecord.PeriodicSyncConfig(syncRunTimeSecs);\n                record.configs.put(syncExtras, periodicSyncConfig);\n            } else {\n                record.extras.add(syncExtras);\n            }\n\n\n        }\n    }\n\n\n    private boolean isAccountExist(int userId, Account account, String providerName) {\n        synchronized (mAppSyncAdapterInfos) {\n            SyncAdapterInfo info = mAppSyncAdapterInfos.get(account.type + \"/\" + providerName);\n            return info != null\n                    && VAppManagerService.get().isAppInstalled(info.serviceInfo.packageName);\n        }\n    }\n\n    public int getIsSyncableAsUser(Account account, String providerName, int userId) {\n        VSyncRecord.SyncRecordKey key = new VSyncRecord.SyncRecordKey(account, providerName);\n        synchronized (mRecords) {\n            Map<VSyncRecord.SyncRecordKey, VSyncRecord> map = mRecords.get(userId);\n            if (map == null) {\n                return -1;\n            }\n            VSyncRecord record = map.get(key);\n            if (record == null) {\n                return -1;\n            }\n            return record.syncable;\n        }\n\n    }\n\n    private void generateServicesMap(List<ResolveInfo> services, Map<String, SyncAdapterInfo> map,\n                                     RegisteredServicesParser accountParser) {\n        for (ResolveInfo info : services) {\n            XmlResourceParser parser = accountParser.getParser(mContext, info.serviceInfo, \"android.content.SyncAdapter\");\n            if (parser != null) {\n                try {\n                    AttributeSet attributeSet = Xml.asAttributeSet(parser);\n                    int type;\n                    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {\n                        // Nothing to do\n                    }\n                    if (\"sync-adapter\".equals(parser.getName())) {\n                        SyncAdapterType adapterType = parseSyncAdapterType(\n                                accountParser.getResources(mContext, info.serviceInfo.applicationInfo), attributeSet);\n                        if (adapterType != null) {\n                            String key = adapterType.accountType + \"/\" + adapterType.authority;\n                            map.put(key, new SyncAdapterInfo(adapterType, info.serviceInfo));\n                        }\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    private SyncAdapterType parseSyncAdapterType(Resources res, AttributeSet set) {\n        TypedArray obtainAttributes = res.obtainAttributes(set, R_Hide.styleable.SyncAdapter.get());\n        try {\n            String contentAuthority = obtainAttributes.getString(R_Hide.styleable.SyncAdapter_contentAuthority.get());\n            String accountType = obtainAttributes.getString(R_Hide.styleable.SyncAdapter_accountType.get());\n            if (contentAuthority == null || accountType == null) {\n                obtainAttributes.recycle();\n                return null;\n            }\n            boolean userVisible = obtainAttributes.getBoolean(R_Hide.styleable.SyncAdapter_userVisible.get(), true);\n            boolean supportsUploading = obtainAttributes.getBoolean(R_Hide.styleable.SyncAdapter_supportsUploading.get(), true);\n            boolean isAlwaysSyncable = obtainAttributes.getBoolean(R_Hide.styleable.SyncAdapter_isAlwaysSyncable.get(), true);\n            boolean allowParallelSyncs = obtainAttributes.getBoolean(R_Hide.styleable.SyncAdapter_allowParallelSyncs.get(), true);\n            String settingsActivity = obtainAttributes.getString(R_Hide.styleable.SyncAdapter_settingsActivity.get());\n            SyncAdapterType type;\n            if (SyncAdapterTypeN.ctor != null) {\n                type = SyncAdapterTypeN.ctor.newInstance(contentAuthority, accountType, userVisible, supportsUploading, isAlwaysSyncable, allowParallelSyncs, settingsActivity, null);\n                obtainAttributes.recycle();\n                return type;\n            }\n            type = mirror.android.content.SyncAdapterType.ctor.newInstance(contentAuthority, accountType, userVisible, supportsUploading, isAlwaysSyncable, allowParallelSyncs, settingsActivity);\n            obtainAttributes.recycle();\n            return type;\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/accounts/VSyncRecord.java",
    "content": "package com.lody.virtual.server.accounts;\n\nimport android.accounts.Account;\nimport android.os.Bundle;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class VSyncRecord {\n\n    public int userId;\n    public SyncRecordKey key;\n    public int syncable = -1;\n    public boolean isPeriodic = false;\n    public Map<SyncExtras, PeriodicSyncConfig> configs = new HashMap<>();\n    public List<SyncExtras> extras = new ArrayList<>();\n\n    public VSyncRecord(int userId, Account account, String authority) {\n        this.userId = userId;\n        key = new SyncRecordKey(account, authority);\n    }\n\n    public static class SyncExtras implements Parcelable {\n        Bundle extras;\n\n        public SyncExtras(Bundle extras) {\n            this.extras = extras;\n        }\n\n        SyncExtras(Parcel in) {\n            this.extras = in.readBundle(getClass().getClassLoader());\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeBundle(this.extras);\n        }\n\n        public static final Parcelable.Creator<SyncExtras> CREATOR = new Parcelable.Creator<SyncExtras>() {\n            @Override\n            public SyncExtras createFromParcel(Parcel source) {\n                return new SyncExtras(source);\n            }\n\n            @Override\n            public SyncExtras[] newArray(int size) {\n                return new SyncExtras[size];\n            }\n        };\n\n        @Override\n        public boolean equals(Object obj) {\n            return VSyncRecord.equals(this.extras, ((SyncExtras) obj).extras, false);\n        }\n    }\n\n    public static class SyncRecordKey implements Parcelable {\n\n        Account account;\n        String authority;\n\n        SyncRecordKey(Account account, String authority) {\n            this.account = account;\n            this.authority = authority;\n        }\n\n        SyncRecordKey(Parcel in) {\n            this.account = in.readParcelable(Account.class.getClassLoader());\n            this.authority = in.readString();\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeParcelable(this.account, flags);\n            dest.writeString(this.authority);\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n\n            SyncRecordKey that = (SyncRecordKey) o;\n\n            if (account != null ? !account.equals(that.account) : that.account != null)\n                return false;\n            return authority != null ? authority.equals(that.authority) : that.authority == null;\n        }\n\n        public static final Parcelable.Creator<SyncRecordKey> CREATOR = new Parcelable.Creator<SyncRecordKey>() {\n            @Override\n            public SyncRecordKey createFromParcel(Parcel source) {\n                return new SyncRecordKey(source);\n            }\n\n            @Override\n            public SyncRecordKey[] newArray(int size) {\n                return new SyncRecordKey[size];\n            }\n        };\n    }\n\n    public static boolean equals(Bundle a, Bundle b, boolean sameSize) {\n        if (a == b) {\n            return true;\n        }\n        if (sameSize && a.size() != b.size()) {\n            return false;\n        }\n        if (a.size() <= b.size()) {\n            Bundle smaller = a;\n            a = b;\n            b = smaller;\n        }\n        for (String key : a.keySet()) {\n            if (sameSize || !isIgnoredKey(key)) {\n                if (!b.containsKey(key)) {\n                    return false;\n                }\n                //noinspection ConstantConditions\n                if (!a.get(key).equals(b.get(key))) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    static class PeriodicSyncConfig implements Parcelable {\n\n        long syncRunTimeSecs;\n\n        public PeriodicSyncConfig(long syncRunTimeSecs) {\n            this.syncRunTimeSecs = syncRunTimeSecs;\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeLong(this.syncRunTimeSecs);\n        }\n\n        PeriodicSyncConfig(Parcel in) {\n            this.syncRunTimeSecs = in.readLong();\n        }\n\n        public static final Parcelable.Creator<PeriodicSyncConfig> CREATOR = new Parcelable.Creator<PeriodicSyncConfig>() {\n            @Override\n            public PeriodicSyncConfig createFromParcel(Parcel source) {\n                return new PeriodicSyncConfig(source);\n            }\n\n            @Override\n            public PeriodicSyncConfig[] newArray(int size) {\n                return new PeriodicSyncConfig[size];\n            }\n        };\n    }\n\n    private static boolean isIgnoredKey(String str) {\n        return str.equals(\"expedited\")\n                || str.equals(\"ignore_settings\")\n                || str.equals(\"ignore_backoff\")\n                || str.equals(\"do_not_retry\")\n                || str.equals(\"force\")\n                || str.equals(\"upload\")\n                || str.equals(\"deletions_override\")\n                || str.equals(\"discard_deletions\")\n                || str.equals(\"expected_upload\")\n                || str.equals(\"expected_download\")\n                || str.equals(\"sync_priority\")\n                || str.equals(\"allow_metered\")\n                || str.equals(\"initialize\");\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/ActivityRecord.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.content.ComponentName;\nimport android.os.IBinder;\n\n/**\n * @author Lody\n */\n\n/* package */ class ActivityRecord {\n\tpublic TaskRecord task;\n\tpublic ComponentName component;\n\tpublic ComponentName caller;\n\t// Client App 中 Activity 的句柄\n\tpublic IBinder token;\n\tpublic int userId;\n\tpublic ProcessRecord process;\n\tpublic int launchMode;\n\tpublic int flags;\n\tpublic boolean marked;\n\tpublic String affinity;\n\n\tpublic ActivityRecord(TaskRecord task, ComponentName component, ComponentName caller, IBinder token, int userId, ProcessRecord process, int launchMode, int flags, String affinity) {\n\t\tthis.task = task;\n\t\tthis.component = component;\n\t\tthis.caller = caller;\n\t\tthis.token = token;\n\t\tthis.userId = userId;\n\t\tthis.process = process;\n\t\tthis.launchMode = launchMode;\n\t\tthis.flags = flags;\n\t\tthis.affinity = affinity;\n\t}\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/ActivityStack.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.app.ActivityManager;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.util.Log;\nimport android.util.SparseArray;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.ClassUtils;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.remote.AppTaskInfo;\nimport com.lody.virtual.remote.StubActivityRecord;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.ListIterator;\n\nimport mirror.android.app.ActivityManagerNative;\nimport mirror.android.app.ActivityThread;\nimport mirror.android.app.IActivityManager;\nimport mirror.android.app.IApplicationThread;\nimport mirror.com.android.internal.R_Hide;\n\nimport static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;\nimport static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;\nimport static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;\n\n/**\n * @author Lody\n */\n// 自己实现的 ActivityStack 管理 Host 中的所有程序\n/* package */ class ActivityStack {\n\n    private final ActivityManager mAM;\n    private final VActivityManagerService mService;\n\n    /**\n     * [Key] = TaskId [Value] = TaskRecord\n     */\n    private final SparseArray<TaskRecord> mHistory = new SparseArray<>();\n\n\n    ActivityStack(VActivityManagerService mService) {\n        this.mService = mService;\n        mAM = (ActivityManager) VirtualCore.get().getContext().getSystemService(Context.ACTIVITY_SERVICE);\n    }\n\n    private static void removeFlags(Intent intent, int flags) {\n        intent.setFlags(intent.getFlags() & ~flags);\n    }\n\n    private static boolean containFlags(Intent intent, int flags) {\n        return (intent.getFlags() & flags) != 0;\n    }\n\n    private static ActivityRecord topActivityInTask(TaskRecord task) {\n        synchronized (task.activities) {\n            for (int size = task.activities.size() - 1; size >= 0; size--) {\n                ActivityRecord r = task.activities.get(size);\n                if (!r.marked) {\n                    return r;\n                }\n            }\n            return null;\n        }\n    }\n\n\n    private void deliverNewIntentLocked(ActivityRecord sourceRecord, ActivityRecord targetRecord, Intent intent) {\n        if (targetRecord == null) {\n            return;\n        }\n        String creator = sourceRecord != null ? sourceRecord.component.getPackageName() : \"android\";\n        try {\n            targetRecord.process.client.scheduleNewIntent(creator, targetRecord.token, intent);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        } catch (NullPointerException npe) {\n            npe.printStackTrace();\n        }\n    }\n\n    private TaskRecord findTaskByAffinityLocked(int userId, String affinity) {\n        for (int i = 0; i < this.mHistory.size(); i++) {\n            TaskRecord r = this.mHistory.valueAt(i);\n            if (userId == r.userId && affinity.equals(r.affinity)) {\n                return r;\n            }\n        }\n        return null;\n    }\n\n    private TaskRecord findTaskByIntentLocked(int userId, Intent intent) {\n        for (int i = 0; i < this.mHistory.size(); i++) {\n            TaskRecord r = this.mHistory.valueAt(i);\n            if (userId == r.userId && r.taskRoot != null && intent.getComponent().equals(r.taskRoot.getComponent())) {\n                return r;\n            }\n        }\n        return null;\n    }\n\n    private ActivityRecord findActivityByToken(int userId, IBinder token) {\n        ActivityRecord target = null;\n        if (token != null) {\n            for (int i = 0; i < this.mHistory.size(); i++) {\n                TaskRecord task = this.mHistory.valueAt(i);\n                if (task.userId != userId) {\n                    continue;\n                }\n                synchronized (task.activities) {\n                    for (ActivityRecord r : task.activities) {\n                        if (r.token == token) {\n                            target = r;\n                        }\n                    }\n                }\n            }\n        }\n        return target;\n    }\n\n    private boolean markTaskByClearTarget(TaskRecord task, ClearTarget clearTarget, ComponentName component) {\n        boolean marked = false;\n        synchronized (task.activities) {\n            switch (clearTarget) {\n                case TASK: {\n                    for (ActivityRecord r : task.activities) {\n                        r.marked = true;\n                        marked = true;\n                    }\n                }\n                break;\n                case SPEC_ACTIVITY: {\n                    for (ActivityRecord r : task.activities) {\n                        if (r.component.equals(component)) {\n                            r.marked = true;\n                            marked = true;\n                        }\n                    }\n                }\n                break;\n                case TOP: {\n                    int N = task.activities.size();\n                    while (N-- > 0) {\n                        ActivityRecord r = task.activities.get(N);\n                        if (r.component.equals(component)) {\n                            marked = true;\n                            break;\n                        }\n                    }\n                    if (marked) {\n                        while (N++ < task.activities.size() - 1) {\n                            task.activities.get(N).marked = true;\n                        }\n                    }\n                }\n                break;\n            }\n        }\n        return marked;\n    }\n\n    /**\n     * App started in VA may be removed in OverView screen, then AMS.removeTask\n     * will be invoked, all data struct about the task in AMS are released,\n     * while the client's process is still alive. So remove related data in VA\n     * as well. A new TaskRecord will be recreated in `onActivityCreated`\n     */\n    private void optimizeTasksLocked() {\n        // noinspection deprecation\n        ArrayList<ActivityManager.RecentTaskInfo> recentTask = new ArrayList<>(mAM.getRecentTasks(Integer.MAX_VALUE,\n                ActivityManager.RECENT_WITH_EXCLUDED | ActivityManager.RECENT_IGNORE_UNAVAILABLE));\n        int N = mHistory.size();\n        while (N-- > 0) {\n            TaskRecord task = mHistory.valueAt(N);\n            ListIterator<ActivityManager.RecentTaskInfo> iterator = recentTask.listIterator();\n            boolean taskAlive = false;\n            while (iterator.hasNext()) {\n                ActivityManager.RecentTaskInfo info = iterator.next();\n                if (info.id == task.taskId) {\n                    taskAlive = true;\n                    iterator.remove();\n                    break;\n                }\n            }\n            if (!taskAlive) {\n                mHistory.removeAt(N);\n            }\n        }\n    }\n\n\n    int startActivitiesLocked(int userId, Intent[] intents, ActivityInfo[] infos, String[] resolvedTypes, IBinder token, Bundle options) {\n        optimizeTasksLocked();\n        ReuseTarget reuseTarget = ReuseTarget.CURRENT;\n        Intent intent = intents[0];\n        ActivityInfo info = infos[0];\n        ActivityRecord resultTo = findActivityByToken(userId, token);\n        if (resultTo != null && resultTo.launchMode == LAUNCH_SINGLE_INSTANCE) {\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        }\n        if (containFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TOP)) {\n            removeFlags(intent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);\n        }\n        if (containFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK) && !containFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK)) {\n            removeFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK);\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            switch (info.documentLaunchMode) {\n                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:\n                    reuseTarget = ReuseTarget.DOCUMENT;\n                    break;\n                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:\n                    reuseTarget = ReuseTarget.MULTIPLE;\n                    break;\n            }\n        }\n        if (containFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK)) {\n            reuseTarget = containFlags(intent, Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ? ReuseTarget.MULTIPLE : ReuseTarget.AFFINITY;\n        } else if (info.launchMode == LAUNCH_SINGLE_TASK) {\n            reuseTarget = containFlags(intent, Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ? ReuseTarget.MULTIPLE : ReuseTarget.AFFINITY;\n        }\n        if (resultTo == null && reuseTarget == ReuseTarget.CURRENT) {\n            reuseTarget = ReuseTarget.AFFINITY;\n        }\n        String affinity = ComponentUtils.getTaskAffinity(info);\n        TaskRecord reuseTask = null;\n        if (reuseTarget == ReuseTarget.AFFINITY) {\n            reuseTask = findTaskByAffinityLocked(userId, affinity);\n        } else if (reuseTarget == ReuseTarget.CURRENT) {\n            reuseTask = resultTo.task;\n        } else if (reuseTarget == ReuseTarget.DOCUMENT) {\n            reuseTask = findTaskByIntentLocked(userId, intent);\n        }\n        Intent[] destIntents = startActivitiesProcess(userId, intents, infos, resultTo);\n        if (reuseTask == null) {\n            realStartActivitiesLocked(null, destIntents, resolvedTypes, options);\n        } else {\n            ActivityRecord top = topActivityInTask(reuseTask);\n            if (top != null) {\n                realStartActivitiesLocked(top.token, destIntents, resolvedTypes, options);\n            }\n        }\n        return 0;\n    }\n\n    private Intent[] startActivitiesProcess(int userId, Intent[] intents, ActivityInfo[] infos, ActivityRecord resultTo) {\n        Intent[] destIntents = new Intent[intents.length];\n        for (int i = 0; i < intents.length; i++) {\n            destIntents[i] = startActivityProcess(userId, resultTo, intents[i], infos[i]);\n        }\n        return destIntents;\n    }\n\n    // 参考 framework 的实现\n    int startActivityLocked(int userId, Intent intent, ActivityInfo info, IBinder resultTo, Bundle options,\n                            String resultWho, int requestCode) {\n        optimizeTasksLocked();\n\n        Intent destIntent;\n        ActivityRecord sourceRecord = findActivityByToken(userId, resultTo);\n        TaskRecord sourceTask = sourceRecord != null ? sourceRecord.task : null;\n\n        ReuseTarget reuseTarget = ReuseTarget.CURRENT;\n        ClearTarget clearTarget = ClearTarget.NOTHING;\n        boolean clearTop = containFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TOP);\n        boolean clearTask = containFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK);\n\n        if (intent.getComponent() == null) {\n            intent.setComponent(new ComponentName(info.packageName, info.name));\n        }\n        if (sourceRecord != null && sourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        }\n        if (clearTop) {\n            removeFlags(intent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);\n            clearTarget = ClearTarget.TOP;\n        }\n        if (clearTask) {\n            if (containFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK)) {\n                clearTarget = ClearTarget.TASK;\n            } else {\n                removeFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK);\n            }\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            switch (info.documentLaunchMode) {\n                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:\n                    clearTarget = ClearTarget.TASK;\n                    reuseTarget = ReuseTarget.DOCUMENT;\n                    break;\n                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:\n                    reuseTarget = ReuseTarget.MULTIPLE;\n                    break;\n            }\n        }\n        boolean singleTop = false;\n\n        switch (info.launchMode) {\n            case LAUNCH_SINGLE_TOP: {\n                singleTop = true;\n                if (containFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK)) {\n                    reuseTarget = containFlags(intent, Intent.FLAG_ACTIVITY_MULTIPLE_TASK)\n                            ? ReuseTarget.MULTIPLE\n                            : ReuseTarget.AFFINITY;\n                }\n            }\n            break;\n            case LAUNCH_SINGLE_TASK: {\n                clearTop = false;\n                clearTarget = ClearTarget.TOP;\n                reuseTarget = containFlags(intent, Intent.FLAG_ACTIVITY_MULTIPLE_TASK)\n                        ? ReuseTarget.MULTIPLE\n                        : ReuseTarget.AFFINITY;\n            }\n            break;\n            case LAUNCH_SINGLE_INSTANCE: {\n                clearTop = false;\n                clearTarget = ClearTarget.TOP;\n                reuseTarget = ReuseTarget.AFFINITY;\n            }\n            break;\n            default: {\n                if (containFlags(intent, Intent.FLAG_ACTIVITY_SINGLE_TOP)) {\n                    singleTop = true;\n                }\n            }\n            break;\n        }\n        if (clearTarget == ClearTarget.NOTHING) {\n            if (containFlags(intent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)) {\n                clearTarget = ClearTarget.SPEC_ACTIVITY;\n            }\n        }\n        if (sourceTask == null && reuseTarget == ReuseTarget.CURRENT) {\n            reuseTarget = ReuseTarget.AFFINITY;\n        }\n\n        String affinity = ComponentUtils.getTaskAffinity(info);\n\n        // 根据 Flag 寻找合适的 Task\n        TaskRecord reuseTask = null;\n        switch (reuseTarget) {\n            case AFFINITY:\n                reuseTask = findTaskByAffinityLocked(userId, affinity);\n                break;\n            case DOCUMENT:\n                reuseTask = findTaskByIntentLocked(userId, intent);\n                break;\n            case CURRENT:\n                reuseTask = sourceTask;\n                break;\n            default:\n                break;\n        }\n\n        boolean taskMarked = false;\n        if (reuseTask == null) {\n            startActivityInNewTaskLocked(userId, intent, info, options);\n        } else {\n            boolean delivered = false;\n            mAM.moveTaskToFront(reuseTask.taskId, 0);\n            boolean startTaskToFront = !clearTask && !clearTop && ComponentUtils.isSameIntent(intent, reuseTask.taskRoot);\n\n            if (clearTarget.deliverIntent || singleTop) {\n                taskMarked = markTaskByClearTarget(reuseTask, clearTarget, intent.getComponent());\n                ActivityRecord topRecord = topActivityInTask(reuseTask);\n                if (clearTop && !singleTop && topRecord != null && taskMarked) {\n                    topRecord.marked = true;\n                }\n                // Target activity is on top\n                if (topRecord != null && !topRecord.marked && topRecord.component.equals(intent.getComponent())) {\n                    deliverNewIntentLocked(sourceRecord, topRecord, intent);\n                    delivered = true;\n                }\n            }\n            if (taskMarked) {\n                synchronized (mHistory) {\n                    scheduleFinishMarkedActivityLocked();\n                }\n            }\n            if (!startTaskToFront) {\n                if (!delivered) {\n                    destIntent = startActivityProcess(userId, sourceRecord, intent, info);\n                    if (destIntent != null) {\n                        startActivityFromSourceTask(reuseTask, destIntent, info, resultWho, requestCode, options);\n                    }\n                }\n            }\n        }\n        return 0;\n    }\n\n    private void startActivityInNewTaskLocked(int userId, Intent intent, ActivityInfo info, Bundle options) {\n        Intent destIntent = startActivityProcess(userId, null, intent, info);\n        if (destIntent != null) {\n            destIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            destIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);\n            destIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);\n\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                // noinspection deprecation\n                destIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);\n            } else {\n                destIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);\n            }\n            // OK 终于调用到了 startActivity 向真正的系统 AMS 请求打开 Host Stub Activity\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                VirtualCore.get().getContext().startActivity(destIntent, options);\n            } else {\n                VirtualCore.get().getContext().startActivity(destIntent);\n            }\n        }\n    }\n\n    private void scheduleFinishMarkedActivityLocked() {\n        int N = mHistory.size();\n        while (N-- > 0) {\n            final TaskRecord task = mHistory.valueAt(N);\n            for (final ActivityRecord r : task.activities) {\n                if (!r.marked) {\n                    continue;\n                }\n                VirtualRuntime.getUIHandler().post(new Runnable() {\n                    @Override\n                    public void run() {\n                        try {\n                            r.process.client.finishActivity(r.token);\n                        } catch (RemoteException e) {\n                            e.printStackTrace();\n                        }\n                    }\n                });\n            }\n        }\n    }\n\n    private void startActivityFromSourceTask(TaskRecord task, Intent intent, ActivityInfo info, String resultWho,\n                                             int requestCode, Bundle options) {\n        ActivityRecord top = task.activities.isEmpty() ? null : task.activities.get(task.activities.size() - 1);\n        if (top != null) {\n            if (startActivityProcess(task.userId, top, intent, info) != null) {\n                realStartActivityLocked(top.token, intent, resultWho, requestCode, options);\n            }\n        }\n    }\n\n\n    private void realStartActivitiesLocked(IBinder resultTo, Intent[] intents, String[] resolvedTypes, Bundle options) {\n        Class<?>[] types = IActivityManager.startActivities.paramList();\n        Object[] args = new Object[types.length];\n        if (types[0] == IApplicationThread.TYPE) {\n            args[0] = ActivityThread.getApplicationThread.call(VirtualCore.mainThread());\n        }\n        int pkgIndex = ArrayUtils.protoIndexOf(types, String.class);\n        int intentsIndex = ArrayUtils.protoIndexOf(types, Intent[].class);\n        int resultToIndex = ArrayUtils.protoIndexOf(types, IBinder.class, 2);\n        int optionsIndex = ArrayUtils.protoIndexOf(types, Bundle.class);\n        int resolvedTypesIndex = intentsIndex + 1;\n        if (pkgIndex != -1) {\n            args[pkgIndex] = VirtualCore.get().getHostPkg();\n        }\n        args[intentsIndex] = intents;\n        args[resultToIndex] = resultTo;\n        args[resolvedTypesIndex] = resolvedTypes;\n        args[optionsIndex] = options;\n        ClassUtils.fixArgs(types, args);\n        IActivityManager.startActivities.call(ActivityManagerNative.getDefault.call(),\n                (Object[]) args);\n    }\n\n    private void realStartActivityLocked(IBinder resultTo, Intent intent, String resultWho, int requestCode,\n                                         Bundle options) {\n        Class<?>[] types = mirror.android.app.IActivityManager.startActivity.paramList();\n        Object[] args = new Object[types.length];\n        if (types[0] == IApplicationThread.TYPE) {\n            args[0] = ActivityThread.getApplicationThread.call(VirtualCore.mainThread());\n        }\n        int intentIndex = ArrayUtils.protoIndexOf(types, Intent.class);\n        int resultToIndex = ArrayUtils.protoIndexOf(types, IBinder.class, 2);\n        int optionsIndex = ArrayUtils.protoIndexOf(types, Bundle.class);\n        int resolvedTypeIndex = intentIndex + 1;\n        int resultWhoIndex = resultToIndex + 1;\n        int requestCodeIndex = resultToIndex + 2;\n\n        args[intentIndex] = intent;\n        args[resultToIndex] = resultTo;\n        args[resultWhoIndex] = resultWho;\n        args[requestCodeIndex] = requestCode;\n        if (optionsIndex != -1) {\n            args[optionsIndex] = options;\n        }\n        args[resolvedTypeIndex] = intent.getType();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            args[intentIndex - 1] = VirtualCore.get().getHostPkg();\n        }\n        ClassUtils.fixArgs(types, args);\n\n        mirror.android.app.IActivityManager.startActivity.call(ActivityManagerNative.getDefault.call(),\n                (Object[]) args);\n    }\n\n    // 获取合适的 StubActivity，返回 StubActivity 全限定名\n    private String fetchStubActivity(int vpid, ActivityInfo targetInfo) {\n\n        boolean isFloating = false;\n        boolean isTranslucent = false;\n        boolean showWallpaper = false;\n        try {\n            int[] R_Styleable_Window = R_Hide.styleable.Window.get();\n            int R_Styleable_Window_windowIsTranslucent = R_Hide.styleable.Window_windowIsTranslucent.get();\n            int R_Styleable_Window_windowIsFloating = R_Hide.styleable.Window_windowIsFloating.get();\n            int R_Styleable_Window_windowShowWallpaper = R_Hide.styleable.Window_windowShowWallpaper.get();\n\n            AttributeCache.Entry ent = AttributeCache.instance().get(targetInfo.packageName, targetInfo.theme,\n                    R_Styleable_Window);\n            if (ent != null && ent.array != null) {\n                showWallpaper = ent.array.getBoolean(R_Styleable_Window_windowShowWallpaper, false);\n                isTranslucent = ent.array.getBoolean(R_Styleable_Window_windowIsTranslucent, false);\n                isFloating = ent.array.getBoolean(R_Styleable_Window_windowIsFloating, false);\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n\n        boolean isDialogStyle = isFloating || isTranslucent || showWallpaper;\n\n        // 根据在 Menifest 中注册的 pid\n        if (isDialogStyle) {\n            return VASettings.getStubDialogName(vpid);\n        } else {\n            return VASettings.getStubActivityName(vpid);\n        }\n    }\n\n    // 使用 Host Stub Activity 的 Intent 包装原 Intent 瞒天过海\n    private Intent startActivityProcess(int userId, ActivityRecord sourceRecord, Intent intent, ActivityInfo info) {\n        intent = new Intent(intent);\n        // 获得 Activity 对应的 ProcessRecorder，如果没有则表示这是 Process 第一个打开的组件，需要初始化 Application\n        ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName);\n        if (targetApp == null) {\n            return null;\n        }\n        Intent targetIntent = new Intent();\n\n        // 根据 Client App 的 PID 获取 StubActivity\n        String stubActivityPath = fetchStubActivity(targetApp.vpid, info);\n\n        Log.e(\"gy\", \"map activity:\" + intent.getComponent().getClassName() + \" -> \" + stubActivityPath);\n\n        targetIntent.setClassName(VirtualCore.get().getHostPkg(), stubActivityPath);\n        ComponentName component = intent.getComponent();\n        if (component == null) {\n            component = ComponentUtils.toComponentName(info);\n        }\n        targetIntent.setType(component.flattenToString());\n        StubActivityRecord saveInstance = new StubActivityRecord(intent, info,\n                sourceRecord != null ? sourceRecord.component : null, userId);\n        saveInstance.saveToIntent(targetIntent);\n        return targetIntent;\n    }\n\n    void onActivityCreated(ProcessRecord targetApp, ComponentName component, ComponentName caller, IBinder token,\n                           Intent taskRoot, String affinity, int taskId, int launchMode, int flags) {\n        synchronized (mHistory) {\n            optimizeTasksLocked();\n            TaskRecord task = mHistory.get(taskId);\n            if (task == null) {\n                task = new TaskRecord(taskId, targetApp.userId, affinity, taskRoot);\n                mHistory.put(taskId, task);\n            }\n            ActivityRecord record = new ActivityRecord(task, component, caller, token, targetApp.userId, targetApp,\n                    launchMode, flags, affinity);\n            synchronized (task.activities) {\n                task.activities.add(record);\n            }\n        }\n    }\n\n    void onActivityResumed(int userId, IBinder token) {\n        synchronized (mHistory) {\n            optimizeTasksLocked();\n            ActivityRecord r = findActivityByToken(userId, token);\n            if (r != null) {\n                synchronized (r.task.activities) {\n                    r.task.activities.remove(r);\n                    r.task.activities.add(r);\n                }\n            }\n        }\n    }\n\n    ActivityRecord onActivityDestroyed(int userId, IBinder token) {\n        synchronized (mHistory) {\n            optimizeTasksLocked();\n            ActivityRecord r = findActivityByToken(userId, token);\n            if (r != null) {\n                synchronized (r.task.activities) {\n                    r.task.activities.remove(r);\n                    // We shouldn't remove task at this point,\n                    // it will be removed by optimizeTasksLocked().\n                }\n            }\n            return r;\n        }\n    }\n\n    void processDied(ProcessRecord record) {\n        synchronized (mHistory) {\n            optimizeTasksLocked();\n            int N = mHistory.size();\n            while (N-- > 0) {\n                TaskRecord task = mHistory.valueAt(N);\n                synchronized (task.activities) {\n                    Iterator<ActivityRecord> iterator = task.activities.iterator();\n                    while (iterator.hasNext()) {\n                        ActivityRecord r = iterator.next();\n                        if (r.process.pid == record.pid) {\n                            iterator.remove();\n                            if (task.activities.isEmpty()) {\n                                mHistory.remove(task.taskId);\n                            }\n                        }\n                    }\n                }\n            }\n\n        }\n    }\n\n    String getPackageForToken(int userId, IBinder token) {\n        synchronized (mHistory) {\n            ActivityRecord r = findActivityByToken(userId, token);\n            if (r != null) {\n                return r.component.getPackageName();\n            }\n            return null;\n        }\n    }\n\n    ComponentName getCallingActivity(int userId, IBinder token) {\n        synchronized (mHistory) {\n            ActivityRecord r = findActivityByToken(userId, token);\n            if (r != null) {\n                return r.caller != null ? r.caller : r.component;\n            }\n            return null;\n        }\n    }\n\n    public String getCallingPackage(int userId, IBinder token) {\n        synchronized (mHistory) {\n            ActivityRecord r = findActivityByToken(userId, token);\n            if (r != null) {\n                return r.caller != null ? r.caller.getPackageName() : \"android\";\n            }\n            return \"android\";\n        }\n    }\n\n    AppTaskInfo getTaskInfo(int taskId) {\n        synchronized (mHistory) {\n            TaskRecord task = mHistory.get(taskId);\n            if (task != null) {\n                return task.getAppTaskInfo();\n            }\n            return null;\n        }\n    }\n\n    ComponentName getActivityClassForToken(int userId, IBinder token) {\n        synchronized (mHistory) {\n            ActivityRecord r = findActivityByToken(userId, token);\n            if (r != null) {\n                return r.component;\n            }\n            return null;\n        }\n    }\n\n    private enum ClearTarget {\n        NOTHING,\n        SPEC_ACTIVITY,\n        TASK(true),\n        TOP(true);\n\n        boolean deliverIntent;\n\n        ClearTarget() {\n            this(false);\n        }\n\n        ClearTarget(boolean deliverIntent) {\n            this.deliverIntent = deliverIntent;\n        }\n    }\n\n    private enum ReuseTarget {\n        CURRENT, AFFINITY, DOCUMENT, MULTIPLE\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/AppBindRecord.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.server.am;\n\nimport java.util.HashSet;\n\n/**\n * An association between a service and one of its client applications.\n */\nfinal class AppBindRecord {\n    final ServiceRecord service;    // The running service.\n    final ServiceRecord.IntentBindRecord intent;  // The intent we are bound to.\n    final ProcessRecord client;     // Who has started/bound the service.\n\n    final HashSet<ConnectionRecord> connections = new HashSet<ConnectionRecord>();\n                                    // All ConnectionRecord for this client.\n\n    AppBindRecord(ServiceRecord _service, ServiceRecord.IntentBindRecord _intent,\n            ProcessRecord _client) {\n        service = _service;\n        intent = _intent;\n        client = _client;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/AttributeCache.java",
    "content": "/*\n**\n** Copyright 2007, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\npackage com.lody.virtual.server.am;\n\nimport java.util.HashMap;\nimport java.util.WeakHashMap;\n\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.util.SparseArray;\n\n/**\n * TODO: This should be better integrated into the system so it doesn't need\n * special calls from the activity manager to clear it.\n */\npublic final class AttributeCache {\n\tprivate static AttributeCache sInstance = null;\n\n\tprivate final Context mContext;\n\tprivate final WeakHashMap<String, Package> mPackages = new WeakHashMap<String, Package>();\n\tprivate final Configuration mConfiguration = new Configuration();\n\n\tpublic AttributeCache(Context context) {\n\t\tmContext = context;\n\t}\n\n\tpublic static void init(Context context) {\n\t\tif (sInstance == null) {\n\t\t\tsInstance = new AttributeCache(context);\n\t\t}\n\t}\n\n\tpublic static AttributeCache instance() {\n\t\treturn sInstance;\n\t}\n\n\tpublic void removePackage(String packageName) {\n\t\tsynchronized (this) {\n\t\t\tmPackages.remove(packageName);\n\t\t}\n\t}\n\n\tpublic void updateConfiguration(Configuration config) {\n\t\tsynchronized (this) {\n\t\t\tint changes = mConfiguration.updateFrom(config);\n\t\t\tif ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_KEYBOARD_HIDDEN\n\t\t\t\t\t| ActivityInfo.CONFIG_ORIENTATION)) != 0) {\n\t\t\t\t// The configurations being masked out are ones that commonly\n\t\t\t\t// change so we don't want flushing the cache... all others\n\t\t\t\t// will flush the cache.\n\t\t\t\tmPackages.clear();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Entry get(String packageName, int resId, int[] styleable) {\n\t\tsynchronized (this) {\n\t\t\tPackage pkg = mPackages.get(packageName);\n\t\t\tHashMap<int[], Entry> map = null;\n\t\t\tEntry ent = null;\n\t\t\tif (pkg != null) {\n\t\t\t\tmap = pkg.mMap.get(resId);\n\t\t\t\tif (map != null) {\n\t\t\t\t\tent = map.get(styleable);\n\t\t\t\t\tif (ent != null) {\n\t\t\t\t\t\treturn ent;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tContext context;\n\t\t\t\ttry {\n\t\t\t\t\tcontext = mContext.createPackageContext(packageName,\n\t\t\t\t\t\t\tContext.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);\n\t\t\t\t\tif (context == null) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t} catch (PackageManager.NameNotFoundException e) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tpkg = new Package(context);\n\t\t\t\tmPackages.put(packageName, pkg);\n\t\t\t}\n\n\t\t\tif (map == null) {\n\t\t\t\tmap = new HashMap<int[], Entry>();\n\t\t\t\tpkg.mMap.put(resId, map);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tent = new Entry(pkg.context, pkg.context.obtainStyledAttributes(resId, styleable));\n\t\t\t\tmap.put(styleable, ent);\n\t\t\t} catch (Resources.NotFoundException e) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn ent;\n\t\t}\n\t}\n\n\tpublic final static class Package {\n\t\tpublic final Context context;\n\t\tprivate final SparseArray<HashMap<int[], Entry>> mMap = new SparseArray<HashMap<int[], Entry>>();\n\n\t\tpublic Package(Context c) {\n\t\t\tcontext = c;\n\t\t}\n\t}\n\n\tpublic final static class Entry {\n\t\tpublic final Context context;\n\t\tpublic final TypedArray array;\n\n\t\tpublic Entry(Context c, TypedArray ta) {\n\t\t\tcontext = c;\n\t\t\tarray = ta;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/BroadcastSystem.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.app.DownloadManager;\nimport android.content.BroadcastReceiver;\nimport android.content.ContentProvider;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ActivityInfo;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Message;\nimport android.util.Log;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.SpecialComponentList;\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.remote.PendingResultData;\nimport com.lody.virtual.server.pm.PackageSetting;\nimport com.lody.virtual.server.pm.VAppManagerService;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport mirror.android.app.ContextImpl;\nimport mirror.android.app.LoadedApkHuaWei;\nimport mirror.android.rms.resource.ReceiverResourceLP;\nimport mirror.android.rms.resource.ReceiverResourceM;\nimport mirror.android.rms.resource.ReceiverResourceN;\n\nimport static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;\n\n/**\n * @author Lody\n */\n\npublic class BroadcastSystem {\n\n    private static final String TAG = BroadcastSystem.class.getSimpleName();\n    /**\n     * MUST < 10000.\n     */\n    private static final int BROADCAST_TIME_OUT = 8500;\n    private static BroadcastSystem gDefault;\n\n    private final ArrayMap<String, List<BroadcastReceiver>> mReceivers = new ArrayMap<>();\n    private final Map<IBinder, BroadcastRecord> mBroadcastRecords = new HashMap<>();\n    private final Context mContext;\n    private final StaticScheduler mScheduler;\n    private final TimeoutHandler mTimeoutHandler;\n    private final VActivityManagerService mAMS;\n    private final VAppManagerService mApp;\n\n    private BroadcastSystem(Context context, VActivityManagerService ams, VAppManagerService app) {\n        this.mContext = context;\n        this.mApp = app;\n        this.mAMS = ams;\n        mScheduler = new StaticScheduler();\n        mTimeoutHandler = new TimeoutHandler();\n        fuckHuaWeiVerifier();\n    }\n\n    public static void attach(VActivityManagerService ams, VAppManagerService app) {\n        if (gDefault != null) {\n            throw new IllegalStateException();\n        }\n        gDefault = new BroadcastSystem(VirtualCore.get().getContext(), ams, app);\n    }\n\n    public static BroadcastSystem get() {\n        return gDefault;\n    }\n\n    /**\n     * FIX ISSUE #171:\n     * java.lang.AssertionError: Register too many Broadcast Receivers\n     * at android.app.LoadedApk.checkRecevierRegisteredLeakLocked(LoadedApk.java:772)\n     * at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:800)\n     * at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1329)\n     * at android.app.ContextImpl.registerReceiver(ContextImpl.java:1309)\n     * at com.lody.virtual.server.am.BroadcastSystem.startApp(BroadcastSystem.java:54)\n     * at com.lody.virtual.server.pm.VAppManagerService.install(VAppManagerService.java:193)\n     * at com.lody.virtual.server.pm.VAppManagerService.preloadAllApps(VAppManagerService.java:98)\n     * at com.lody.virtual.server.pm.VAppManagerService.systemReady(VAppManagerService.java:70)\n     * at com.lody.virtual.server.BinderProvider.onCreate(BinderProvider.java:42)\n     */\n    private void fuckHuaWeiVerifier() {\n\n        if (LoadedApkHuaWei.mReceiverResource != null) {\n            Object packageInfo = ContextImpl.mPackageInfo.get(mContext);\n            if (packageInfo != null) {\n                Object receiverResource = LoadedApkHuaWei.mReceiverResource.get(packageInfo);\n                if (receiverResource != null) {\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                        if (ReceiverResourceN.mWhiteList != null) {\n                            List<String> whiteList = ReceiverResourceN.mWhiteList.get(receiverResource);\n                            List<String> newWhiteList = new ArrayList<>();\n                            // Add our package name to the white list.\n                            newWhiteList.add(mContext.getPackageName());\n                            if (whiteList != null) {\n                                newWhiteList.addAll(whiteList);\n                            }\n                            ReceiverResourceN.mWhiteList.set(receiverResource, newWhiteList);\n                        }\n\n                    } else {\n                        if (ReceiverResourceM.mWhiteList != null) {\n                            String[] whiteList = ReceiverResourceM.mWhiteList.get(receiverResource);\n                            List<String> newWhiteList = new LinkedList<>();\n                            Collections.addAll(newWhiteList, whiteList);\n                            // Add our package name to the white list.\n                            newWhiteList.add(mContext.getPackageName());\n                            ReceiverResourceM.mWhiteList.set(receiverResource, newWhiteList.toArray(new String[newWhiteList.size()]));\n                        } else if (ReceiverResourceLP.mResourceConfig != null) {\n                            // Just clear the ResourceConfig.\n                            ReceiverResourceLP.mResourceConfig.set(receiverResource, null);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    // 静态 Receiver 的注册\n    public void startApp(VPackage p) {\n        PackageSetting setting = (PackageSetting) p.mExtras;\n        // 遍历 Client App 的 Receiver\n        for (VPackage.ActivityComponent receiver : p.receivers) {\n            ActivityInfo info = receiver.info;\n            // 得到对应 Client App 在 VAService 中的记录列表\n            List<BroadcastReceiver> receivers = mReceivers.get(p.packageName);\n            if (receivers == null) {\n                receivers = new ArrayList<>();\n                mReceivers.put(p.packageName, receivers);\n            }\n            // 注册显式意图\n            String componentAction = String.format(\"_VA_%s_%s\", info.packageName, info.name);\n            IntentFilter componentFilter = new IntentFilter(componentAction);\n            BroadcastReceiver r = new StaticBroadcastReceiver(setting.appId, info, componentFilter);\n            mContext.registerReceiver(r, componentFilter, null, mScheduler);\n            // 推入记录\n            receivers.add(r);\n            // 遍历注册隐式意图\n            for (VPackage.ActivityIntentInfo ci : receiver.intents) {\n                IntentFilter cloneFilter = new IntentFilter(ci.filter);\n                SpecialComponentList.protectIntentFilter(cloneFilter);\n                r = new StaticBroadcastReceiver(setting.appId, info, cloneFilter);\n                mContext.registerReceiver(r, cloneFilter, null, mScheduler);\n                // 推入记录\n                receivers.add(r);\n                Log.e(\"gy\",\"register receiver: \" + ci.filter.getAction(0));\n            }\n        }\n    }\n\n\n    public void stopApp(String packageName) {\n        synchronized (mBroadcastRecords) {\n            Iterator<Map.Entry<IBinder, BroadcastRecord>> iterator = mBroadcastRecords.entrySet().iterator();\n            while (iterator.hasNext()) {\n                Map.Entry<IBinder, BroadcastRecord> entry = iterator.next();\n                BroadcastRecord record = entry.getValue();\n                if (record.receiverInfo.packageName.equals(packageName)) {\n                    record.pendingResult.finish();\n                    iterator.remove();\n                }\n            }\n        }\n        synchronized (mReceivers) {\n            List<BroadcastReceiver> receivers = mReceivers.get(packageName);\n            if (receivers != null) {\n                for (BroadcastReceiver r : receivers) {\n                    mContext.unregisterReceiver(r);\n                }\n            }\n            mReceivers.remove(packageName);\n        }\n    }\n\n    void broadcastFinish(PendingResultData res) {\n        synchronized (mBroadcastRecords) {\n            BroadcastRecord record = mBroadcastRecords.remove(res.mToken);\n            if (record == null) {\n                VLog.e(TAG, \"Unable to find the BroadcastRecord by token: \" + res.mToken);\n            }\n        }\n        mTimeoutHandler.removeMessages(0, res.mToken);\n        res.finish();\n    }\n\n    void broadcastSent(int vuid, ActivityInfo receiverInfo, PendingResultData res) {\n        BroadcastRecord record = new BroadcastRecord(vuid, receiverInfo, res);\n        synchronized (mBroadcastRecords) {\n            mBroadcastRecords.put(res.mToken, record);\n        }\n        Message msg = new Message();\n        msg.obj = res.mToken;\n        mTimeoutHandler.sendMessageDelayed(msg, BROADCAST_TIME_OUT);\n    }\n\n    private static final class StaticScheduler extends Handler {\n\n    }\n\n    private static final class BroadcastRecord {\n        int vuid;\n        ActivityInfo receiverInfo;\n        PendingResultData pendingResult;\n\n        BroadcastRecord(int vuid, ActivityInfo receiverInfo, PendingResultData pendingResult) {\n            this.vuid = vuid;\n            this.receiverInfo = receiverInfo;\n            this.pendingResult = pendingResult;\n        }\n    }\n\n    private final class TimeoutHandler extends Handler {\n        @Override\n        public void handleMessage(Message msg) {\n            IBinder token = (IBinder) msg.obj;\n            BroadcastRecord r = mBroadcastRecords.remove(token);\n            if (r != null) {\n                VLog.w(TAG, \"Broadcast timeout, cancel to dispatch it.\");\n                r.pendingResult.finish();\n            }\n        }\n    }\n\n\n    private final class StaticBroadcastReceiver extends BroadcastReceiver {\n        private int appId;\n        private ActivityInfo info;\n        @SuppressWarnings(\"unused\")\n        private IntentFilter filter;\n\n        private StaticBroadcastReceiver(int appId, ActivityInfo info, IntentFilter filter) {\n            this.appId = appId;\n            this.info = info;\n            this.filter = filter;\n        }\n\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            Log.e(\"gy\",\"receive action=:\" + intent.getAction());\n            if (mApp.isBooting()) {\n                return;\n            }\n            if ((intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) != 0 || isInitialStickyBroadcast()) {\n                return;\n            }\n            String privilegePkg = intent.getStringExtra(\"_VA_|_privilege_pkg_\");\n            if (privilegePkg != null && !info.packageName.equals(privilegePkg)) {\n                return;\n            }\n            PendingResult result = goAsync();\n            if (!mAMS.handleStaticBroadcast(appId, info, intent, new PendingResultData(result))) {\n                result.finish();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/ConnectionRecord.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.server.am;\n\nimport android.app.IServiceConnection;\n\n/**\n * Description of a single binding to a service.\n */\nfinal class ConnectionRecord {\n    final AppBindRecord binding;    // The application/service binding.\n    final IServiceConnection conn;  // The client connection.\n    final int flags;                // Binding options.\n    boolean serviceDead;            // Well is it?\n\n    ConnectionRecord(AppBindRecord _binding,\n               IServiceConnection _conn, int _flags) {\n        binding = _binding;\n        conn = _conn;\n        flags = _flags;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/PendingIntents.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.remote.PendingIntentData;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n\n/**\n * @author Lody\n */\npublic final class PendingIntents {\n\n    private final Map<IBinder, PendingIntentData> mLruHistory = new HashMap<>();\n\n    final PendingIntentData getPendingIntent(IBinder binder) {\n        synchronized (mLruHistory) {\n            return mLruHistory.get(binder);\n        }\n    }\n\n    final void addPendingIntent(final IBinder binder, String creator) {\n        synchronized (mLruHistory) {\n            try {\n                binder.linkToDeath(new IBinder.DeathRecipient() {\n                    @Override\n                    public void binderDied() {\n                        binder.unlinkToDeath(this, 0);\n                        mLruHistory.remove(binder);\n                    }\n                }, 0);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            PendingIntentData pendingIntentData = mLruHistory.get(binder);\n            if (pendingIntentData == null) {\n                mLruHistory.put(binder, new PendingIntentData(creator, binder));\n            } else {\n                pendingIntentData.creator = creator;\n            }\n        }\n    }\n\n    final void removePendingIntent(IBinder binder) {\n        synchronized (mLruHistory) {\n            mLruHistory.remove(binder);\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/ProcessMap.java",
    "content": "package com.lody.virtual.server.am;\n\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.helper.collection.SparseArray;\n\nclass ProcessMap<E> {\n\tprivate final ArrayMap<String, SparseArray<E>> mMap = new ArrayMap<>();\n\n\tpublic E get(String name, int uid) {\n\t\tSparseArray<E> uids = mMap.get(name);\n\t\tif (uids == null)\n\t\t\treturn null;\n\t\treturn uids.get(uid);\n\t}\n\n\tpublic E put(String name, int uid, E value) {\n\t\tSparseArray<E> uids = mMap.get(name);\n\t\tif (uids == null) {\n\t\t\tuids = new SparseArray<E>(2);\n\t\t\tmMap.put(name, uids);\n\t\t}\n\t\tuids.put(uid, value);\n\t\treturn value;\n\t}\n\n\tpublic E remove(String name, int uid) {\n\t\tSparseArray<E> uids = mMap.get(name);\n\t\tif (uids != null) {\n\t\t\tfinal E old = uids.removeReturnOld(uid);\n\t\t\tif (uids.size() == 0) {\n\t\t\t\tmMap.remove(name);\n\t\t\t}\n\t\t\treturn old;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic ArrayMap<String, SparseArray<E>> getMap() {\n\t\treturn mMap;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/ProcessRecord.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.content.pm.ApplicationInfo;\nimport android.os.Binder;\nimport android.os.ConditionVariable;\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.IVClient;\nimport com.lody.virtual.os.VUserHandle;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nfinal class ProcessRecord extends Binder implements Comparable<ProcessRecord> {\n\n\tfinal ConditionVariable lock = new ConditionVariable();\n\tpublic final ApplicationInfo info; // all about the first app in the process\n\tfinal public String processName; // name of the process\n\tfinal Set<String> pkgList = new HashSet<>(); // List of packages\n\tpublic IVClient client;\n\tIInterface appThread;\n\tpublic int pid;\n\tpublic int vuid;\n\tpublic int vpid;\n\tpublic int userId;\n\tboolean doneExecuting;\n    int priority;\n\n\tpublic ProcessRecord(ApplicationInfo info, String processName, int vuid, int vpid) {\n\t\tthis.info = info;\n\t\tthis.vuid = vuid;\n\t\tthis.vpid = vpid;\n\t\tthis.userId = VUserHandle.getUserId(vuid);\n\t\tthis.processName = processName;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o)\n\t\t\treturn true;\n\t\tif (o == null || getClass() != o.getClass())\n\t\t\treturn false;\n\t\tProcessRecord record = (ProcessRecord) o;\n\t\treturn processName != null ? processName.equals(record.processName) : record.processName == null;\n\t}\n\n    @Override\n    public int compareTo(ProcessRecord another) {\n        return this.priority - another.priority;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/ServiceRecord.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.app.IServiceConnection;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ServiceInfo;\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport android.app.Notification;\n\npublic class ServiceRecord extends Binder {\n\tfinal HashMap<Intent.FilterComparison, IntentBindRecord> bindings\n\t\t\t= new HashMap<Intent.FilterComparison, IntentBindRecord>();\n\tpublic long activeSince;\n\tpublic long lastActivityTime;\n\tpublic ComponentName name;\n\tpublic ServiceInfo serviceInfo;\n\tpublic int startId;\n\tpublic ProcessRecord process;\n\tfinal HashMap<IBinder, ArrayList<ConnectionRecord>> connections\n\t\t\t= new HashMap<IBinder, ArrayList<ConnectionRecord>>();\n\tpublic Notification foregroundNoti;\n\tpublic int foregroundId;\n\n\tpublic boolean containConnection(IServiceConnection connection) {\n\t\tfor (IntentBindRecord record : bindings.values()) {\n\t\t\tif (record.containConnection(connection)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic IntentBindRecord retrieveIntentBindRecord(Intent intent) {\n\t\tIntent.FilterComparison filter = new Intent.FilterComparison(intent);\n\t\tIntentBindRecord i = bindings.get(filter);\n\t\tif (i == null) {\n\t\t\ti = new IntentBindRecord();\n\t\t\ti.intent = intent;\n\t\t\tbindings.put(filter, i);\n\t\t}\n\n\t\treturn i;\n\t}\n\n\tpublic AppBindRecord retrieveAppBindingLocked(Intent intent, ProcessRecord app) {\n\t\tIntentBindRecord i = retrieveIntentBindRecord(intent);\n\t\tAppBindRecord a = i.apps.get(app);\n\t\tif (a != null) {\n\t\t\treturn a;\n\t\t}\n\t\ta = new AppBindRecord(this, i, app);\n\t\ti.apps.put(app, a);\n\t\treturn a;\n\t}\n\n\tpublic boolean hasAutoCreateConnections() {\n\t\tCollection<ArrayList<ConnectionRecord>> connectionRecords = connections.values();\n\t\tif (connectionRecords == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tIterator<ArrayList<ConnectionRecord>> connectionRecordIterator\n\t\t\t\t= connectionRecords.iterator();\n\t\twhile (connectionRecordIterator.hasNext()) {\n\t\t\tArrayList<ConnectionRecord> cr = connectionRecordIterator.next();\n\t\t\tfor (int i=0; i<cr.size(); i++) {\n\t\t\t\tif ((cr.get(i).flags & Context.BIND_AUTO_CREATE) != 0) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic int getClientCount() {\n\t\treturn bindings.size();\n\t}\n\n\n\tint getConnectionCount() {\n\t\tint count = 0;\n\t\tsynchronized (bindings) {\n\t\t\tfor (IntentBindRecord record : bindings.values()) {\n\t\t\t\tcount += record.connections.size();\n\t\t\t}\n\t\t}\n\t\treturn count;\n\t}\n\n\n\tIntentBindRecord peekBinding(Intent service) {\n\t\tsynchronized (bindings) {\n\t\t\tfor (IntentBindRecord bindRecord : bindings.values()) {\n\t\t\t\tif (bindRecord.intent != null && bindRecord.intent.filterEquals(service)) {\n\t\t\t\t\treturn bindRecord;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tvoid addToBoundIntent(Intent intent, IServiceConnection connection) {\n\t\tIntentBindRecord record = peekBinding(intent);\n\t\tif (record == null) {\n\t\t\trecord = new IntentBindRecord();\n\t\t\trecord.intent = intent;\n\t\t\tsynchronized (bindings) {\n\t\t\t\tIntent.FilterComparison filter = new Intent.FilterComparison(intent);\n\t\t\t\tbindings.put(filter, record);\n\t\t\t}\n\t\t}\n\t\trecord.addConnection(connection);\n\t}\n\n\tpublic static class IntentBindRecord {\n\t\tfinal HashMap<ProcessRecord, AppBindRecord> apps\n\t\t\t\t= new HashMap<ProcessRecord, AppBindRecord>();\n\t\tpublic  final List<IServiceConnection> connections = Collections.synchronizedList(new ArrayList<IServiceConnection>());\n\t\tpublic IBinder binder;\n\t\t/** Set when we have initiated a request for this binder. */\n\t\tboolean requested;\n\t\t/** Set when we still need to tell the service all clients are unbound. */\n\t\tboolean hasBound;\n\t\tIntent intent;\n\t\tpublic boolean doRebind = false;\n\n\t\tint collectFlags() {\n\t\t\tint flags = 0;\n\t\t\tSet<Map.Entry<ProcessRecord,AppBindRecord>> entrySet = apps.entrySet();\n\t\t\tfor (Map.Entry<ProcessRecord,AppBindRecord> app : entrySet) {\n\t\t\t\tif (app.getValue().connections.size() > 0) {\n\t\t\t\t\tfor (ConnectionRecord conn : app.getValue().connections) {\n\t\t\t\t\t\tflags |= conn.flags;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn flags;\n\t\t}\n\n\t\tpublic boolean containConnection(IServiceConnection connection) {\n\t\t\tfor (IServiceConnection con : connections) {\n\t\t\t\tif (con.asBinder() == connection.asBinder()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void addConnection(IServiceConnection connection) {\n\t\t\tif (!containConnection(connection)) {\n\t\t\t\tconnections.add(connection);\n\t\t\t\ttry {\n\t\t\t\t\tconnection.asBinder().linkToDeath(new DeathRecipient(this, connection), 0);\n\t\t\t\t} catch (RemoteException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void removeConnection(IServiceConnection connection) {\n\t\t\tsynchronized (connections) {\n\t\t\t\tIterator<IServiceConnection> iterator = connections.iterator();\n\t\t\t\twhile (iterator.hasNext()) {\n\t\t\t\t\tIServiceConnection conn = iterator.next();\n\t\t\t\t\tif (conn.asBinder() == connection.asBinder()) {\n\t\t\t\t\t\titerator.remove();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class DeathRecipient implements IBinder.DeathRecipient {\n\n\t\tprivate final IntentBindRecord bindRecord;\n\t\tprivate final IServiceConnection connection;\n\n\t\tprivate DeathRecipient(IntentBindRecord bindRecord, IServiceConnection connection) {\n\t\t\tthis.bindRecord = bindRecord;\n\t\t\tthis.connection = connection;\n\t\t}\n\n\t\t@Override\n\t\tpublic void binderDied() {\n\t\t\tbindRecord.removeConnection(connection);\n\t\t\tconnection.asBinder().unlinkToDeath(this, 0);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/TaskRecord.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\n\nimport com.lody.virtual.remote.AppTaskInfo;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author Lody\n */\n\nclass TaskRecord {\n    public final List<ActivityRecord> activities = Collections.synchronizedList(new ArrayList<ActivityRecord>());\n    public int taskId;\n    public int userId;\n    public String affinity;\n    public Intent taskRoot;\n\n    TaskRecord(int taskId, int userId, String affinity, Intent intent) {\n        this.taskId = taskId;\n        this.userId = userId;\n        this.affinity = affinity;\n        this.taskRoot = intent;\n    }\n\n    AppTaskInfo getAppTaskInfo() {\n        int len = activities.size();\n        if (len <= 0) {\n            return null;\n        }\n        ComponentName top = activities.get(len - 1).component;\n        return new AppTaskInfo(taskId, taskRoot, taskRoot.getComponent(), top);\n    }\n\n    public boolean isFinishing() {\n        boolean allFinish = true;\n        for (ActivityRecord r : activities) {\n            if (!r.marked) allFinish = false;\n        }\n        return allFinish;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/UidSystem.java",
    "content": "package com.lody.virtual.server.am;\n\nimport com.lody.virtual.helper.utils.FileUtils;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static android.os.Process.FIRST_APPLICATION_UID;\n\n/**\n * @author Lody\n */\n\npublic class UidSystem {\n\n    private static final String TAG = UidSystem.class.getSimpleName();\n\n    private final HashMap<String, Integer> mSharedUserIdMap = new HashMap<>();\n    private int mFreeUid = FIRST_APPLICATION_UID;\n\n\n    public void initUidList() {\n        mSharedUserIdMap.clear();\n        File uidFile = VEnvironment.getUidListFile();\n        if (!loadUidList(uidFile)) {\n            File bakUidFile = VEnvironment.getBakUidListFile();\n            loadUidList(bakUidFile);\n        }\n    }\n\n    private boolean loadUidList(File uidFile) {\n        if (!uidFile.exists()) {\n            return false;\n        }\n        try {\n            ObjectInputStream is = new ObjectInputStream(new FileInputStream(uidFile));\n            mFreeUid = is.readInt();\n            //noinspection unchecked\n            Map<String, Integer> map = (HashMap<String, Integer>) is.readObject();\n            mSharedUserIdMap.putAll(map);\n            is.close();\n        } catch (Throwable e) {\n            return false;\n        }\n        return true;\n    }\n\n    private void save() {\n        File uidFile = VEnvironment.getUidListFile();\n        File bakUidFile = VEnvironment.getBakUidListFile();\n        if (uidFile.exists()) {\n            if (bakUidFile.exists() && !bakUidFile.delete()) {\n                VLog.w(TAG, \"Warning: Unable to delete the expired file --\\n \" + bakUidFile.getPath());\n            }\n            try {\n                FileUtils.copyFile(uidFile, bakUidFile);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        try {\n            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(uidFile));\n            os.writeInt(mFreeUid);\n            os.writeObject(mSharedUserIdMap);\n            os.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public int getOrCreateUid(VPackage pkg) {\n        String sharedUserId = pkg.mSharedUserId;\n        if (sharedUserId == null) {\n            sharedUserId = pkg.packageName;\n        }\n        Integer uid = mSharedUserIdMap.get(sharedUserId);\n        if (uid != null) {\n            return uid;\n        }\n        int newUid = ++mFreeUid;\n        mSharedUserIdMap.put(sharedUserId, newUid);\n        save();\n        return newUid;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/am/VActivityManagerService.java",
    "content": "package com.lody.virtual.server.am;\n\nimport android.app.ActivityManager;\nimport android.app.IServiceConnection;\nimport android.app.IStopUserCallback;\nimport android.app.Notification;\nimport android.app.NotificationManager;\nimport android.content.BroadcastReceiver;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ServiceInfo;\nimport android.net.Uri;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.Parcel;\nimport android.os.Process;\nimport android.os.RemoteException;\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport com.lody.virtual.client.IVClient;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.SpecialComponentList;\nimport com.lody.virtual.client.ipc.ProviderCall;\nimport com.lody.virtual.client.ipc.VNotificationManager;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.helper.collection.SparseArray;\nimport com.lody.virtual.helper.compat.ActivityManagerCompat;\nimport com.lody.virtual.helper.compat.ApplicationThreadCompat;\nimport com.lody.virtual.helper.compat.BundleCompat;\nimport com.lody.virtual.helper.compat.IApplicationThreadCompat;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VBinder;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.AppTaskInfo;\nimport com.lody.virtual.remote.PendingIntentData;\nimport com.lody.virtual.remote.PendingResultData;\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.IActivityManager;\nimport com.lody.virtual.server.interfaces.IProcessObserver;\nimport com.lody.virtual.server.pm.PackageCacheManager;\nimport com.lody.virtual.server.pm.PackageSetting;\nimport com.lody.virtual.server.pm.VAppManagerService;\nimport com.lody.virtual.server.pm.VPackageManagerService;\nimport com.lody.virtual.server.secondary.BinderDelegateService;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport static android.os.Process.killProcess;\nimport static com.lody.virtual.os.VUserHandle.getUserId;\n\n/**\n * @author Lody\n * 山寨 AMS\n * 远程服务\n */\npublic class  VActivityManagerService extends IActivityManager.Stub {\n\n    private static final boolean BROADCAST_NOT_STARTED_PKG = true;\n\n    private static final AtomicReference<VActivityManagerService> sService = new AtomicReference<>();\n    private static final String TAG = VActivityManagerService.class.getSimpleName();\n    private final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();\n    private final ActivityStack mMainStack = new ActivityStack(this);\n    private final Set<ServiceRecord> mHistory = new HashSet<ServiceRecord>();\n    private final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections\n            = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();\n    private final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();\n    private final PendingIntents mPendingIntents = new PendingIntents();\n    private ActivityManager am = (ActivityManager) VirtualCore.get().getContext()\n            .getSystemService(Context.ACTIVITY_SERVICE);\n    private NotificationManager nm = (NotificationManager) VirtualCore.get().getContext()\n            .getSystemService(Context.NOTIFICATION_SERVICE);\n\n    public static VActivityManagerService get() {\n        return sService.get();\n    }\n\n    public static void systemReady(Context context) {\n        new VActivityManagerService().onCreate(context);\n    }\n\n    private static ServiceInfo resolveServiceInfo(Intent service, int userId) {\n        if (service != null) {\n            ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, userId);\n            if (serviceInfo != null) {\n                return serviceInfo;\n            }\n        }\n        return null;\n    }\n\n    public void onCreate(Context context) {\n        Log.e(\"gy\", \"VAMS start!\");\n        AttributeCache.init(context);\n        PackageManager pm = context.getPackageManager();\n        PackageInfo packageInfo = null;\n        try {\n            packageInfo = pm.getPackageInfo(context.getPackageName(),\n                    PackageManager.GET_ACTIVITIES | PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n\n        if (packageInfo == null) {\n            throw new RuntimeException(\"Unable to found PackageInfo : \" + context.getPackageName());\n        }\n        sService.set(this);\n\n    }\n\n    // 相当于调用到了服务端的\n    @Override\n    public int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) {\n        synchronized (this) {\n            Log.e(\"gy\", \"startActivity : \" + intent.getComponent().getClassName());\n            return mMainStack.startActivityLocked(userId, intent, info, resultTo, options, resultWho, requestCode);\n        }\n    }\n\n    @Override\n    public int startActivities(Intent[] intents, String[] resolvedTypes, IBinder token, Bundle options, int userId) {\n        synchronized (this) {\n            ActivityInfo[] infos = new ActivityInfo[intents.length];\n            for (int i = 0; i < intents.length; i++) {\n                ActivityInfo ai = VirtualCore.get().resolveActivityInfo(intents[i], userId);\n                if (ai == null) {\n                    return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;\n                }\n                infos[i] = ai;\n\n            }\n            return mMainStack.startActivitiesLocked(userId, intents, infos, resolvedTypes, token, options);\n        }\n    }\n\n    @Override\n    public String getPackageForIntentSender(IBinder binder) {\n        PendingIntentData data = mPendingIntents.getPendingIntent(binder);\n        if (data != null) {\n            return data.creator;\n        }\n        return null;\n    }\n\n\n    @Override\n    public PendingIntentData getPendingIntent(IBinder binder) {\n        return mPendingIntents.getPendingIntent(binder);\n    }\n\n    @Override\n    public void addPendingIntent(IBinder binder, String creator) {\n        mPendingIntents.addPendingIntent(binder, creator);\n    }\n\n    @Override\n    public void removePendingIntent(IBinder binder) {\n        mPendingIntents.removePendingIntent(binder);\n    }\n\n    @Override\n    public int getSystemPid() {\n        return VirtualCore.get().myUid();\n    }\n\n    @Override\n    public void onActivityCreated(ComponentName component, ComponentName caller, IBinder token, Intent intent, String affinity, int taskId, int launchMode, int flags) {\n        int pid = Binder.getCallingPid();\n        ProcessRecord targetApp = findProcessLocked(pid);\n        if (targetApp != null) {\n            mMainStack.onActivityCreated(targetApp, component, caller, token, intent, affinity, taskId, launchMode, flags);\n        }\n    }\n\n    @Override\n    public void onActivityResumed(int userId, IBinder token) {\n        mMainStack.onActivityResumed(userId, token);\n    }\n\n    @Override\n    public boolean onActivityDestroyed(int userId, IBinder token) {\n        ActivityRecord r = mMainStack.onActivityDestroyed(userId, token);\n        return r != null;\n    }\n\n    @Override\n    public AppTaskInfo getTaskInfo(int taskId) {\n        return mMainStack.getTaskInfo(taskId);\n    }\n\n    @Override\n    public String getPackageForToken(int userId, IBinder token) {\n        return mMainStack.getPackageForToken(userId, token);\n    }\n\n    @Override\n    public ComponentName getActivityClassForToken(int userId, IBinder token) {\n        return mMainStack.getActivityClassForToken(userId, token);\n    }\n\n\n    private void processDead(ProcessRecord record) {\n        synchronized (mHistory) {\n            Iterator<ServiceRecord> iterator = mHistory.iterator();\n            while (iterator.hasNext()) {\n                ServiceRecord r = iterator.next();\n                if (r.process != null && r.process.pid == record.pid) {\n                    iterator.remove();\n                }\n            }\n            mMainStack.processDied(record);\n        }\n    }\n\n\n    @Override\n    public IBinder acquireProviderClient(int userId, ProviderInfo info) {\n        ProcessRecord callerApp;\n        synchronized (mPidsSelfLocked) {\n            callerApp = findProcessLocked(VBinder.getCallingPid());\n        }\n        if (callerApp == null) {\n            throw new SecurityException(\"Who are you?\");\n        }\n        String processName = info.processName;\n        ProcessRecord r;\n        synchronized (this) {\n            r = startProcessIfNeedLocked(processName, userId, info.packageName);\n        }\n        if (r != null && r.client.asBinder().isBinderAlive()) {\n            try {\n                return r.client.acquireProviderClient(info);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ComponentName getCallingActivity(int userId, IBinder token) {\n        return mMainStack.getCallingActivity(userId, token);\n    }\n\n    @Override\n    public String getCallingPackage(int userId, IBinder token) {\n        return mMainStack.getCallingPackage(userId, token);\n    }\n\n\n    @Override\n    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        try {\n            return super.onTransact(code, data, reply, flags);\n        } catch (Throwable e) {\n            e.printStackTrace();\n            throw e;\n        }\n    }\n\n    private void addRecord(ServiceRecord r) {\n        mHistory.add(r);\n    }\n\n    private ServiceRecord findRecordLocked(int userId, ServiceInfo serviceInfo) {\n        synchronized (mHistory) {\n            for (ServiceRecord r : mHistory) {\n                // If service is not created, and bindService with the flag that is\n                // not BIND_AUTO_CREATE, r.process is null\n                if ((r.process == null || r.process.userId == userId)\n                        && ComponentUtils.isSameComponent(serviceInfo, r.serviceInfo)) {\n                    return r;\n                }\n            }\n            return null;\n        }\n    }\n\n    private ServiceRecord findRecordLocked(IServiceConnection connection) {\n        synchronized (mHistory) {\n            for (ServiceRecord r : mHistory) {\n                if (r.containConnection(connection)) {\n                    return r;\n                }\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public ComponentName startService(IBinder caller, Intent service, String resolvedType, int userId) {\n        synchronized (this) {\n            return startServiceCommon(service, true, userId);\n        }\n    }\n\n    // For the pending bind(with the flag that is not BIND_AUTO_CREATE)\n    private final void requestServiceBindingsLocked(ServiceRecord r) {\n        if (r.bindings == null) {\n            return;\n        }\n        for (ServiceRecord.IntentBindRecord record : r.bindings.values()) {\n            if (!requestServiceBindingLocked(r, record, false)) {\n                break;\n            }\n        }\n    }\n\n    private final boolean requestServiceBindingLocked(ServiceRecord r,\n                                                      ServiceRecord.IntentBindRecord i,\n                                                      boolean rebind) {\n        if (r.process == null || r.process.appThread == null) {\n            // If service is not currently running, can't yet bind.\n            return false;\n        }\n        if ((!i.requested || rebind) && i.apps.size() > 0) {\n            try {\n                IApplicationThreadCompat.scheduleBindService(r.process.appThread, r,\n                        i.intent, rebind, 0);\n                if (!rebind) {\n                    i.requested = true;\n                }\n                i.hasBound = true;\n                i.doRebind = false;\n            } catch (RemoteException e) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private ComponentName startServiceCommon(Intent service,\n                                             boolean scheduleServiceArgs, int userId) {\n        ServiceInfo serviceInfo = resolveServiceInfo(service, userId);\n        if (serviceInfo == null) {\n            return null;\n        }\n        ProcessRecord targetApp = startProcessIfNeedLocked(ComponentUtils.getProcessName(serviceInfo),\n                userId,\n                serviceInfo.packageName);\n\n        if (targetApp == null) {\n            VLog.e(TAG, \"Unable to start new Process for : \" + ComponentUtils.toComponentName(serviceInfo));\n            return null;\n        }\n        IInterface appThread = targetApp.appThread;\n        ServiceRecord r = findRecordLocked(userId, serviceInfo);\n        boolean needCreateService = false;\n        if (r == null) {\n            r = new ServiceRecord();\n            r.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);\n            r.startId = 0;\n            r.activeSince = SystemClock.elapsedRealtime();\n            r.process = targetApp;\n            r.serviceInfo = serviceInfo;\n            needCreateService = true;\n        } else {\n            if (r.process == null) {\n                r.process = targetApp;\n                needCreateService = true;\n            }\n        }\n\n        // 如果 service 尚未创建\n        if (needCreateService) {\n            try {\n                // 调用 ApplicationThread.scheduleCreateService 直接创建 Service\n                IApplicationThreadCompat.scheduleCreateService(appThread, r, r.serviceInfo, 0);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n\n            // Note: If the service has been called for not AUTO_CREATE binding, the corresponding\n            // ServiceRecord is already in mHistory, so we use Set to replace List to avoid add\n            // ServiceRecord twice\n            // 将 ServiceRecorder 推入 history\n            addRecord(r);\n\n            // 等待 bindService，如果是通过 bindService 自动创建的 Service，在创建 Service 完成后会进入 bindService 流程\n            requestServiceBindingsLocked(r);\n        }\n\n        r.lastActivityTime = SystemClock.uptimeMillis();\n        if (scheduleServiceArgs) {\n            r.startId++;\n            boolean taskRemoved = serviceInfo.applicationInfo != null\n                    && serviceInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.ECLAIR;\n            try {\n                IApplicationThreadCompat.scheduleServiceArgs(appThread, r, taskRemoved, r.startId, 0, service);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n        return ComponentUtils.toComponentName(serviceInfo);\n    }\n\n    @Override\n    public int stopService(IBinder caller, Intent service, String resolvedType, int userId) {\n        synchronized (this) {\n            ServiceInfo serviceInfo = resolveServiceInfo(service, userId);\n            if (serviceInfo == null) {\n                return 0;\n            }\n            ServiceRecord r = findRecordLocked(userId, serviceInfo);\n            if (r == null) {\n                return 0;\n            }\n\n            stopServiceCommon(r);\n            return 1;\n        }\n    }\n\n    @Override\n    public boolean stopServiceToken(ComponentName className, IBinder token, int startId, int userId) {\n        synchronized (this) {\n            ServiceRecord r = (ServiceRecord) token;\n            if (r != null && (r.startId == startId || startId == -1)) {\n                stopServiceCommon(r);\n                return true;\n            }\n            return false;\n        }\n    }\n\n    /**\n     * Extracting common method of stopService(see bringDownServiceIfNeededLocked in android source)\n     *\n     * @param r ServiceRecord\n     */\n    private void stopServiceCommon(ServiceRecord r) {\n        if (r.hasAutoCreateConnections()) {\n            return;\n        }\n\n        // Report to all of the connections that the service is no longer\n        // available.\n        if (r.connections != null && !r.connections.isEmpty()) {\n            for (ArrayList<ConnectionRecord> c : r.connections.values()) {\n                for (int i = 0; i < c.size(); i++) {\n                    ConnectionRecord cr = c.get(i);\n                    // There is still a connection to the service that is\n                    // being brought down.  Mark it as dead.\n                    cr.serviceDead = true;\n                    try {\n                        cr.conn.connected(r.name, null);\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n\n        // Tell the service that it has been unbound.\n        if (r.process != null && r.process.appThread != null) {\n            Set<Map.Entry<Intent.FilterComparison, ServiceRecord.IntentBindRecord>> entrySet\n                    = r.bindings.entrySet();\n            for (Map.Entry<Intent.FilterComparison, ServiceRecord.IntentBindRecord> entry\n                    : entrySet) {\n                ServiceRecord.IntentBindRecord ibr = entry.getValue();\n                if (ibr.hasBound) {\n                    try {\n                        ibr.hasBound = false;\n                        IApplicationThreadCompat.scheduleUnbindService(r.process.appThread,\n                                r, ibr.intent);\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n\n        try {\n            if (r.process != null) {\n                IApplicationThreadCompat.scheduleStopService(r.process.appThread, r);\n            }\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        mHistory.remove(r);\n    }\n\n    @Override\n    public void setServiceForeground(ComponentName className, IBinder token, int id, Notification notification,\n                                     boolean removeNotification, int userId) {\n        ServiceRecord r = (ServiceRecord) token;\n        if (r != null) {\n            if (id != 0) {\n                if (notification == null) {\n                    throw new IllegalArgumentException(\"null notification\");\n                }\n                if (r.foregroundId != id) {\n                    if (r.foregroundId != 0) {\n                        cancelNotification(userId, r.foregroundId, r.serviceInfo.packageName);\n                    }\n                    r.foregroundId = id;\n                }\n                r.foregroundNoti = notification;\n                postNotification(userId, id, r.serviceInfo.packageName, notification);\n            } else {\n                if (removeNotification) {\n                    cancelNotification(userId, r.foregroundId, r.serviceInfo.packageName);\n                    r.foregroundId = 0;\n                    r.foregroundNoti = null;\n                }\n            }\n        }\n    }\n\n    private void cancelNotification(int userId, int id, String pkg) {\n        id = VNotificationManager.get().dealNotificationId(id, pkg, null, userId);\n        String tag = VNotificationManager.get().dealNotificationTag(id, pkg, null, userId);\n        nm.cancel(tag, id);\n    }\n\n    private void postNotification(int userId, int id, String pkg, Notification notification) {\n        id = VNotificationManager.get().dealNotificationId(id, pkg, null, userId);\n        String tag = VNotificationManager.get().dealNotificationTag(id, pkg, null, userId);\n        VNotificationManager.get().dealNotification(id, notification, pkg);\n        VNotificationManager.get().addNotification(id, tag, pkg, userId);\n        try {\n            nm.notify(tag, id, notification);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    private ProcessRecord getRecordForAppLocked(IBinder caller, int userId) {\n        synchronized (mProcessNames) {\n            ArrayMap<String, SparseArray<ProcessRecord>> map = mProcessNames.getMap();\n            int N = map.size();\n            while (N-- > 0) {\n                SparseArray<ProcessRecord> uids = map.valueAt(N);\n                for (int i = 0; i < uids.size(); i++) {\n                    ProcessRecord r = uids.valueAt(i);\n                    if (userId != VUserHandle.USER_ALL) {\n                        if (r.userId != userId) {\n                            continue;\n                        }\n                    }\n                    if (caller == r.appThread.asBinder()) {\n                        return r;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public int bindService(IBinder caller, IBinder token, Intent service, String resolvedType,\n                           IServiceConnection connection, int flags, int userId) {\n        synchronized (this) {\n            ServiceInfo serviceInfo = resolveServiceInfo(service, userId);\n            if (serviceInfo == null) {\n                return 0;\n            }\n            ServiceRecord r = findRecordLocked(userId, serviceInfo);\n            boolean firstLaunch = r == null;\n            if (firstLaunch) {\n                if ((flags & Context.BIND_AUTO_CREATE) != 0) {\n                    startServiceCommon(service, false, userId);\n                    r = findRecordLocked(userId, serviceInfo);\n                }\n            }\n            boolean canBind = true;\n            if (r == null) {\n                r = new ServiceRecord();\n                r.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);\n                r.startId = 0;\n                r.activeSince = SystemClock.elapsedRealtime();\n                r.serviceInfo = serviceInfo;\n                ServiceRecord.IntentBindRecord intentBindRecord\n                        = r.retrieveIntentBindRecord(service);\n                intentBindRecord.addConnection(connection);\n                addRecord(r);\n                canBind = false;\n            }\n            ProcessRecord processRecord = getRecordForAppLocked(caller, userId);\n            AppBindRecord b = r.retrieveAppBindingLocked(service, processRecord);\n            ConnectionRecord c = new ConnectionRecord(b, connection, flags);\n            IBinder binder = connection.asBinder();\n            ArrayList<ConnectionRecord> clist = r.connections.get(binder);\n            if (clist == null) {\n                clist = new ArrayList<>();\n                r.connections.put(binder, clist);\n            }\n            clist.add(c);\n            b.connections.add(c);\n\n            clist = mServiceConnections.get(binder);\n            if (clist == null) {\n                clist = new ArrayList<>();\n                mServiceConnections.put(binder, clist);\n            }\n            clist.add(c);\n\n            if (!canBind) {\n                return 0;\n            }\n            ServiceRecord.IntentBindRecord boundRecord = r.peekBinding(service);\n\n            if (boundRecord != null && boundRecord.binder != null && boundRecord.binder.isBinderAlive()) {\n                if (boundRecord.doRebind) {\n                    try {\n                        IApplicationThreadCompat.scheduleBindService(r.process.appThread, r, service, true, 0);\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n                }\n                ComponentName componentName = new ComponentName(r.serviceInfo.packageName, r.serviceInfo.name);\n                connectService(connection, componentName, boundRecord);\n            } else {\n                try {\n                    IApplicationThreadCompat.scheduleBindService(r.process.appThread, r, service, false, 0);\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n            r.lastActivityTime = SystemClock.uptimeMillis();\n            r.addToBoundIntent(service, connection);\n            return 1;\n        }\n    }\n\n    private void removeConnectionLocked(\n            ConnectionRecord c) {\n        IBinder binder = c.conn.asBinder();\n        AppBindRecord b = c.binding;\n        ServiceRecord s = b.service;\n        ArrayList<ConnectionRecord> clist = s.connections.get(binder);\n        if (clist != null) {\n            clist.remove(c);\n            if (clist.size() == 0) {\n                s.connections.remove(binder);\n            }\n        }\n        b.connections.remove(c);\n        clist = mServiceConnections.get(binder);\n        if (clist != null) {\n            clist.remove(c);\n            if (clist.size() == 0) {\n                mServiceConnections.remove(binder);\n            }\n        }\n\n        if (b.connections.size() == 0) {\n            b.intent.apps.remove(b.client);\n        }\n\n        b.intent.removeConnection(c.conn);\n\n        ServiceRecord r = findRecordLocked(c.conn);\n        if (r == null) {\n            return;\n        }\n\n        if (!c.serviceDead) {\n            try {\n                IApplicationThreadCompat.scheduleUnbindService(r.process.appThread, r, b.intent.intent);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    @Override\n    public boolean unbindService(IServiceConnection connection, int userId) {\n        IBinder binder = connection.asBinder();\n        ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);\n        if (clist == null) {\n            return false;\n        } else {\n            while (clist.size() > 0) {\n                ConnectionRecord r = clist.get(0);\n                removeConnectionLocked(r);\n            }\n        }\n\n        return true;\n    }\n\n    @Override\n    public void unbindFinished(IBinder token, Intent service, boolean doRebind, int userId) {\n        synchronized (this) {\n            ServiceRecord r = (ServiceRecord) token;\n            if (r != null) {\n                ServiceRecord.IntentBindRecord boundRecord = r.peekBinding(service);\n                if (boundRecord != null) {\n                    boundRecord.doRebind = doRebind;\n                }\n            }\n        }\n    }\n\n\n    @Override\n    public boolean isVAServiceToken(IBinder token) {\n        return token instanceof ServiceRecord;\n    }\n\n\n    @Override\n    public void serviceDoneExecuting(IBinder token, int type, int startId, int res, int userId) {\n        synchronized (this) {\n            ServiceRecord r = (ServiceRecord) token;\n            if (r == null) {\n                return;\n            }\n            if (ActivityManagerCompat.SERVICE_DONE_EXECUTING_STOP == type) {\n                mHistory.remove(r);\n            }\n        }\n    }\n\n    @Override\n    public IBinder peekService(Intent service, String resolvedType, int userId) {\n        synchronized (this) {\n            ServiceInfo serviceInfo = resolveServiceInfo(service, userId);\n            if (serviceInfo == null) {\n                return null;\n            }\n            ServiceRecord r = findRecordLocked(userId, serviceInfo);\n            if (r != null) {\n                ServiceRecord.IntentBindRecord boundRecord = r.peekBinding(service);\n                if (boundRecord != null) {\n                    return boundRecord.binder;\n                }\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public void publishService(IBinder token, Intent intent, IBinder service, int userId) {\n        synchronized (this) {\n            ServiceRecord r = (ServiceRecord) token;\n            if (r != null) {\n                ServiceRecord.IntentBindRecord boundRecord = r.peekBinding(intent);\n                if (boundRecord != null) {\n                    boundRecord.binder = service;\n                    for (IServiceConnection conn : boundRecord.connections) {\n                        ComponentName component = ComponentUtils.toComponentName(r.serviceInfo);\n                        connectService(conn, component, boundRecord);\n                    }\n                }\n            }\n        }\n    }\n\n    private void connectService(IServiceConnection conn, ComponentName component, ServiceRecord.IntentBindRecord r) {\n        try {\n            BinderDelegateService delegateService = new BinderDelegateService(component, r.binder);\n            conn.connected(component, delegateService);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public VParceledListSlice<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags, int userId) {\n        synchronized (mHistory) {\n            List<ActivityManager.RunningServiceInfo> services = new ArrayList<>(mHistory.size());\n            for (ServiceRecord r : mHistory) {\n                if (r.process == null) {\n                    continue;\n                }\n                if (r.process.userId != userId) {\n                    continue;\n                }\n                ActivityManager.RunningServiceInfo info = new ActivityManager.RunningServiceInfo();\n                info.uid = r.process.vuid;\n                info.pid = r.process.pid;\n                ProcessRecord processRecord = findProcessLocked(r.process.pid);\n                if (processRecord != null) {\n                    info.process = processRecord.processName;\n                    info.clientPackage = processRecord.info.packageName;\n                }\n                info.activeSince = r.activeSince;\n                info.lastActivityTime = r.lastActivityTime;\n                info.clientCount = r.getClientCount();\n                info.service = ComponentUtils.toComponentName(r.serviceInfo);\n                info.started = r.startId > 0;\n                services.add(info);\n            }\n            return new VParceledListSlice<>(services);\n        }\n    }\n\n    @Override\n    public void processRestarted(String packageName, String processName, int userId) {\n        int callingPid = getCallingPid();\n        int appId = VAppManagerService.get().getAppId(packageName);\n        int uid = VUserHandle.getUid(userId, appId);\n        synchronized (this) {\n            ProcessRecord app = findProcessLocked(callingPid);\n            if (app == null) {\n                ApplicationInfo appInfo = VPackageManagerService.get().getApplicationInfo(packageName, 0, userId);\n                appInfo.flags |= ApplicationInfo.FLAG_HAS_CODE;\n                String stubProcessName = getProcessName(callingPid);\n                int vpid = parseVPid(stubProcessName);\n                if (vpid != -1) {\n                    performStartProcessLocked(uid, vpid, appInfo, processName);\n                }\n            }\n        }\n    }\n\n    private int parseVPid(String stubProcessName) {\n        String prefix = VirtualCore.get().getHostPkg() + \":p\";\n        if (stubProcessName != null && stubProcessName.startsWith(prefix)) {\n            try {\n                return Integer.parseInt(stubProcessName.substring(prefix.length()));\n            } catch (NumberFormatException e) {\n                // ignore\n            }\n        }\n        return -1;\n    }\n\n\n    private String getProcessName(int pid) {\n        for (ActivityManager.RunningAppProcessInfo info : am.getRunningAppProcesses()) {\n            if (info.pid == pid) {\n                return info.processName;\n            }\n        }\n        return null;\n    }\n\n    // 将子程序句柄推入服务端 VAMS\n    private void attachClient(int pid, final IBinder clientBinder) {\n        final IVClient client = IVClient.Stub.asInterface(clientBinder);\n        if (client == null) {\n            killProcess(pid);\n            return;\n        }\n        IInterface thread = null;\n        try {\n            thread = ApplicationThreadCompat.asInterface(client.getAppThread());\n        } catch (RemoteException e) {\n            // process has dead\n        }\n        if (thread == null) {\n            killProcess(pid);\n            return;\n        }\n        ProcessRecord app = null;\n        try {\n            IBinder token = client.getToken();\n            if (token instanceof ProcessRecord) {\n                app = (ProcessRecord) token;\n            }\n        } catch (RemoteException e) {\n            // process has dead\n        }\n        if (app == null) {\n            killProcess(pid);\n            return;\n        }\n        try {\n            final ProcessRecord record = app;\n            clientBinder.linkToDeath(new DeathRecipient() {\n                @Override\n                public void binderDied() {\n                    clientBinder.unlinkToDeath(this, 0);\n                    onProcessDead(record);\n                }\n            }, 0);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        app.client = client;\n        app.appThread = thread;\n        app.pid = pid;\n        synchronized (mProcessNames) {\n            mProcessNames.put(app.processName, app.vuid, app);\n            mPidsSelfLocked.put(app.pid, app);\n        }\n    }\n\n    private void onProcessDead(ProcessRecord record) {\n        mProcessNames.remove(record.processName, record.vuid);\n        mPidsSelfLocked.remove(record.pid);\n        processDead(record);\n        record.lock.open();\n    }\n\n    @Override\n    public int getFreeStubCount() {\n        return VASettings.STUB_COUNT - mPidsSelfLocked.size();\n    }\n\n    @Override\n    public int initProcess(String packageName, String processName, int userId) {\n        synchronized (this) {\n            ProcessRecord r = startProcessIfNeedLocked(processName, userId, packageName);\n            return r != null ? r.vpid : -1;\n        }\n    }\n\n    ProcessRecord startProcessIfNeedLocked(String processName, int userId, String packageName) {\n        // Stub 不足则需要释放内存\n        if (VActivityManagerService.get().getFreeStubCount() < 3) {\n            // run GC\n            killAllApps();\n        }\n        PackageSetting ps = PackageCacheManager.getSetting(packageName);\n        ApplicationInfo info = VPackageManagerService.get().getApplicationInfo(packageName, 0, userId);\n        if (ps == null || info == null) {\n            return null;\n        }\n        if (!ps.isLaunched(userId)) {\n            sendFirstLaunchBroadcast(ps, userId);\n            ps.setLaunched(userId, true);\n            VAppManagerService.get().savePersistenceData();\n        }\n        int uid = VUserHandle.getUid(userId, ps.appId);\n        ProcessRecord app = mProcessNames.get(processName, uid);\n        if (app != null && app.client.asBinder().isBinderAlive()) {\n            return app;\n        }\n        int vpid = queryFreeStubProcessLocked();\n        if (vpid == -1) {\n            return null;\n        }\n        app = performStartProcessLocked(uid, vpid, info, processName);\n        if (app != null) {\n            app.pkgList.add(info.packageName);\n        }\n        return app;\n    }\n\n    private void sendFirstLaunchBroadcast(PackageSetting ps, int userId) {\n        Intent intent = new Intent(Intent.ACTION_PACKAGE_FIRST_LAUNCH, Uri.fromParts(\"package\", ps.packageName, null));\n        intent.setPackage(ps.packageName);\n        intent.putExtra(Intent.EXTRA_UID, VUserHandle.getUid(ps.appId, userId));\n        intent.putExtra(\"android.intent.extra.user_handle\", userId);\n        sendBroadcastAsUser(intent, null);\n    }\n\n\n    @Override\n    public int getUidByPid(int pid) {\n        synchronized (mPidsSelfLocked) {\n            ProcessRecord r = findProcessLocked(pid);\n            if (r != null) {\n                return r.vuid;\n            }\n        }\n        return Process.myUid();\n    }\n\n    private ProcessRecord performStartProcessLocked(int vuid, int vpid, ApplicationInfo info, String processName) {\n        ProcessRecord app = new ProcessRecord(info, processName, vuid, vpid);\n        Bundle extras = new Bundle();\n        BundleCompat.putBinder(extras, \"_VA_|_binder_\", app);\n        extras.putInt(\"_VA_|_vuid_\", vuid);\n        extras.putString(\"_VA_|_process_\", processName);\n        extras.putString(\"_VA_|_pkg_\", info.packageName);\n\n        // 调用子程序包的 init_process 方法，并且得到子程序包 IBinder 句柄\n        Bundle res = ProviderCall.call(VASettings.getStubAuthority(vpid), \"_VA_|_init_process_\", null, extras);\n        if (res == null) {\n            return null;\n        }\n        int pid = res.getInt(\"_VA_|_pid_\");\n        IBinder clientBinder = BundleCompat.getBinder(res, \"_VA_|_client_\");\n        // attach 到 Client 的 VAM\n        attachClient(pid, clientBinder);\n        return app;\n    }\n\n    private int queryFreeStubProcessLocked() {\n        for (int vpid = 0; vpid < VASettings.STUB_COUNT; vpid++) {\n            int N = mPidsSelfLocked.size();\n            boolean using = false;\n            while (N-- > 0) {\n                ProcessRecord r = mPidsSelfLocked.valueAt(N);\n                if (r.vpid == vpid) {\n                    using = true;\n                    break;\n                }\n            }\n            if (using) {\n                continue;\n            }\n            return vpid;\n        }\n        return -1;\n    }\n\n    @Override\n    public boolean isAppProcess(String processName) {\n        return parseVPid(processName) != -1;\n    }\n\n    @Override\n    public boolean isAppPid(int pid) {\n        synchronized (mPidsSelfLocked) {\n            return findProcessLocked(pid) != null;\n        }\n    }\n\n    @Override\n    public String getAppProcessName(int pid) {\n        synchronized (mPidsSelfLocked) {\n            ProcessRecord r = mPidsSelfLocked.get(pid);\n            if (r != null) {\n                return r.processName;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public List<String> getProcessPkgList(int pid) {\n        synchronized (mPidsSelfLocked) {\n            ProcessRecord r = mPidsSelfLocked.get(pid);\n            if (r != null) {\n                return new ArrayList<>(r.pkgList);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public void killAllApps() {\n        synchronized (mPidsSelfLocked) {\n            for (int i = 0; i < mPidsSelfLocked.size(); i++) {\n                ProcessRecord r = mPidsSelfLocked.valueAt(i);\n                killProcess(r.pid);\n            }\n        }\n    }\n\n    @Override\n    public void killAppByPkg(final String pkg, int userId) {\n        synchronized (mProcessNames) {\n            ArrayMap<String, SparseArray<ProcessRecord>> map = mProcessNames.getMap();\n            int N = map.size();\n            while (N-- > 0) {\n                SparseArray<ProcessRecord> uids = map.valueAt(N);\n                for (int i = 0; i < uids.size(); i++) {\n                    ProcessRecord r = uids.valueAt(i);\n                    if (userId != VUserHandle.USER_ALL) {\n                        if (r.userId != userId) {\n                            continue;\n                        }\n                    }\n                    if (r.pkgList.contains(pkg)) {\n                        killProcess(r.pid);\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean isAppRunning(String packageName, int userId) {\n        boolean running = false;\n        synchronized (mPidsSelfLocked) {\n            int N = mPidsSelfLocked.size();\n            while (N-- > 0) {\n                ProcessRecord r = mPidsSelfLocked.valueAt(N);\n                if (r.userId == userId && r.info.packageName.equals(packageName)) {\n                    running = true;\n                    break;\n                }\n            }\n            return running;\n        }\n    }\n\n    @Override\n    public void killApplicationProcess(final String processName, int uid) {\n        synchronized (mProcessNames) {\n            ProcessRecord r = mProcessNames.get(processName, uid);\n            if (r != null) {\n                killProcess(r.pid);\n            }\n        }\n    }\n\n    @Override\n    public void dump() {\n\n    }\n\n    @Override\n    public void registerProcessObserver(IProcessObserver observer) {\n\n    }\n\n    @Override\n    public void unregisterProcessObserver(IProcessObserver observer) {\n\n    }\n\n    @Override\n    public String getInitialPackage(int pid) {\n        synchronized (mPidsSelfLocked) {\n            ProcessRecord r = mPidsSelfLocked.get(pid);\n            if (r != null) {\n                return r.info.packageName;\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public void handleApplicationCrash() {\n        // Nothing\n    }\n\n    @Override\n    public void appDoneExecuting() {\n        synchronized (mPidsSelfLocked) {\n            ProcessRecord r = mPidsSelfLocked.get(VBinder.getCallingPid());\n            if (r != null) {\n                r.doneExecuting = true;\n                r.lock.open();\n            }\n        }\n    }\n\n\n    /**\n     * Should guard by {@link VActivityManagerService#mPidsSelfLocked}\n     *\n     * @param pid pid\n     */\n    public ProcessRecord findProcessLocked(int pid) {\n        return mPidsSelfLocked.get(pid);\n    }\n\n    /**\n     * Should guard by {@link VActivityManagerService#mProcessNames}\n     *\n     * @param uid vuid\n     */\n    public ProcessRecord findProcessLocked(String processName, int uid) {\n        return mProcessNames.get(processName, uid);\n    }\n\n    public int stopUser(int userHandle, IStopUserCallback.Stub stub) {\n        synchronized (mPidsSelfLocked) {\n            int N = mPidsSelfLocked.size();\n            while (N-- > 0) {\n                ProcessRecord r = mPidsSelfLocked.valueAt(N);\n                if (r.userId == userHandle) {\n                    killProcess(r.pid);\n                }\n            }\n        }\n        try {\n            stub.userStopped(userHandle);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        return 0;\n    }\n\n    public void sendOrderedBroadcastAsUser(Intent intent, VUserHandle user, String receiverPermission,\n                                           BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,\n                                           String initialData, Bundle initialExtras) {\n        Context context = VirtualCore.get().getContext();\n        if (user != null) {\n            intent.putExtra(\"_VA_|_user_id_\", user.getIdentifier());\n        }\n        // TODO: checkPermission\n        context.sendOrderedBroadcast(intent, null/* permission */, resultReceiver, scheduler, initialCode, initialData,\n                initialExtras);\n    }\n\n    public void sendBroadcastAsUser(Intent intent, VUserHandle user) {\n        SpecialComponentList.protectIntent(intent);\n        Context context = VirtualCore.get().getContext();\n        if (user != null) {\n            intent.putExtra(\"_VA_|_user_id_\", user.getIdentifier());\n        }\n        context.sendBroadcast(intent);\n    }\n\n    public boolean bindServiceAsUser(Intent service, ServiceConnection connection, int flags, VUserHandle user) {\n        service = new Intent(service);\n        if (user != null) {\n            service.putExtra(\"_VA_|_user_id_\", user.getIdentifier());\n        }\n        return VirtualCore.get().getContext().bindService(service, connection, flags);\n    }\n\n    public void sendBroadcastAsUser(Intent intent, VUserHandle user, String permission) {\n        SpecialComponentList.protectIntent(intent);\n        Context context = VirtualCore.get().getContext();\n        if (user != null) {\n            intent.putExtra(\"_VA_|_user_id_\", user.getIdentifier());\n        }\n        // TODO: checkPermission\n        context.sendBroadcast(intent);\n    }\n\n    boolean handleStaticBroadcast(int appId, ActivityInfo info, Intent intent,\n                                  PendingResultData result) {\n        // 这里是取出真正的目标 Intent\n        Intent realIntent = intent.getParcelableExtra(\"_VA_|_intent_\");\n        // 取出真正的目标 component\n        ComponentName component = intent.getParcelableExtra(\"_VA_|_component_\");\n        // 用户 id\n        int userId = intent.getIntExtra(\"_VA_|_user_id_\", VUserHandle.USER_NULL);\n        if (realIntent == null) {\n            return false;\n        }\n        if (userId < 0) {\n            VLog.w(TAG, \"Sent a broadcast without userId \" + realIntent);\n            return false;\n        }\n        int vuid = VUserHandle.getUid(userId, appId);\n        return handleUserBroadcast(vuid, info, component, realIntent, result);\n    }\n\n    private boolean handleUserBroadcast(int vuid, ActivityInfo info, ComponentName component, Intent realIntent, PendingResultData result) {\n        if (component != null && !ComponentUtils.toComponentName(info).equals(component)) {\n            // Verify the component.\n            return false;\n        }\n        Log.e(\"gy\", \"handler handleUserBroadcast: 1\");\n        String originAction = SpecialComponentList.unprotectAction(realIntent.getAction());\n        if (originAction != null) {\n            // restore to origin action.\n            realIntent.setAction(originAction);\n        }\n        Log.e(\"gy\", \"handler handleUserBroadcast: 2\" );\n        handleStaticBroadcastAsUser(vuid, info, realIntent, result);\n        return true;\n    }\n\n    private void handleStaticBroadcastAsUser(int vuid, ActivityInfo info, Intent intent,\n                                             PendingResultData result) {\n        synchronized (this) {\n            ProcessRecord r = findProcessLocked(info.processName, vuid);\n            if (BROADCAST_NOT_STARTED_PKG && r == null) {\n                r = startProcessIfNeedLocked(info.processName, getUserId(vuid), info.packageName);\n            }\n            if (r != null && r.appThread != null) {\n                performScheduleReceiver(r.client, vuid, info, intent,\n                        result);\n            }\n        }\n    }\n\n    private void performScheduleReceiver(IVClient client, int vuid, ActivityInfo info, Intent intent,\n                                         PendingResultData result) {\n\n        ComponentName componentName = ComponentUtils.toComponentName(info);\n        BroadcastSystem.get().broadcastSent(vuid, info, result);\n        try {\n            // 远程调用 client app 的 scheduleReceiver\n            client.scheduleReceiver(info.processName, componentName, intent, result);\n        } catch (Throwable e) {\n            if (result != null) {\n                result.finish();\n            }\n        }\n    }\n\n    @Override\n    public void broadcastFinish(PendingResultData res) {\n        BroadcastSystem.get().broadcastFinish(res);\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/device/DeviceInfoPersistenceLayer.java",
    "content": "package com.lody.virtual.server.device;\n\nimport android.os.Parcel;\n\nimport com.lody.virtual.helper.PersistenceLayer;\nimport com.lody.virtual.helper.collection.SparseArray;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.remote.VDeviceInfo;\n\n/**\n * @author Lody\n */\n\npublic class DeviceInfoPersistenceLayer extends PersistenceLayer {\n\n    private VDeviceManagerService mService;\n\n    public DeviceInfoPersistenceLayer(VDeviceManagerService service) {\n        super(VEnvironment.getDeviceInfoFile());\n        this.mService = service;\n    }\n\n    @Override\n    public int getCurrentVersion() {\n        return 1;\n    }\n\n    @Override\n    public void writeMagic(Parcel p) {\n\n    }\n\n    @Override\n    public boolean verifyMagic(Parcel p) {\n        return true;\n    }\n\n    @Override\n    public void writePersistenceData(Parcel p) {\n        SparseArray<VDeviceInfo> infos = mService.getDeviceInfos();\n        int size = infos.size();\n        p.writeInt(size);\n        for (int i = 0; i < size; i++) {\n            int userId = infos.keyAt(i);\n            VDeviceInfo info = infos.valueAt(i);\n            p.writeInt(userId);\n            info.writeToParcel(p, 0);\n        }\n    }\n\n    @Override\n    public void readPersistenceData(Parcel p) {\n        SparseArray<VDeviceInfo> infos = mService.getDeviceInfos();\n        infos.clear();\n        int size = p.readInt();\n        while (size-- > 0) {\n            int userId = p.readInt();\n            VDeviceInfo info = new VDeviceInfo(p);\n            infos.put(userId, info);\n        }\n    }\n\n    @Override\n    public boolean onVersionConflict(int fileVersion, int currentVersion) {\n        return false;\n    }\n\n    @Override\n    public void onPersistenceFileDamage() {\n        getPersistenceFile().delete();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/device/VDeviceManagerService.java",
    "content": "package com.lody.virtual.server.device;\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.helper.collection.SparseArray;\nimport com.lody.virtual.remote.VDeviceInfo;\nimport com.lody.virtual.server.IDeviceInfoManager;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * @author Lody\n */\n\npublic class VDeviceManagerService extends IDeviceInfoManager.Stub {\n\n    private static VDeviceManagerService sInstance = new VDeviceManagerService();\n    private final SparseArray<VDeviceInfo> mDeviceInfos = new SparseArray<>();\n    private DeviceInfoPersistenceLayer mPersistenceLayer = new DeviceInfoPersistenceLayer(this);\n    private UsedDeviceInfoPool mPool = new UsedDeviceInfoPool();\n\n    public static VDeviceManagerService get() {\n        return sInstance;\n    }\n\n    private final class UsedDeviceInfoPool {\n        List<String> deviceIds = new ArrayList<>();\n        List<String> androidIds = new ArrayList<>();\n        List<String> wifiMacs = new ArrayList<>();\n        List<String> bluetoothMacs = new ArrayList<>();\n        List<String> iccIds = new ArrayList<>();\n    }\n\n    public VDeviceManagerService() {\n        mPersistenceLayer.read();\n        for (int i = 0; i < mDeviceInfos.size(); i++) {\n            VDeviceInfo info = mDeviceInfos.valueAt(i);\n            addDeviceInfoToPool(info);\n        }\n    }\n\n    private void addDeviceInfoToPool(VDeviceInfo info) {\n        mPool.deviceIds.add(info.deviceId);\n        mPool.androidIds.add(info.androidId);\n        mPool.wifiMacs.add(info.wifiMac);\n        mPool.bluetoothMacs.add(info.bluetoothMac);\n        mPool.iccIds.add(info.iccId);\n    }\n\n    @Override\n    public VDeviceInfo getDeviceInfo(int userId) throws RemoteException {\n        VDeviceInfo info;\n        synchronized (mDeviceInfos) {\n            info = mDeviceInfos.get(userId);\n            if (info == null) {\n                info = generateRandomDeviceInfo();\n                mDeviceInfos.put(userId, info);\n                mPersistenceLayer.save();\n            }\n        }\n        return info;\n    }\n\n    private VDeviceInfo generateRandomDeviceInfo() {\n        VDeviceInfo info = new VDeviceInfo();\n        String value;\n        do {\n            value = generate10(15);\n            info.deviceId = value;\n        } while (mPool.deviceIds.contains(value));\n        do {\n            value = generate16(16);\n            info.androidId = value;\n        } while (mPool.androidIds.contains(value));\n        do {\n            value = generateMac();\n            info.wifiMac = value;\n        } while (mPool.wifiMacs.contains(value));\n        do {\n            value = generateMac();\n            info.bluetoothMac = value;\n        } while (mPool.bluetoothMacs.contains(value));\n\n        do {\n            value = generate10(20);\n            info.iccId = value;\n        } while (mPool.iccIds.contains(value));\n\n        info.serial = generateSerial();\n\n        addDeviceInfoToPool(info);\n        return info;\n    }\n\n\n    SparseArray<VDeviceInfo> getDeviceInfos() {\n        return mDeviceInfos;\n    }\n\n    private static String generate10(int length) {\n        Random random = new Random();\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            sb.append(random.nextInt(10));\n        }\n        return sb.toString();\n    }\n\n    private static String generate16(int length) {\n        Random random = new Random();\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            int nextInt = random.nextInt(16);\n            if (nextInt < 10) {\n                sb.append(nextInt);\n            } else {\n                sb.append((char) (nextInt + 87));\n            }\n        }\n        return sb.toString();\n    }\n\n    private static String generateMac() {\n        Random random = new Random();\n        StringBuilder sb = new StringBuilder();\n        int next = 1;\n        int cur = 0;\n        while (cur < 12) {\n            int val = random.nextInt(16);\n            if (val < 10) {\n                sb.append(val);\n            } else {\n                sb.append((char) (val + 87));\n            }\n            if (cur == next && cur != 11) {\n                sb.append(\":\");\n                next += 2;\n            }\n            cur++;\n        }\n        return sb.toString();\n    }\n\n    @SuppressLint(\"HardwareIds\")\n    private static String generateSerial() {\n        String serial;\n        if (Build.SERIAL == null || Build.SERIAL.length() <= 0) {\n            serial = \"0123456789ABCDEF\";\n        } else {\n            serial = Build.SERIAL;\n        }\n        List<Character> list = new ArrayList<>();\n        for (char c : serial.toCharArray()) {\n            list.add(c);\n        }\n        Collections.shuffle(list);\n        StringBuilder sb = new StringBuilder();\n        for (Character c : list) {\n            sb.append(c.charValue());\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/job/VJobSchedulerService.java",
    "content": "package com.lody.virtual.server.job;\n\nimport android.annotation.TargetApi;\nimport android.app.job.JobInfo;\nimport android.app.job.JobScheduler;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.PersistableBundle;\nimport android.os.RemoteException;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.VJobScheduler;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.utils.Singleton;\nimport com.lody.virtual.os.VBinder;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.server.IJobScheduler;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class VJobSchedulerService extends IJobScheduler.Stub {\n\n    private static final String TAG = VJobScheduler.class.getSimpleName();\n\n    private static final int JOB_FILE_VERSION = 1;\n    private final Map<JobId, JobConfig> mJobStore = new HashMap<>();\n    private int mGlobalJobId;\n\n    private final JobScheduler mScheduler = (JobScheduler)\n            VirtualCore.get().getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);\n\n    private final ComponentName mJobProxyComponent;\n\n    private VJobSchedulerService() {\n        mJobProxyComponent = new ComponentName(VirtualCore.get().getHostPkg(), VASettings.STUB_JOB);\n        readJobs();\n    }\n\n    private static final Singleton<VJobSchedulerService> gDefault = new Singleton<VJobSchedulerService>() {\n        @Override\n        protected VJobSchedulerService create() {\n            return new VJobSchedulerService();\n        }\n    };\n\n    public static VJobSchedulerService get() {\n        return gDefault.get();\n    }\n\n\n    public static final class JobId implements Parcelable {\n\n        public int vuid;\n        public String packageName;\n        /**\n         * The id given by User.\n         */\n        public int clientJobId;\n\n        JobId(int vuid, String packageName, int id) {\n            this.vuid = vuid;\n            this.packageName = packageName;\n            this.clientJobId = id;\n        }\n\n\n        JobId(Parcel in) {\n            this.vuid = in.readInt();\n            this.packageName = in.readString();\n            this.clientJobId = in.readInt();\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n\n            JobId jobId = (JobId) o;\n\n            return vuid == jobId.vuid\n                    && clientJobId == jobId.clientJobId\n                    && TextUtils.equals(packageName, jobId.packageName);\n        }\n\n        @Override\n        public int hashCode() {\n            int result = vuid;\n            result = 31 * result + (packageName != null ? packageName.hashCode() : 0);\n            result = 31 * result + clientJobId;\n            return result;\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeInt(this.vuid);\n            dest.writeString(this.packageName);\n            dest.writeInt(this.clientJobId);\n        }\n\n        public static final Parcelable.Creator<JobId> CREATOR = new Parcelable.Creator<JobId>() {\n            @Override\n            public JobId createFromParcel(Parcel source) {\n                return new JobId(source);\n            }\n\n            @Override\n            public JobId[] newArray(int size) {\n                return new JobId[size];\n            }\n        };\n    }\n\n    public static final class JobConfig implements Parcelable {\n\n        /**\n         * The id given by VA.\n         */\n        public int virtualJobId;\n        public String serviceName;\n        public PersistableBundle extras;\n\n        JobConfig(int virtualJobId, String serviceName, PersistableBundle extra) {\n            this.virtualJobId = virtualJobId;\n            this.serviceName = serviceName;\n            this.extras = extra;\n        }\n\n        JobConfig(Parcel in) {\n            this.virtualJobId = in.readInt();\n            this.serviceName = in.readString();\n            this.extras = in.readParcelable(PersistableBundle.class.getClassLoader());\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeInt(this.virtualJobId);\n            dest.writeString(this.serviceName);\n            dest.writeParcelable(this.extras, flags);\n        }\n\n        public static final Parcelable.Creator<JobConfig> CREATOR = new Parcelable.Creator<JobConfig>() {\n            @Override\n            public JobConfig createFromParcel(Parcel source) {\n                return new JobConfig(source);\n            }\n\n            @Override\n            public JobConfig[] newArray(int size) {\n                return new JobConfig[size];\n            }\n        };\n    }\n\n\n    @Override\n    public int schedule(JobInfo job) throws RemoteException {\n        int vuid = VBinder.getCallingUid();\n        int id = job.getId();\n        ComponentName service = job.getService();\n        JobId jobId = new JobId(vuid, service.getPackageName(), id);\n        JobConfig config = mJobStore.get(jobId);\n        if (config == null) {\n            config = new JobConfig(mGlobalJobId++, service.getClassName(), job.getExtras());\n            mJobStore.put(jobId, config);\n        } else {\n            config.serviceName = service.getClassName();\n            config.extras = job.getExtras();\n        }\n        saveJobs();\n        mirror.android.app.job.JobInfo.jobId.set(job, config.virtualJobId);\n        mirror.android.app.job.JobInfo.service.set(job, mJobProxyComponent);\n        return mScheduler.schedule(job);\n    }\n\n    private void saveJobs() {\n        File jobFile = VEnvironment.getJobConfigFile();\n        Parcel p = Parcel.obtain();\n        try {\n            p.writeInt(JOB_FILE_VERSION);\n            p.writeInt(mJobStore.size());\n            for (Map.Entry<JobId, JobConfig> entry : mJobStore.entrySet()) {\n                entry.getKey().writeToParcel(p, 0);\n                entry.getValue().writeToParcel(p, 0);\n            }\n            FileOutputStream fos = new FileOutputStream(jobFile);\n            fos.write(p.marshall());\n            fos.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            p.recycle();\n        }\n    }\n\n    private void readJobs() {\n        File jobFile = VEnvironment.getJobConfigFile();\n        if (!jobFile.exists()) {\n            return;\n        }\n        Parcel p = Parcel.obtain();\n        try {\n            FileInputStream fis = new FileInputStream(jobFile);\n            byte[] bytes = new byte[(int) jobFile.length()];\n            int len = fis.read(bytes);\n            fis.close();\n            if (len != bytes.length) {\n                throw new IOException(\"Unable to read job config.\");\n            }\n            p.unmarshall(bytes, 0, bytes.length);\n            p.setDataPosition(0);\n            int version = p.readInt();\n            if (version != JOB_FILE_VERSION) {\n                throw new IOException(\"Bad version of job file: \" + version);\n            }\n            if (!mJobStore.isEmpty()) {\n                mJobStore.clear();\n            }\n            int count = p.readInt();\n            for (int i = 0; i < count; i++) {\n                JobId jobId = new JobId(p);\n                JobConfig config = new JobConfig(p);\n                mJobStore.put(jobId, config);\n                mGlobalJobId = Math.max(mGlobalJobId, config.virtualJobId);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            p.recycle();\n        }\n\n    }\n\n    @Override\n    public void cancel(int jobId) throws RemoteException {\n        int vuid = VBinder.getCallingUid();\n        synchronized (mJobStore) {\n            boolean changed = false;\n            Iterator<Map.Entry<JobId, JobConfig>> iterator = mJobStore.entrySet().iterator();\n            while (iterator.hasNext()) {\n                Map.Entry<JobId, JobConfig> entry = iterator.next();\n                JobId job = entry.getKey();\n                JobConfig config = entry.getValue();\n                if (job.vuid == vuid && job.clientJobId == jobId) {\n                    changed = true;\n                    mScheduler.cancel(config.virtualJobId);\n                    iterator.remove();\n                    break;\n                }\n            }\n            if (changed) {\n                saveJobs();\n            }\n        }\n    }\n\n    @Override\n    public void cancelAll() throws RemoteException {\n        int vuid = VBinder.getCallingUid();\n        synchronized (mJobStore) {\n            boolean changed = false;\n            Iterator<Map.Entry<JobId, JobConfig>> iterator = mJobStore.entrySet().iterator();\n            while (iterator.hasNext()) {\n                Map.Entry<JobId, JobConfig> entry = iterator.next();\n                JobId job = entry.getKey();\n                if (job.vuid == vuid) {\n                    JobConfig config = entry.getValue();\n                    mScheduler.cancel(config.virtualJobId);\n                    changed = true;\n                    iterator.remove();\n                    break;\n                }\n            }\n            if (changed) {\n                saveJobs();\n            }\n        }\n    }\n\n    @Override\n    public List<JobInfo> getAllPendingJobs() throws RemoteException {\n        int vuid = VBinder.getCallingUid();\n        List<JobInfo> jobs = mScheduler.getAllPendingJobs();\n        synchronized (mJobStore) {\n            Iterator<JobInfo> iterator = jobs.listIterator();\n            while (iterator.hasNext()) {\n                JobInfo job = iterator.next();\n                if (!VASettings.STUB_JOB.equals(job.getService().getClassName())) {\n                    // Schedule by Host, invisible in VA.\n                    iterator.remove();\n                    continue;\n                }\n                Map.Entry<JobId, JobConfig> jobEntry = findJobByVirtualJobId(job.getId());\n                if (jobEntry == null) {\n                    iterator.remove();\n                    continue;\n                }\n                JobId jobId = jobEntry.getKey();\n                JobConfig config = jobEntry.getValue();\n                if (jobId.vuid != vuid) {\n                    iterator.remove();\n                    continue;\n                }\n                mirror.android.app.job.JobInfo.jobId.set(job, jobId.clientJobId);\n                mirror.android.app.job.JobInfo.service.set(job, new ComponentName(jobId.packageName, config.serviceName));\n            }\n        }\n        return jobs;\n    }\n\n\n    public Map.Entry<JobId, JobConfig> findJobByVirtualJobId(int virtualJobId) {\n        synchronized (mJobStore) {\n            for (Map.Entry<JobId, JobConfig> entry : mJobStore.entrySet()) {\n                if (entry.getValue().virtualJobId == virtualJobId) {\n                    return entry;\n                }\n            }\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/NotificationCompat.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.app.Notification;\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\nimport android.widget.RemoteViews;\n\nimport com.lody.virtual.client.core.VirtualCore;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mirror.com.android.internal.R_Hide;\n\n/**\n * @author 247321453\n */\npublic abstract class NotificationCompat {\n\n    public static final String EXTRA_TITLE = \"android.title\";\n    public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + \".big\";\n    public static final String EXTRA_TEXT = \"android.text\";\n    public static final String EXTRA_SUB_TEXT = \"android.subText\";\n    public static final String EXTRA_INFO_TEXT = \"android.infoText\";\n    public static final String EXTRA_SUMMARY_TEXT = \"android.summaryText\";\n    public static final String EXTRA_BIG_TEXT = \"android.bigText\";\n    public static final String EXTRA_PROGRESS = \"android.progress\";\n    public static final String EXTRA_PROGRESS_MAX = \"android.progressMax\";\n    public static final String EXTRA_BUILDER_APPLICATION_INFO = \"android.appInfo\";\n    static final String TAG = NotificationCompat.class.getSimpleName();\n    static final String SYSTEM_UI_PKG = \"com.android.systemui\";\n    private final List<Integer> sSystemLayoutResIds = new ArrayList<>(10);\n    private NotificationFixer mNotificationFixer;\n\n    NotificationCompat() {\n        loadSystemLayoutRes();\n        mNotificationFixer = new NotificationFixer(this);\n    }\n\n    public static NotificationCompat create() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            return new NotificationCompatCompatV21();\n        } else {\n            return new NotificationCompatCompatV14();\n        }\n    }\n\n    private void loadSystemLayoutRes() {\n        Field[] fields = R_Hide.layout.TYPE.getFields();\n        for (Field field : fields) {\n            if (Modifier.isStatic(field.getModifiers())\n                    && Modifier.isFinal(field.getModifiers())) {\n                try {\n                    int id = field.getInt(null);\n                    sSystemLayoutResIds.add(id);\n                } catch (Throwable e) {\n                    // ignore\n                }\n            }\n        }\n    }\n\n    NotificationFixer getNotificationFixer() {\n        return mNotificationFixer;\n    }\n\n    boolean isSystemLayout(RemoteViews remoteViews) {\n        return remoteViews != null\n                && sSystemLayoutResIds.contains(remoteViews.getLayoutId());\n    }\n\n    public Context getHostContext() {\n        return VirtualCore.get().getContext();\n    }\n\n    PackageInfo getPackageInfo(String packageName) {\n        try {\n            return VirtualCore.get().getUnHookPackageManager().getPackageInfo(packageName, 0);\n        } catch (PackageManager.NameNotFoundException e) {\n            // ignore\n        }\n        return null;\n    }\n\n    public abstract boolean dealNotification(int id, Notification notification, String packageName);\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/NotificationCompatCompatV14.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.app.Notification;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\n\nimport com.lody.virtual.client.core.VirtualCore;\n\n/**\n * @author 247321543\n */\n@SuppressWarnings(\"deprecation\")\nclass NotificationCompatCompatV14 extends NotificationCompat {\n    private final RemoteViewsFixer mRemoteViewsFixer;\n\n    NotificationCompatCompatV14() {\n        super();\n        mRemoteViewsFixer = new RemoteViewsFixer(this);\n    }\n\n    private RemoteViewsFixer getRemoteViewsFixer() {\n        return mRemoteViewsFixer;\n    }\n\n    @Override\n    public boolean dealNotification(int id, Notification notification, final String packageName) {\n        Context appContext = getAppContext(packageName);\n        if (appContext == null) {\n            return false;\n        }\n        if (VirtualCore.get().isOutsideInstalled(packageName)) {\n            getNotificationFixer().fixIconImage(appContext.getResources(), notification.contentView, false, notification);\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                getNotificationFixer().fixIconImage(appContext.getResources(), notification.bigContentView, false, notification);\n            }\n            notification.icon = getHostContext().getApplicationInfo().icon;\n            return true;\n        }\n        if (notification.tickerView != null) {\n            if (isSystemLayout(notification.tickerView)) {\n                getNotificationFixer().fixRemoteViewActions(appContext, false, notification.tickerView);\n            } else {\n                notification.tickerView = getRemoteViewsFixer().makeRemoteViews(id + \":tickerView\", appContext,\n                        notification.tickerView, false, false);\n            }\n        }\n        if (notification.contentView != null) {\n            if (isSystemLayout(notification.contentView)) {\n                boolean hasIconBitmap = getNotificationFixer().fixRemoteViewActions(appContext, false, notification.contentView);\n                getNotificationFixer().fixIconImage(appContext.getResources(), notification.contentView, hasIconBitmap, notification);\n            } else {\n                notification.contentView = getRemoteViewsFixer().makeRemoteViews(id + \":contentView\", appContext,\n                        notification.contentView, false, true);\n            }\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            if (notification.bigContentView != null) {\n                if (isSystemLayout(notification.bigContentView)) {\n                    getNotificationFixer().fixRemoteViewActions(appContext, false, notification.bigContentView);\n                } else {\n                    notification.bigContentView = getRemoteViewsFixer().makeRemoteViews(id + \":bigContentView\", appContext,\n                            notification.bigContentView, true, true);\n                }\n            }\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            if (notification.headsUpContentView != null) {\n                if (isSystemLayout(notification.headsUpContentView)) {\n                    boolean hasIconBitmap = getNotificationFixer().fixRemoteViewActions(appContext, false, notification.headsUpContentView);\n                    getNotificationFixer().fixIconImage(appContext.getResources(), notification.contentView, hasIconBitmap, notification);\n                } else {\n                    notification.headsUpContentView = getRemoteViewsFixer().makeRemoteViews(id + \":headsUpContentView\", appContext,\n                            notification.headsUpContentView, false, false);\n                }\n            }\n        }\n        notification.icon = getHostContext().getApplicationInfo().icon;\n        return true;\n    }\n\n    Context getAppContext(final String packageName) {\n        Context context = null;\n        try {\n            context = getHostContext().createPackageContext(packageName,\n                    Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);\n        } catch (PackageManager.NameNotFoundException e) {\n           e.printStackTrace();\n        }\n        return context;\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/NotificationCompatCompatV21.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.app.Notification;\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.widget.RemoteViews;\n\nimport com.lody.virtual.helper.utils.Reflect;\n\nimport static com.lody.virtual.os.VEnvironment.getPackageResourcePath;\n\n/**\n * @author 247321543\n */\n/* package */ class NotificationCompatCompatV21 extends NotificationCompatCompatV14 {\n\n    private static final String TAG = NotificationCompatCompatV21.class.getSimpleName();\n\n    NotificationCompatCompatV21() {\n        super();\n    }\n\n    @Override\n    public boolean dealNotification(int id, Notification notification, String packageName) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            Context appContext = getAppContext(packageName);\n            return resolveRemoteViews(appContext, packageName, notification)\n                    || resolveRemoteViews(appContext, packageName, notification.publicVersion);\n        }\n        return super.dealNotification(id, notification, packageName);\n    }\n\n    private boolean resolveRemoteViews(Context appContext, String packageName, Notification notification) {\n        if (notification == null) {\n            return false;\n        }\n        String sourcePath = null;\n        PackageInfo packageInfo = getPackageInfo(packageName);\n        ApplicationInfo host = getHostContext().getApplicationInfo();\n        if (packageInfo != null) {\n            sourcePath = packageInfo.applicationInfo.sourceDir;\n        }\n        if (TextUtils.isEmpty(sourcePath)) {\n            sourcePath = getPackageResourcePath(packageName).getAbsolutePath();\n        }\n\n        //Fix RemoteViews\n        getNotificationFixer().fixNotificationRemoteViews(appContext, notification);\n        //Fix Icon\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            getNotificationFixer().fixIcon(notification.getSmallIcon(), appContext, packageInfo != null);\n            getNotificationFixer().fixIcon(notification.getLargeIcon(), appContext, packageInfo != null);\n        } else {\n            getNotificationFixer().fixIconImage(appContext.getResources(), notification.contentView, false, notification);\n        }\n        notification.icon = host.icon;\n\n        ApplicationInfo proxyApplicationInfo = new ApplicationInfo(host);\n\n        proxyApplicationInfo.packageName = packageName;\n        proxyApplicationInfo.publicSourceDir = sourcePath;\n        proxyApplicationInfo.sourceDir = sourcePath;\n\n        fixApplicationInfo(notification.tickerView, proxyApplicationInfo);\n        fixApplicationInfo(notification.contentView, proxyApplicationInfo);\n        fixApplicationInfo(notification.bigContentView, proxyApplicationInfo);\n        fixApplicationInfo(notification.headsUpContentView, proxyApplicationInfo);\n        Bundle bundle = Reflect.on(notification).get(\"extras\");\n        if (bundle != null) {\n            bundle.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, proxyApplicationInfo);\n        }\n        return true;\n    }\n\n    private ApplicationInfo getApplicationInfo(Notification notification) {\n        ApplicationInfo ai = getApplicationInfo(notification.tickerView);\n        if (ai != null) {\n            return ai;\n        }\n        ai = getApplicationInfo(notification.contentView);\n        if (ai != null) {\n            return ai;\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            ai = getApplicationInfo(notification.bigContentView);\n            if (ai != null) {\n                return ai;\n            }\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            ai = getApplicationInfo(notification.headsUpContentView);\n            if (ai != null) {\n                return ai;\n            }\n        }\n        return null;\n    }\n\n    private ApplicationInfo getApplicationInfo(RemoteViews remoteViews) {\n        if (remoteViews != null) {\n            return mirror.android.widget.RemoteViews.mApplication.get(remoteViews);\n        }\n        return null;\n    }\n\n    private void fixApplicationInfo(RemoteViews remoteViews, ApplicationInfo ai) {\n        if (remoteViews != null) {\n            mirror.android.widget.RemoteViews.mApplication.set(remoteViews, ai);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/NotificationFixer.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.annotation.TargetApi;\nimport android.app.Notification;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.PixelFormat;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.Icon;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.widget.RemoteViews;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mirror.com.android.internal.R_Hide;\n\n/* package */ class NotificationFixer {\n\n    private static final String TAG = NotificationCompat.TAG;\n    private NotificationCompat mNotificationCompat;\n\n    NotificationFixer(NotificationCompat notificationCompat) {\n        this.mNotificationCompat = notificationCompat;\n    }\n\n    private static void fixNotificationIcon(Context context, Notification notification, Notification.Builder builder) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            //noinspection deprecation\n            builder.setSmallIcon(notification.icon);\n            //noinspection deprecation\n            builder.setLargeIcon(notification.largeIcon);\n        } else {\n            Icon icon = notification.getSmallIcon();\n            if (icon != null) {\n                Bitmap bitmap = drawableToBitMap(icon.loadDrawable(context));\n                if (bitmap != null) {\n                    Icon newIcon = Icon.createWithBitmap(bitmap);\n                    builder.setSmallIcon(newIcon);\n                }\n            }\n            Icon largeIcon = notification.getLargeIcon();\n            if (largeIcon != null) {\n                Bitmap bitmap = drawableToBitMap(largeIcon.loadDrawable(context));\n                if (bitmap != null) {\n                    Icon newIcon = Icon.createWithBitmap(bitmap);\n                    builder.setLargeIcon(newIcon);\n                }\n            }\n        }\n    }\n\n    private static Bitmap drawableToBitMap(Drawable drawable) {\n        if (drawable == null) {\n            return null;\n        }\n        if (drawable instanceof BitmapDrawable) {\n            BitmapDrawable bitmapDrawable = ((BitmapDrawable) drawable);\n            return bitmapDrawable.getBitmap();\n        } else {\n            Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),\n                    drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);\n            Canvas canvas = new Canvas(bitmap);\n            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n            drawable.draw(canvas);\n            return bitmap;\n        }\n    }\n\n    void fixIcon(Icon icon, Context appContext, boolean installed) {\n        if (icon == null) {\n            return;\n        }\n        int type = mirror.android.graphics.drawable.Icon.mType.get(icon);\n        if (type == mirror.android.graphics.drawable.Icon.TYPE_RESOURCE) {\n            if (installed) {\n                mirror.android.graphics.drawable.Icon.mObj1.set(icon, appContext.getResources());\n                mirror.android.graphics.drawable.Icon.mString1.set(icon, appContext.getPackageName());\n            } else {\n                Drawable drawable = icon.loadDrawable(appContext);\n                Bitmap bitmap = drawableToBitMap(drawable);\n                mirror.android.graphics.drawable.Icon.mObj1.set(icon, bitmap);\n                mirror.android.graphics.drawable.Icon.mString1.set(icon, null);\n                mirror.android.graphics.drawable.Icon.mType.set(icon, mirror.android.graphics.drawable.Icon.TYPE_BITMAP);\n            }\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    void fixNotificationRemoteViews(Context pluginContext, Notification notification) {\n        Notification.Builder rebuild = null;\n        try {\n            rebuild = Reflect.on(Notification.Builder.class).create(pluginContext, notification).get();\n        } catch (Exception e) {\n            // ignore\n        }\n        if (rebuild != null) {\n            Notification renotification = rebuild.build();\n            if (notification.tickerView == null) {\n                notification.tickerView = renotification.tickerView;\n            }\n            if (notification.contentView == null) {\n                notification.contentView = renotification.contentView;\n            }\n            if (notification.bigContentView == null) {\n                notification.bigContentView = renotification.bigContentView;\n            }\n            if (notification.headsUpContentView == null) {\n                notification.headsUpContentView = renotification.headsUpContentView;\n            }\n        }\n    }\n\n    boolean fixRemoteViewActions(Context appContext, boolean installed, final RemoteViews remoteViews) {\n        boolean hasIcon = false;\n        if (remoteViews != null) {\n            int systemIconViewId = R_Hide.id.icon.get();\n            List<BitmapReflectionAction> mNew = new ArrayList<>();\n            ArrayList<Object> mActions = Reflect.on(remoteViews).get(\"mActions\");\n            if (mActions != null) {\n                int count = mActions.size();\n                for (int i = count - 1; i >= 0; i--) {\n                    Object action = mActions.get(i);\n                    if (action == null) {\n                        continue;\n                    }\n                    //TextViewDrawableAction\n                    //setImageURI\n                    //setLabelFor\n                    if (action.getClass().getSimpleName().endsWith(\"TextViewDrawableAction\")) {\n                        mActions.remove(action);\n                        continue;\n                    }\n                    if (ReflectionActionCompat.isInstance(action)\n                            || (action.getClass().getSimpleName().endsWith(\"ReflectionAction\"))) {\n                        int viewId = Reflect.on(action).get(\"viewId\");\n\n                        String methodName = Reflect.on(action).get(\"methodName\");\n                        int type = Reflect.on(action).get(\"type\");\n                        Object value = Reflect.on(action).get(\"value\");\n                        if (!hasIcon) {\n                            hasIcon = viewId == systemIconViewId;\n                            if (hasIcon) {\n                                if (type == ReflectionActionCompat.INT && (int) value == 0) {\n                                    hasIcon = false;\n                                }\n                                if (hasIcon) {\n                                    VLog.v(TAG, \"find icon \" + methodName + \" type=\" + type + \", value=\" + value);\n                                }\n                            }\n                        }\n                        if (methodName.equals(\"setImageResource\")) {\n                            //setImageBitmap\n                            mNew.add(new BitmapReflectionAction(viewId, \"setImageBitmap\",\n                                    drawableToBitMap(appContext.getResources().getDrawable((int) value))));\n                            mActions.remove(action);\n                        } else if (methodName.equals(\"setText\") && type == ReflectionActionCompat.INT) {\n                            //setText string\n                            Reflect.on(action).set(\"type\", ReflectionActionCompat.STRING);\n                            Reflect.on(action).set(\"value\", appContext.getResources().getString((int) value));\n                        } else if (methodName.equals(\"setLabelFor\")) {\n                            //TODO remove\n                            mActions.remove(action);\n                        } else if (methodName.equals(\"setBackgroundResource\")) {\n                            //TODO remove\n                            mActions.remove(action);\n                        } else if (methodName.equals(\"setImageURI\")) {\n                            Uri uri = (Uri) value;\n                            if (!uri.getScheme().startsWith(\"http\")) {\n                                mActions.remove(action);\n                            }\n                        } else {\n                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                                if (value instanceof Icon) {\n                                    Icon icon = (Icon) value;\n                                    fixIcon(icon, appContext, installed);\n                                }\n                            }\n                        }\n                    }\n                }\n                for (BitmapReflectionAction action : mNew) {\n                    remoteViews.setBitmap(action.viewId, action.methodName, action.bitmap);\n                }\n            }\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                mirror.android.widget.RemoteViews.mPackage.set(remoteViews, VirtualCore.get().getHostPkg());\n            }\n        }\n        return hasIcon;\n    }\n\n    void fixIconImage(Resources resources, RemoteViews remoteViews, boolean hasIconBitmap, Notification notification) {\n        if (remoteViews == null) return;\n        if (!mNotificationCompat.isSystemLayout(remoteViews)) {\n            return;\n        }\n        try {\n            //noinspection deprecation\n            int id = R_Hide.id.icon.get();\n            //only fake small icon\n            if (!hasIconBitmap && notification.largeIcon == null) {\n                Drawable drawable = resources.getDrawable(notification.icon);\n                drawable.setLevel(notification.iconLevel);\n                Bitmap bitmap = drawableToBitMap(drawable);\n                remoteViews.setImageViewBitmap(id, bitmap);\n            }\n            if (Build.VERSION.SDK_INT >= 21) {\n                remoteViews.setInt(id, \"setBackgroundColor\", Color.TRANSPARENT);\n            }\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                remoteViews.setViewPadding(id, 0, 0, 0, 0);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static class BitmapReflectionAction {\n        int viewId;\n        String methodName;\n        Bitmap bitmap;\n\n        BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {\n            this.viewId = viewId;\n            this.methodName = methodName;\n            this.bitmap = bitmap;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/PendIntentCompat.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport android.app.PendingIntent;\nimport android.graphics.Rect;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.widget.ImageView;\nimport android.widget.RemoteViews;\nimport android.widget.TextView;\n\nimport static com.lody.virtual.server.notification.NotificationCompat.TAG;\n\n/***\n * Remoteviews's PendIntent\n *\n * @author 247321453\n */\nclass PendIntentCompat {\n    private RemoteViews mRemoteViews;\n    private Map<Integer, PendingIntent> clickIntents;\n\n    PendIntentCompat(RemoteViews mRemoteViews) {\n        this.mRemoteViews = mRemoteViews;\n    }\n\n    public int findPendIntents() {\n        if (clickIntents == null) {\n            clickIntents = getClickIntents(mRemoteViews);\n        }\n        return clickIntents.size();\n    }\n\n    /**\n     *\n     * @param remoteViews notification's old remoteViews\n     * @param remoteview notification's old remoteViews view\n     * @param oldRemoteView notification's new remoteViews view\n     */\n    public void setPendIntent(RemoteViews remoteViews, View remoteview, View oldRemoteView) {\n        if (findPendIntents() > 0) {\n            Iterator<Map.Entry<Integer, PendingIntent>> set = clickIntents.entrySet().iterator();\n            List<RectInfo> list = new ArrayList<>();\n            int index = 0;\n            VLog.v(TAG, \"start find intent\");\n            while (set.hasNext()) {\n                Map.Entry<Integer, PendingIntent> e = set.next();\n                View view = oldRemoteView.findViewById(e.getKey());\n                if (view != null) {\n                    Rect rect = getRect(view);\n                    list.add(new RectInfo(rect, e.getValue(), index));\n                    index++;\n                }\n            }\n            VLog.v(TAG, \"find:\" + list);\n            if (remoteview instanceof ViewGroup) {\n                setIntentByViewGroup(remoteViews, (ViewGroup) remoteview, list);\n            }\n        }\n    }\n\n    private Rect getRect(View view) {\n        Rect rect = new Rect();\n        rect.top = view.getTop();\n        rect.left = view.getLeft();\n        rect.right = view.getRight();\n        rect.bottom = view.getBottom();\n\n        ViewParent viewParent = view.getParent();\n        if (viewParent != null) {\n            if (viewParent instanceof ViewGroup) {\n                Rect prect = getRect((ViewGroup) viewParent);\n                rect.top += prect.top;\n                rect.left += prect.left;\n                rect.right += prect.left;\n                rect.bottom += prect.top;\n            }\n        }\n        return rect;\n    }\n\n    private void setIntentByViewGroup(RemoteViews remoteViews, ViewGroup viewGroup, List<RectInfo> list) {\n        int count = viewGroup.getChildCount();\n        Rect p = new Rect();\n        viewGroup.getHitRect(p);\n        for (int i = 0; i < count; i++) {\n            View v = viewGroup.getChildAt(i);\n            if (v instanceof ViewGroup) {\n                // linearlayout\n                setIntentByViewGroup(remoteViews, (ViewGroup) v, list);\n            } else if (v instanceof TextView || v instanceof ImageView) {\n                // textview\n                Rect rect = getRect(v);\n                RectInfo next = findIntent(rect, list);\n                if (next != null) {\n//\t\t\t\t\tVLog.d(TAG, next.rect+\":setPendIntent:\"+i);\n//                    remoteViews.setImageViewBitmap(v.getId(), next.testBg);\n                    remoteViews.setOnClickPendingIntent(v.getId(), next.mPendingIntent);\n                }\n            }\n        }\n    }\n\n    private RectInfo findIntent(Rect rect, List<RectInfo> list) {\n        int maxArea = 0;\n        RectInfo next = null;\n        for (RectInfo rectInfo : list) {\n            int size = getOverlapArea(rect, rectInfo.rect);\n            if (size > maxArea) {\n                if (size == 0) {\n                    Log.w(\"PendingIntentCompat\", \"find two:\" + rectInfo.rect);\n                }\n                maxArea = size;\n                next = rectInfo;\n            }\n        }\n        return next;\n    }\n\n    private int getOverlapArea(Rect rect1, Rect rect2) {\n        Rect rect = new Rect();\n        rect.left = Math.max(rect1.left, rect2.left);\n        rect.top = Math.max(rect1.top, rect2.top);\n        rect.right = Math.min(rect1.right, rect2.right);\n        rect.bottom = Math.min(rect1.bottom, rect2.bottom);\n        if (rect.left < rect.right && rect.top < rect.bottom) {\n            return (rect.right - rect.left) * (rect.bottom - rect.top);\n        }\n        return 0;\n    }\n\n    private Map<Integer, PendingIntent> getClickIntents(RemoteViews remoteViews) {\n        Map<Integer, PendingIntent> map = new HashMap<>();\n        if (remoteViews == null)\n            return map;\n        Object mActionsObj = null;\n        try {\n            mActionsObj = Reflect.on(remoteViews).get(\"mActions\");\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        if (mActionsObj == null) {\n            return map;\n        }\n        if (mActionsObj instanceof Collection) {\n            Collection mActions = (Collection) mActionsObj;\n            for (Object one : mActions) {\n                if (one != null) {\n                    String action;\n                    try {\n                        action = Reflect.on(one).call(\"getActionName\").get();\n                    } catch (Exception e) {\n                        action = one.getClass().getSimpleName();\n                    }\n                    if (\"SetOnClickPendingIntent\".equalsIgnoreCase(action)) {\n                        int id = Reflect.on(one).get(\"viewId\");\n                        PendingIntent intent = Reflect.on(one).get(\"pendingIntent\");\n                        map.put(id, intent);\n                    }\n                }\n            }\n        }\n        return map;\n    }\n\n    class RectInfo {\n        Rect rect;\n        PendingIntent mPendingIntent;\n        int index;\n\n        public RectInfo(Rect rect, PendingIntent pendingIntent, int index) {\n            this.rect = rect;\n            mPendingIntent = pendingIntent;\n            this.index = index;\n        }\n\n        @Override\n        public String toString() {\n            return \"RectInfo{\" +\n                    \"rect=\" + rect +\n                    '}';\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/ReflectionActionCompat.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.widget.RemoteViews;\n\n\n/* package */ class ReflectionActionCompat {\n    private static Class ReflectionActionClass;\n    private static final String ReflectionAction = \"ReflectionAction\";\n\n    static final int TAG = 2;\n\n    static final int BOOLEAN = 1;\n    static final int BYTE = 2;\n    static final int SHORT = 3;\n    static final int INT = 4;\n    static final int LONG = 5;\n    static final int FLOAT = 6;\n    static final int DOUBLE = 7;\n    static final int CHAR = 8;\n    static final int STRING = 9;\n    static final int CHAR_SEQUENCE = 10;\n    static final int URI = 11;\n    // BITMAP actions are never stored in the list of actions. They are only used locally\n    // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.\n    static final int BITMAP = 12;\n    static final int BUNDLE = 13;\n    static final int INTENT = 14;\n    static final int COLOR_STATE_LIST = 15;\n    static final int ICON = 16;\n\n    static {\n        try {\n            ReflectionActionClass = Class.forName(RemoteViews.class.getName() + \"$\" + ReflectionAction);\n        } catch (ClassNotFoundException e) {\n        }\n    }\n\n    static boolean isInstance(Object object) {\n        return ReflectionActionClass != null && ReflectionActionClass.isInstance(object);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/RemoteViewsFixer.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.graphics.Bitmap;\nimport android.os.Build;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.FrameLayout;\nimport android.widget.RemoteViews;\nimport android.widget.TextView;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\n\n\n/* package */ class RemoteViewsFixer {\n    private static final String TAG = NotificationCompat.TAG;\n    private final WidthCompat mWidthCompat;\n    private int notification_min_height, notification_max_height, notification_mid_height;\n    private int notification_panel_width;\n    private int notification_side_padding;\n    private int notification_padding;\n\n    private final HashMap<String, Bitmap> mImages = new HashMap<>();\n    private NotificationCompat mNotificationCompat;\n\n    RemoteViewsFixer(NotificationCompat notificationCompat) {\n        mWidthCompat = new WidthCompat();\n        mNotificationCompat = notificationCompat;\n    }\n\n    View toView(final Context context, RemoteViews remoteViews, boolean isBig, boolean systemId) {\n        View mCache = null;\n        try {\n            mCache = createView(context, remoteViews, isBig, systemId);\n        } catch (Throwable throwable) {\n            VLog.w(TAG, \"toView 1\", throwable);\n            try {\n                mCache = LayoutInflater.from(context).inflate(remoteViews.getLayoutId(), null);\n            } catch (Throwable e) {\n                VLog.w(TAG, \"toView 2\", e);\n            }\n        }\n        return mCache;\n    }\n\n    Bitmap createBitmap(View mCache) {\n        if (mCache == null) {\n            return null;\n        }\n        mCache.setDrawingCacheEnabled(true);\n        mCache.buildDrawingCache();\n        return mCache.getDrawingCache();\n    }\n\n    private View apply(Context context, RemoteViews remoteViews) {\n        View view = null;\n        try {\n            view = LayoutInflater.from(context).inflate(remoteViews.getLayoutId(), null, false);\n            try {\n                Reflect.on(view).call(\"setTagInternal\", Reflect.on(\"com.android.internal.R$id\").get(\"widget_frame\"), remoteViews.getLayoutId());\n            } catch (Exception e2) {\n                VLog.w(TAG, \"setTagInternal\", e2);\n            }\n        } catch (Exception e) {\n            VLog.w(TAG, \"inflate\", e);\n        }\n        if (view != null) {\n            ArrayList<Object> mActions = Reflect.on(remoteViews).get(\"mActions\");\n            if (mActions != null) {\n                VLog.d(TAG, \"apply actions:\"+mActions.size());\n                for (Object action : mActions) {\n                    try {\n                        Reflect.on(action).call(\"apply\", view, null, null);\n                    } catch (Exception e) {\n                        VLog.w(TAG, \"apply action\", e);\n                    }\n                }\n            }\n        } else {\n            VLog.e(TAG, \"create views\");\n        }\n        return view;\n    }\n\n    private View createView(final Context context, RemoteViews remoteViews, boolean isBig, boolean systemId) {\n        if (remoteViews == null)\n            return null;\n        Context base = mNotificationCompat.getHostContext();\n        init(base);\n        VLog.v(TAG, \"createView:big=\" + isBig + \",system=\" + systemId);\n\n        int height = isBig ? notification_max_height : notification_min_height;\n        int width = mWidthCompat.getNotificationWidth(base, notification_panel_width, height,\n                notification_side_padding);\n        VLog.v(TAG, \"createView:getNotificationWidth=\" + width);\n        ViewGroup frameLayout = new FrameLayout(context);\n        VLog.v(TAG, \"createView:apply\");\n\n        View view1 = apply(context, remoteViews);\n\n        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,\n                ViewGroup.LayoutParams.MATCH_PARENT);\n        params.gravity = Gravity.CENTER_VERTICAL;\n        frameLayout.addView(view1, params);\n        if (view1 instanceof ViewGroup) {\n            VLog.v(TAG, \"createView:fixTextView\");\n            fixTextView((ViewGroup) view1);\n        }\n        int mode;\n        //TODO need adaptation\n        if (systemId) {\n            mode = View.MeasureSpec.EXACTLY;\n        } else {\n            if (isBig) {\n                mode = View.MeasureSpec.AT_MOST;\n            } else {\n                mode = View.MeasureSpec.EXACTLY;\n            }\n        }\n        VLog.v(TAG, \"createView:layout\");\n        View mCache = frameLayout;\n        mCache.layout(0, 0, width, height);\n        mCache.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),\n                View.MeasureSpec.makeMeasureSpec(height, mode));\n        mCache.layout(0, 0, width, mCache.getMeasuredHeight());\n        VLog.v(TAG, \"notification:systemId=\" + systemId + \",max=%d/%d, szie=%d/%d\", width, height,\n                mCache.getMeasuredWidth(), mCache.getMeasuredHeight());\n        return mCache;\n    }\n\n    private void fixTextView(ViewGroup viewGroup) {\n        int count = viewGroup.getChildCount();\n        for (int i = 0; i < count; i++) {\n            View v = viewGroup.getChildAt(i);\n            if (v instanceof TextView) {\n                TextView tv = (TextView) v;\n                if (isSingleLine(tv)) {\n                    tv.setSingleLine(false);\n                    tv.setMaxLines(1);\n                }\n            } else if (v instanceof ViewGroup) {\n                fixTextView((ViewGroup) v);\n            }\n        }\n    }\n\n    private boolean isSingleLine(TextView textView) {\n        boolean singleLine;\n        try {\n            singleLine = Reflect.on(textView).get(\"mSingleLine\");\n        } catch (Exception e) {\n            singleLine = (textView.getInputType() & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0;\n        }\n        return singleLine;\n    }\n\n    public RemoteViews makeRemoteViews(String key, Context pluginContext, RemoteViews contentView, boolean isBig, boolean click) {\n        if (contentView == null) {\n            return null;\n        }\n        final boolean systemId = false;\n        final PendIntentCompat pendIntentCompat = new PendIntentCompat(contentView);\n        final int layoutId;\n        if (!click || pendIntentCompat.findPendIntents() <= 0) {\n            layoutId = R.layout.custom_notification_lite;\n        } else {\n            layoutId = R.layout.custom_notification;\n        }\n        VLog.v(TAG, \"createviews id = \" + layoutId);\n        //make a remoteViews\n        RemoteViews remoteViews = new RemoteViews(mNotificationCompat.getHostContext().getPackageName(), layoutId);\n        VLog.v(TAG, \"remoteViews to view\");\n        View cache = toView(pluginContext, contentView, isBig, systemId);\n        // remoteViews to bitmap\n        VLog.v(TAG, \"start createBitmap\");\n        final Bitmap bmp = createBitmap(cache);\n        if (bmp == null) {\n            VLog.e(TAG, \"bmp is null,contentView=\" + contentView);\n            // return null; //ignore notification\n        } else {\n            VLog.v(TAG, \"bmp w=\" + bmp.getWidth() + \",h=\" + bmp.getHeight());\n        }\n        Bitmap old;\n        synchronized (mImages) {\n            old = mImages.get(key);\n        }\n        if (old != null && !old.isRecycled()) {\n            VLog.v(TAG, \"recycle \" + key);\n            old.recycle();\n        }\n        remoteViews.setImageViewBitmap(R.id.im_main, bmp);\n        VLog.v(TAG, \"createview \" + key);\n        synchronized (mImages) {\n            mImages.put(key, bmp);\n        }\n        //notification's click\n        if (click) {\n            if (layoutId == R.layout.custom_notification) {\n                VLog.v(TAG, \"start setPendIntent\");\n                try {\n                    pendIntentCompat.setPendIntent(remoteViews,\n                            toView(mNotificationCompat.getHostContext(), remoteViews, isBig, systemId),\n                            cache);\n                } catch (Exception e) {\n                    VLog.e(TAG, \"setPendIntent error\", e);\n                }\n            }\n        }\n        return remoteViews;\n    }\n\n    private boolean init = false;\n\n    private void init(Context context) {\n        if (init) return;\n        init = true;\n        if (notification_panel_width == 0) {\n            Context systemUi = null;\n            try {\n                systemUi = context.createPackageContext(NotificationCompat.SYSTEM_UI_PKG, Context.CONTEXT_IGNORE_SECURITY);\n            } catch (PackageManager.NameNotFoundException e) {\n            }\n            if (Build.VERSION.SDK_INT <= 19) {\n                notification_side_padding = 0;\n            } else {\n                notification_side_padding = getDimem(context, systemUi, \"notification_side_padding\",\n                        R.dimen.notification_side_padding);\n            }\n            notification_panel_width = getDimem(context, systemUi, \"notification_panel_width\",\n                    R.dimen.notification_panel_width);\n            if (notification_panel_width <= 0) {\n                notification_panel_width = context.getResources().getDisplayMetrics().widthPixels;\n            }\n            notification_min_height = getDimem(context, systemUi, \"notification_min_height\",\n                    R.dimen.notification_min_height);\n            // getDimem(context, systemUi, \"notification_row_min_height\", 0);\n            // if (notification_min_height == 0) {\n            // notification_min_height =\n            // }\n            notification_max_height = getDimem(context, systemUi, \"notification_max_height\",\n                    R.dimen.notification_max_height);\n            notification_mid_height = getDimem(context, systemUi, \"notification_mid_height\",\n                    R.dimen.notification_mid_height);\n            notification_padding = getDimem(context, systemUi, \"notification_padding\", R.dimen.notification_padding);\n            // notification_collapse_second_card_padding\n        }\n    }\n\n    private int getDimem(Context context, Context sysContext, String name, int defId) {\n        if (sysContext != null) {\n            int id = sysContext.getResources().getIdentifier(name, \"dimen\", NotificationCompat.SYSTEM_UI_PKG);\n            if (id != 0) {\n                try {\n                    return Math.round(sysContext.getResources().getDimension(id));\n                } catch (Exception e) {\n\n                }\n            }\n        }\n        // VLog.w(TAG, \"use my dimen:\" + name);\n        return defId == 0 ? 0 : Math.round(context.getResources().getDimension(defId));\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/VNotificationManagerService.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.app.NotificationManager;\nimport android.content.Context;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.server.INotificationManager;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\n\npublic class VNotificationManagerService extends INotificationManager.Stub {\n    private static final AtomicReference<VNotificationManagerService> gService = new AtomicReference<>();\n    private NotificationManager mNotificationManager;\n    static final String TAG = NotificationCompat.class.getSimpleName();\n    private final List<String> mDisables = new ArrayList<>();\n    //VApp's Notifications\n    private final HashMap<String, List<NotificationInfo>> mNotifications = new HashMap<>();\n    private Context mContext;\n\n    private VNotificationManagerService(Context context) {\n        mContext = context;\n        mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\n    }\n\n    public static void systemReady(Context context) {\n        VNotificationManagerService instance = new VNotificationManagerService(context);\n        gService.set(instance);\n    }\n\n    public static VNotificationManagerService get() {\n        return gService.get();\n    }\n\n    /***\n     * fake notification's id\n     *\n     * @param id          notification's id\n     * @param packageName notification's package\n     * @param userId      user\n     * @return\n     */\n    @Override\n    public int dealNotificationId(int id, String packageName, String tag, int userId) {\n        return id;\n    }\n\n    /***\n     * fake notification's tag\n     *\n     * @param id          notification's id\n     * @param packageName notification's package\n     * @param tag         notification's tag\n     * @param userId      user\n     * @return\n     */\n    @Override\n    public String dealNotificationTag(int id, String packageName, String tag, int userId) {\n        if (TextUtils.equals(mContext.getPackageName(), packageName)) {\n            return tag;\n        }\n        if (tag == null) {\n            return packageName + \"@\" + userId;\n        }\n        return packageName + \":\" + tag + \"@\" + userId;\n    }\n\n    @Override\n    public boolean areNotificationsEnabledForPackage(String packageName, int userId) {\n        return !mDisables.contains(packageName + \":\" + userId);\n    }\n\n    @Override\n    public void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId) {\n        String key = packageName + \":\" + userId;\n        if (enable) {\n            if (mDisables.contains(key)) {\n                mDisables.remove(key);\n            }\n        } else {\n            if (!mDisables.contains(key)) {\n                mDisables.add(key);\n            }\n        }\n        //TODO: save mDisables ?\n    }\n\n    @Override\n    public void addNotification(int id, String tag, String packageName, int userId) {\n        NotificationInfo notificationInfo = new NotificationInfo(id, tag, packageName, userId);\n        synchronized (mNotifications) {\n            List<NotificationInfo> list = mNotifications.get(packageName);\n            if (list == null) {\n                list = new ArrayList<>();\n                mNotifications.put(packageName, list);\n            }\n            if (!list.contains(notificationInfo)) {\n                list.add(notificationInfo);\n            }\n        }\n    }\n\n    @Override\n    public void cancelAllNotification(String packageName, int userId) {\n        List<NotificationInfo> infos = new ArrayList<>();\n        synchronized (mNotifications) {\n            List<NotificationInfo> list = mNotifications.get(packageName);\n            if (list != null) {\n                int count = list.size();\n                for (int i = count - 1; i >= 0; i--) {\n                    NotificationInfo info = list.get(i);\n                    if (info.userId == userId) {\n                        infos.add(info);\n                        list.remove(i);\n                    }\n                }\n            }\n        }\n        for (NotificationInfo info : infos) {\n            VLog.d(TAG, \"cancel \" + info.tag + \" \" + info.id);\n            mNotificationManager.cancel(info.tag, info.id);\n        }\n    }\n\n    private static class NotificationInfo {\n        int id;\n        String tag;\n        String packageName;\n        int userId;\n\n        NotificationInfo(int id, String tag, String packageName, int userId) {\n            this.id = id;\n            this.tag = tag;\n            this.packageName = packageName;\n            this.userId = userId;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (obj instanceof NotificationInfo) {\n                NotificationInfo that = (NotificationInfo) obj;\n                return that.id == id && TextUtils.equals(that.tag, tag)\n                        && TextUtils.equals(packageName, that.packageName)\n                        && that.userId == userId;\n            }\n            return super.equals(obj);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/notification/WidthCompat.java",
    "content": "package com.lody.virtual.server.notification;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.LinearLayout;\n\nimport com.lody.virtual.helper.utils.OSUtils;\n\n/**\n * Created by 247321453 on 2016/7/17.\n * notification's width\n */\n\n/* package */ class WidthCompat {\n    private final static String TAG = WidthCompat.class.getSimpleName();\n    private volatile int mWidth = 0;\n\n    public int getNotificationWidth(Context context, int width, int height, int padding) {\n        if (mWidth > 0) {\n            return mWidth;\n        }\n        int w = getDefaultWidth(width, padding);\n        if (OSUtils.getInstance().isEmui()) {\n            // huawei's emui\n            w = getEMUINotificationWidth(context, width, height);\n        } else if (OSUtils.getInstance().isMiui()) {\n            if (Build.VERSION.SDK_INT >= 21) {\n                padding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,\n                        context.getResources().getDisplayMetrics()));\n                w = getMIUINotificationWidth(context, width - padding * 2, height);\n            } else {\n                padding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25f,\n                        context.getResources().getDisplayMetrics()));\n                w = getMIUINotificationWidth(context, width - padding * 2, height);\n            }\n        }\n        mWidth = w;\n        return w;\n    }\n\n    private int getDefaultWidth(int width, int padding) {\n        if (Build.VERSION.SDK_INT >= 21)\n            return width - padding * 2;\n        return width;\n    }\n\n\n    private int getMIUINotificationWidth(Context context, int width, int height) {\n        // status_bar_notification_row\n        // adaptive\n        // content\n        try {\n            Context systemUi = context.createPackageContext(NotificationCompat.SYSTEM_UI_PKG,\n                    Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);\n            int layoutId = getSystemId(systemUi, \"status_bar_notification_row\", \"layout\");\n            // status_bar_notification_row\n            if (layoutId != 0) {\n                ViewGroup viewGroup = createViewGroup(systemUi, layoutId);\n\n                int lid = getSystemId(systemUi, \"adaptive\", \"id\");\n                if (lid == 0) {\n                    lid = getSystemId(systemUi, \"content\", \"id\");\n                } else {\n                    // miui5的子view不存在的空指针\n                    View child = viewGroup.findViewById(lid);\n                    if (child != null && child instanceof ViewGroup) {\n                        ((ViewGroup) child).addView(new View(systemUi));\n                    }\n                }\n                layout(viewGroup, width, height);\n                if (lid != 0) {\n                    View child = viewGroup.findViewById(lid);\n                    if (child != null) {\n                        return width - child.getLeft() - child.getPaddingLeft() - child.getPaddingRight();\n                    }\n                } else {\n                    int count = viewGroup.getChildCount();\n                    for (int i = 0; i < count; i++) {\n                        View child = viewGroup.getChildAt(i);\n                        if (FrameLayout.class.isInstance(child) || \"LatestItemView\".equals(child.getClass().getName())\n                                || \"SizeAdaptiveLayout\".equals(child.getClass().getName())) {\n                            return width - child.getLeft() - child.getPaddingLeft() - child.getPaddingRight();// (LinearLayout)child;\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            // ignore\n        }\n        return width;\n    }\n\n    /**\n     * emui 3.0\n     */\n    private int getEMUINotificationWidth(Context context, int width, int height) {\n        try {\n            Context systemUi = context.createPackageContext(NotificationCompat.SYSTEM_UI_PKG,\n                    Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);\n            int layoutId = getSystemId(systemUi, \"time_axis\", \"layout\");\n            if (layoutId != 0) {\n                ViewGroup viewGroup = createViewGroup(systemUi, layoutId);\n                layout(viewGroup, width, height);\n                int lid = getSystemId(systemUi, \"content_view_group\", \"id\");\n                if (lid != 0) {\n                    View child = viewGroup.findViewById(lid);\n                    return width - child.getLeft() - child.getPaddingLeft() - child.getPaddingRight();\n                } else {\n                    int count = viewGroup.getChildCount();\n                    for (int i = 0; i < count; i++) {\n                        View child = viewGroup.getChildAt(i);\n                        if (LinearLayout.class.isInstance(child)) {\n                            // (LinearLayout)child;\n                            return width - child.getLeft() - child.getPaddingLeft() - child.getPaddingRight();\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            // ignore\n        }\n        return width;\n    }\n\n    private int getSystemId(Context systemUi, String name, String type) {\n        return systemUi.getResources().getIdentifier(name, type, NotificationCompat.SYSTEM_UI_PKG);\n    }\n\n    private ViewGroup createViewGroup(Context context, int layoutId) {\n        try {\n            return (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null);\n        } catch (Throwable e) {\n            // ignore\n        }\n        return new FrameLayout(context);\n    }\n\n    private void layout(View view, int width, int height) {\n        view.layout(0, 0, width, height);\n        view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST),\n                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST));\n        view.layout(0, 0, width, height);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/FastImmutableArraySet.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.server.pm;\n\nimport java.util.AbstractSet;\nimport java.util.Iterator;\n\n/**\n * A fast immutable set wrapper for an array that is optimized for\n * non-concurrent iteration. The same iterator instance is reused each time to\n * avoid creating lots of garbage. Iterating over an array in this fashion is\n * 2.5x faster than iterating over a {@link java.util.HashSet} so it is worth\n * copying the contents of the set to an array when iterating over it hundreds\n * of times.\n * \n * @hide\n */\npublic final class FastImmutableArraySet<T> extends AbstractSet<T> {\n\tFastIterator<T> mIterator;\n\tT[] mContents;\n\n\tpublic FastImmutableArraySet(T[] contents) {\n\t\tmContents = contents;\n\t}\n\n\t@Override\n\tpublic Iterator<T> iterator() {\n\t\tFastIterator<T> it = mIterator;\n\t\tif (it == null) {\n\t\t\tit = new FastIterator<T>(mContents);\n\t\t\tmIterator = it;\n\t\t} else {\n\t\t\tit.mIndex = 0;\n\t\t}\n\t\treturn it;\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn mContents.length;\n\t}\n\n\tprivate static final class FastIterator<T> implements Iterator<T> {\n\t\tprivate final T[] mContents;\n\t\tint mIndex;\n\n\t\tpublic FastIterator(T[] contents) {\n\t\t\tmContents = contents;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasNext() {\n\t\t\treturn mIndex != mContents.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic T next() {\n\t\t\treturn mContents[mIndex++];\n\t\t}\n\n\t\t@Override\n\t\tpublic void remove() {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/IntentResolver.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.pm.ResolveInfo;\nimport android.net.Uri;\nimport android.os.Build;\n\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\npublic abstract class IntentResolver<F extends VPackage.IntentInfo, R extends Object> {\n\n\tprivate static final String TAG = \"IntentResolver\";\n\n\t// Sorts a List of IntentFilter objects into descending priority order.\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate static final Comparator sResolvePrioritySorter = new Comparator() {\n\t\tpublic int compare(Object o1, Object o2) {\n\t\t\tint q1;\n\t\t\tint q2;\n\t\t\tif (o1 instanceof IntentFilter) {\n\t\t\t\tq1 = ((IntentFilter) o1).getPriority();\n\t\t\t\tq2 = ((IntentFilter) o2).getPriority();\n\t\t\t} else if (o1 instanceof ResolveInfo) {\n\t\t\t\tResolveInfo r1 = (ResolveInfo) o1;\n\t\t\t\tResolveInfo r2 = (ResolveInfo) o2;\n\t\t\t\tq1 = r1.filter == null ? 0 : r1.filter.getPriority();\n\t\t\t\tq2 = r2.filter == null ? 0 : r2.filter.getPriority();\n\t\t\t} else {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0);\n\t\t}\n\t};\n\t/**\n\t * All filters that have been registered.\n\t */\n\tprivate HashSet<F> mFilters = new HashSet<F>();\n\t/**\n\t * All of the MIME types that have been registered, such as \"image/jpeg\",\n\t * \"image/*\", or \"{@literal *}/*\".\n\t */\n\tprivate HashMap<String, F[]> mTypeToFilter = new HashMap<String, F[]>();\n\t/**\n\t * The base names of all of all fully qualified MIME types that have been\n\t * registered, such as \"image\" or \"*\". Wild card MIME types such as\n\t * \"image/*\" will not be here.\n\t */\n\tprivate HashMap<String, F[]> mBaseTypeToFilter = new HashMap<String, F[]>();\n\t/**\n\t * The base names of all of the MIME types with a sub-type wildcard that\n\t * have been registered. For example, a filter with \"image/*\" will be\n\t * included here as \"image\" but one with \"image/jpeg\" will not be included\n\t * here. This also includes the \"*\" for the \"{@literal *}/*\" MIME type.\n\t */\n\tprivate HashMap<String, F[]> mWildTypeToFilter = new HashMap<String, F[]>();\n\t/**\n\t * All of the URI schemes (such as http) that have been registered.\n\t */\n\tprivate HashMap<String, F[]> mSchemeToFilter = new HashMap<String, F[]>();\n\t/**\n\t * All of the actions that have been registered, but only those that did not\n\t * specify data.\n\t */\n\tprivate HashMap<String, F[]> mActionToFilter = new HashMap<String, F[]>();\n\t/**\n\t * All of the actions that have been registered and specified a MIME type.\n\t */\n\tprivate HashMap<String, F[]> mTypedActionToFilter = new HashMap<String, F[]>();\n\n\tprivate static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {\n\t\tfinal Set<String> categories = intent.getCategories();\n\t\tif (categories == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));\n\t}\n\n\tpublic void addFilter(F f) {\n\n\t\tmFilters.add(f);\n\t\tint numS = register_intent_filter(f, f.filter.schemesIterator(), mSchemeToFilter, \"      Scheme: \");\n\t\tint numT = register_mime_types(f, \"      Type: \");\n\t\tif (numS == 0 && numT == 0) {\n\t\t\tregister_intent_filter(f, f.filter.actionsIterator(), mActionToFilter, \"      Action: \");\n\t\t}\n\t\tif (numT != 0) {\n\t\t\tregister_intent_filter(f, f.filter.actionsIterator(), mTypedActionToFilter, \"      TypedAction: \");\n\t\t}\n\t}\n\n\tprivate boolean filterEquals(IntentFilter f1, IntentFilter f2) {\n\t\tint s1 = f1.countActions();\n\t\tint s2 = f2.countActions();\n\t\tif (s1 != s2) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < s1; i++) {\n\t\t\tif (!f2.hasAction(f1.getAction(i))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\ts1 = f1.countCategories();\n\t\ts2 = f2.countCategories();\n\t\tif (s1 != s2) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < s1; i++) {\n\t\t\tif (!f2.hasCategory(f1.getCategory(i))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\ts1 = f1.countDataTypes();\n\t\ts2 = f2.countDataTypes();\n\t\tif (s1 != s2) {\n\t\t\treturn false;\n\t\t}\n\t\ts1 = f1.countDataSchemes();\n\t\ts2 = f2.countDataSchemes();\n\t\tif (s1 != s2) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0; i < s1; i++) {\n\t\t\tif (!f2.hasDataScheme(f1.getDataScheme(i))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\ts1 = f1.countDataAuthorities();\n\t\ts2 = f2.countDataAuthorities();\n\t\tif (s1 != s2) {\n\t\t\treturn false;\n\t\t}\n\t\ts1 = f1.countDataPaths();\n\t\ts2 = f2.countDataPaths();\n\t\tif (s1 != s2) {\n\t\t\treturn false;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n\t\t\ts1 = f1.countDataSchemeSpecificParts();\n\t\t\ts2 = f2.countDataSchemeSpecificParts();\n\t\t\tif (s1 != s2) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate ArrayList<F> collectFilters(F[] array, IntentFilter matching) {\n\t\tArrayList<F> res = null;\n\t\tif (array != null) {\n\t\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\t\tF cur = array[i];\n\t\t\t\tif (cur == null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (filterEquals(cur.filter, matching)) {\n\t\t\t\t\tif (res == null) {\n\t\t\t\t\t\tres = new ArrayList<>();\n\t\t\t\t\t}\n\t\t\t\t\tres.add(cur);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn res;\n\t}\n\n\tpublic ArrayList<F> findFilters(IntentFilter matching) {\n\t\tif (matching.countDataSchemes() == 1) {\n\t\t\t// Fast case.\n\t\t\treturn collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching);\n\t\t} else if (matching.countDataTypes() != 0 && matching.countActions() == 1) {\n\t\t\t// Another fast case.\n\t\t\treturn collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching);\n\t\t} else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0 && matching.countActions() == 1) {\n\t\t\t// Last fast case.\n\t\t\treturn collectFilters(mActionToFilter.get(matching.getAction(0)), matching);\n\t\t} else {\n\t\t\tArrayList<F> res = null;\n\t\t\tfor (F cur : mFilters) {\n\t\t\t\tif (filterEquals(cur.filter, matching)) {\n\t\t\t\t\tif (res == null) {\n\t\t\t\t\t\tres = new ArrayList<>();\n\t\t\t\t\t}\n\t\t\t\t\tres.add(cur);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\t}\n\n\tpublic void removeFilter(F f) {\n\t\tremoveFilterInternal(f);\n\t\tmFilters.remove(f);\n\t}\n\n\tvoid removeFilterInternal(F f) {\n\n\t\tint numS = unregister_intent_filter(f, f.filter.schemesIterator(), mSchemeToFilter, \"      Scheme: \");\n\t\tint numT = unregister_mime_types(f, \"      Type: \");\n\t\tif (numS == 0 && numT == 0) {\n\t\t\tunregister_intent_filter(f, f.filter.actionsIterator(), mActionToFilter, \"      Action: \");\n\t\t}\n\t\tif (numT != 0) {\n\t\t\tunregister_intent_filter(f, f.filter.actionsIterator(), mTypedActionToFilter, \"      TypedAction: \");\n\t\t}\n\t}\n\n\t/**\n\t * Returns an iterator allowing filters to be removed.\n\t */\n\tpublic Iterator<F> filterIterator() {\n\t\treturn new IteratorWrapper(mFilters.iterator());\n\t}\n\n\t/**\n\t * Returns a read-only set of the filters.\n\t */\n\tpublic Set<F> filterSet() {\n\t\treturn Collections.unmodifiableSet(mFilters);\n\t}\n\n\tpublic List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,\n\t\t\tArrayList<F[]> listCut, int userId) {\n\t\tArrayList<R> resultList = new ArrayList<R>();\n\t\tFastImmutableArraySet<String> categories = getFastIntentCategories(intent);\n\t\tfinal String scheme = intent.getScheme();\n\t\tint N = listCut.size();\n\t\tfor (int i = 0; i < N; ++i) {\n\t\t\tbuildResolveList(intent, categories, defaultOnly, resolvedType, scheme, listCut.get(i), resultList, userId);\n\t\t}\n\t\tsortResults(resultList);\n\t\treturn resultList;\n\t}\n\n\tpublic List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) {\n\t\tString scheme = intent.getScheme();\n\n\t\tArrayList<R> finalList = new ArrayList<R>();\n\t\tF[] firstTypeCut = null;\n\t\tF[] secondTypeCut = null;\n\t\tF[] thirdTypeCut = null;\n\t\tF[] schemeCut = null;\n\n\t\t// If the intent includes a MIME type, then we want to collect all of\n\t\t// the filters that match that MIME type.\n\t\tif (resolvedType != null) {\n\t\t\tint slashpos = resolvedType.indexOf('/');\n\t\t\tif (slashpos > 0) {\n\t\t\t\tfinal String baseType = resolvedType.substring(0, slashpos);\n\t\t\t\tif (!baseType.equals(\"*\")) {\n\t\t\t\t\tif (resolvedType.length() != slashpos + 2 || resolvedType.charAt(slashpos + 1) != '*') {\n\t\t\t\t\t\t// Not a wild card, so we can just look for all filters\n\t\t\t\t\t\t// that\n\t\t\t\t\t\t// completely match or wildcards whose base type\n\t\t\t\t\t\t// matches.\n\t\t\t\t\t\tfirstTypeCut = mTypeToFilter.get(resolvedType);\n\t\t\t\t\t\tsecondTypeCut = mWildTypeToFilter.get(baseType);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// We can match anything with our base type.\n\t\t\t\t\t\tfirstTypeCut = mBaseTypeToFilter.get(baseType);\n\t\t\t\t\t\tsecondTypeCut = mWildTypeToFilter.get(baseType);\n\t\t\t\t\t}\n\t\t\t\t\t// Any */* types always apply, but we only need to do this\n\t\t\t\t\t// if the intent type was not already */*.\n\t\t\t\t\tthirdTypeCut = mWildTypeToFilter.get(\"*\");\n\t\t\t\t} else if (intent.getAction() != null) {\n\t\t\t\t\t// The intent specified any type ({@literal *}/*). This\n\t\t\t\t\t// can be a whole heck of a lot of things, so as a first\n\t\t\t\t\t// cut let's use the action instead.\n\t\t\t\t\tfirstTypeCut = mTypedActionToFilter.get(intent.getAction());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If the intent includes a data URI, then we want to collect all of\n\t\t// the filters that match its scheme (we will further refine matches\n\t\t// on the authority and path by directly matching each resulting\n\t\t// filter).\n\t\tif (scheme != null) {\n\t\t\tschemeCut = mSchemeToFilter.get(scheme);\n\t\t}\n\n\t\t// If the intent does not specify any data -- either a MIME type or\n\t\t// a URI -- then we will only be looking for matches against empty\n\t\t// data.\n\t\tif (resolvedType == null && scheme == null && intent.getAction() != null) {\n\t\t\tfirstTypeCut = mActionToFilter.get(intent.getAction());\n\t\t}\n\n\t\tFastImmutableArraySet<String> categories = getFastIntentCategories(intent);\n\t\tif (firstTypeCut != null) {\n\t\t\tbuildResolveList(intent, categories, defaultOnly, resolvedType, scheme, firstTypeCut, finalList, userId);\n\t\t}\n\t\tif (secondTypeCut != null) {\n\t\t\tbuildResolveList(intent, categories, defaultOnly, resolvedType, scheme, secondTypeCut, finalList, userId);\n\t\t}\n\t\tif (thirdTypeCut != null) {\n\t\t\tbuildResolveList(intent, categories, defaultOnly, resolvedType, scheme, thirdTypeCut, finalList, userId);\n\t\t}\n\t\tif (schemeCut != null) {\n\t\t\tbuildResolveList(intent, categories, defaultOnly, resolvedType, scheme, schemeCut, finalList, userId);\n\t\t}\n\t\tsortResults(finalList);\n\t\treturn finalList;\n\t}\n\n\t/**\n\t * Control whether the given filter is allowed to go into the result list.\n\t * Mainly intended to prevent adding multiple filters for the same target\n\t * object.\n\t */\n\tprotected boolean allowFilterResult(F filter, List<R> dest) {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns whether the object associated with the given filter is \"stopped\",\n\t * that is whether it should not be included in the result if the intent\n\t * requests to excluded stopped objects.\n\t */\n\tprotected boolean isFilterStopped(F filter) {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns whether this filter is owned by this package. This must be\n\t * implemented to provide correct filtering of Intents that have specified a\n\t * package name they are to be delivered to.\n\t */\n\tprotected abstract boolean isPackageForFilter(String packageName, F filter);\n\n\tprotected abstract F[] newArray(int size);\n\n\t@SuppressWarnings(\"unchecked\")\n\tprotected R newResult(F filter, int match, int userId) {\n\t\treturn (R) filter;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprotected void sortResults(List<R> results) {\n\t\tCollections.sort(results, sResolvePrioritySorter);\n\t}\n\n\tprotected void dumpFilter(PrintWriter out, String prefix, F filter) {\n\t\tout.print(prefix);\n\t\tout.println(filter);\n\t}\n\n\tprotected Object filterToLabel(F filter) {\n\t\treturn \"IntentFilter\";\n\t}\n\n\tprotected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {\n\t\tout.print(prefix);\n\t\tout.print(label);\n\t\tout.print(\": \");\n\t\tout.println(count);\n\t}\n\n\tprivate void addFilter(HashMap<String, F[]> map, String name, F filter) {\n\t\tF[] array = map.get(name);\n\t\tif (array == null) {\n\t\t\tarray = newArray(2);\n\t\t\tmap.put(name, array);\n\t\t\tarray[0] = filter;\n\t\t} else {\n\t\t\tfinal int N = array.length;\n\t\t\tint i = N;\n\t\t\twhile (i > 0 && array[i - 1] == null) {\n\t\t\t\ti--;\n\t\t\t}\n\t\t\tif (i < N) {\n\t\t\t\tarray[i] = filter;\n\t\t\t} else {\n\t\t\t\tF[] newa = newArray((N * 3) / 2);\n\t\t\t\tSystem.arraycopy(array, 0, newa, 0, N);\n\t\t\t\tnewa[N] = filter;\n\t\t\t\tmap.put(name, newa);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate int register_mime_types(F filter, String prefix) {\n\t\tfinal Iterator<String> i = filter.filter.typesIterator();\n\t\tif (i == null) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tint num = 0;\n\t\twhile (i.hasNext()) {\n\t\t\tString name = i.next();\n\t\t\tnum++;\n\t\t\tString baseName = name;\n\t\t\tfinal int slashpos = name.indexOf('/');\n\t\t\tif (slashpos > 0) {\n\t\t\t\tbaseName = name.substring(0, slashpos).intern();\n\t\t\t} else {\n\t\t\t\tname = name + \"/*\";\n\t\t\t}\n\n\t\t\taddFilter(mTypeToFilter, name, filter);\n\n\t\t\tif (slashpos > 0) {\n\t\t\t\taddFilter(mBaseTypeToFilter, baseName, filter);\n\t\t\t} else {\n\t\t\t\taddFilter(mWildTypeToFilter, baseName, filter);\n\t\t\t}\n\t\t}\n\n\t\treturn num;\n\t}\n\n\tprivate int unregister_mime_types(F filter, String prefix) {\n\t\tfinal Iterator<String> i = filter.filter.typesIterator();\n\t\tif (i == null) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tint num = 0;\n\t\twhile (i.hasNext()) {\n\t\t\tString name = i.next();\n\t\t\tnum++;\n\t\t\tString baseName = name;\n\t\t\tfinal int slashpos = name.indexOf('/');\n\t\t\tif (slashpos > 0) {\n\t\t\t\tbaseName = name.substring(0, slashpos).intern();\n\t\t\t} else {\n\t\t\t\tname = name + \"/*\";\n\t\t\t}\n\n\t\t\tremove_all_objects(mTypeToFilter, name, filter);\n\n\t\t\tif (slashpos > 0) {\n\t\t\t\tremove_all_objects(mBaseTypeToFilter, baseName, filter);\n\t\t\t} else {\n\t\t\t\tremove_all_objects(mWildTypeToFilter, baseName, filter);\n\t\t\t}\n\t\t}\n\t\treturn num;\n\t}\n\n\tprivate int register_intent_filter(F filter, Iterator<String> i, HashMap<String, F[]> dest, String prefix) {\n\t\tif (i == null) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tint num = 0;\n\t\twhile (i.hasNext()) {\n\t\t\tString name = i.next();\n\t\t\tnum++;\n\t\t\taddFilter(dest, name, filter);\n\t\t}\n\t\treturn num;\n\t}\n\n\tprivate int unregister_intent_filter(F filter, Iterator<String> i, HashMap<String, F[]> dest, String prefix) {\n\t\tif (i == null) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tint num = 0;\n\t\twhile (i.hasNext()) {\n\t\t\tString name = i.next();\n\t\t\tnum++;\n\t\t\tremove_all_objects(dest, name, filter);\n\t\t}\n\t\treturn num;\n\t}\n\n\tprivate void remove_all_objects(HashMap<String, F[]> map, String name, Object object) {\n\t\tF[] array = map.get(name);\n\t\tif (array != null) {\n\t\t\tint LAST = array.length - 1;\n\t\t\twhile (LAST >= 0 && array[LAST] == null) {\n\t\t\t\tLAST--;\n\t\t\t}\n\t\t\tfor (int idx = LAST; idx >= 0; idx--) {\n\t\t\t\tif (array[idx] == object) {\n\t\t\t\t\tfinal int remain = LAST - idx;\n\t\t\t\t\tif (remain > 0) {\n\t\t\t\t\t\tSystem.arraycopy(array, idx + 1, array, idx, remain);\n\t\t\t\t\t}\n\t\t\t\t\tarray[LAST] = null;\n\t\t\t\t\tLAST--;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (LAST < 0) {\n\t\t\t\tmap.remove(name);\n\t\t\t} else if (LAST < (array.length / 2)) {\n\t\t\t\tF[] newa = newArray(LAST + 2);\n\t\t\t\tSystem.arraycopy(array, 0, newa, 0, LAST + 1);\n\t\t\t\tmap.put(name, newa);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,\n\t\t\t\t\t\t\t\t  boolean defaultOnly, String resolvedType, String scheme, F[] src, List<R> dest, int userId) {\n\t\tfinal String action = intent.getAction();\n\t\tfinal Uri data = intent.getData();\n\t\tfinal String packageName = intent.getPackage();\n\n\t\tfinal int N = src != null ? src.length : 0;\n\t\tboolean hasNonDefaults = false;\n\t\tint i;\n\t\tF filter;\n\t\tfor (i = 0; i < N && (filter = src[i]) != null; i++) {\n\t\t\tint match;\n\n\t\t\t// Is delivery being limited to filters owned by a particular\n\t\t\t// package?\n\t\t\tif (packageName != null && !isPackageForFilter(packageName, filter)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Do we already have this one?\n\t\t\tif (!allowFilterResult(filter, dest)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmatch = filter.filter.match(action, resolvedType, scheme, data, categories, TAG);\n\t\t\tif (match >= 0) {\n\t\t\t\tif (!defaultOnly || filter.filter.hasCategory(Intent.CATEGORY_DEFAULT)) {\n\t\t\t\t\tfinal R oneResult = newResult(filter, match, userId);\n\t\t\t\t\tif (oneResult != null) {\n\t\t\t\t\t\tdest.add(oneResult);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\thasNonDefaults = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (hasNonDefaults) {\n\t\t\tif (dest.size() == 0) {\n\t\t\t\tVLog.w(TAG, \"resolveIntent failed: found match, but none with CATEGORY_DEFAULT\");\n\t\t\t} else if (dest.size() > 1) {\n\t\t\t\tVLog.w(TAG, \"resolveIntent: multiple matches, only some with CATEGORY_DEFAULT\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class IteratorWrapper implements Iterator<F> {\n\t\tprivate Iterator<F> mI;\n\t\tprivate F mCur;\n\n\t\tIteratorWrapper(Iterator<F> it) {\n\t\t\tmI = it;\n\t\t}\n\n\t\tpublic boolean hasNext() {\n\t\t\treturn mI.hasNext();\n\t\t}\n\n\t\tpublic F next() {\n\t\t\treturn (mCur = mI.next());\n\t\t}\n\n\t\tpublic void remove() {\n\t\t\tif (mCur != null) {\n\t\t\t\tremoveFilterInternal(mCur);\n\t\t\t}\n\t\t\tmI.remove();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/PackageCacheManager.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.server.pm.parser.PackageParserEx;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\n/**\n * @author Lody\n */\n\npublic class PackageCacheManager {\n\n    static final ArrayMap<String, VPackage> PACKAGE_CACHE = new ArrayMap<>();\n\n    public static int size() {\n        synchronized (PACKAGE_CACHE) {\n            return PACKAGE_CACHE.size();\n        }\n    }\n\n    public static void put(VPackage pkg, PackageSetting ps) {\n        synchronized (PackageCacheManager.class) {\n            PackageParserEx.initApplicationInfoBase(ps, pkg);\n            PACKAGE_CACHE.put(pkg.packageName, pkg);\n            pkg.mExtras = ps;\n            VPackageManagerService.get().analyzePackageLocked(pkg);\n        }\n    }\n\n    public static VPackage get(String packageName) {\n        synchronized (PackageCacheManager.class) {\n            return PACKAGE_CACHE.get(packageName);\n        }\n    }\n\n    public static PackageSetting getSetting(String packageName) {\n        synchronized (PackageCacheManager.class) {\n            VPackage p = PACKAGE_CACHE.get(packageName);\n            if (p != null) {\n                return (PackageSetting) p.mExtras;\n            }\n            return null;\n        }\n    }\n\n    public static VPackage remove(String packageName) {\n        synchronized (PackageCacheManager.class) {\n            VPackageManagerService.get().deletePackageLocked(packageName);\n            return PACKAGE_CACHE.remove(packageName);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/PackagePersistenceLayer.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.os.Parcel;\n\nimport com.lody.virtual.helper.PersistenceLayer;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.util.Arrays;\n\n/**\n * @author Lody\n */\n\nclass PackagePersistenceLayer extends PersistenceLayer {\n\n    private static final char[] MAGIC = {'v', 'p', 'k', 'g'};\n    private static final int CURRENT_VERSION = 3;\n\n    private VAppManagerService mService;\n\n    PackagePersistenceLayer(VAppManagerService service) {\n        super(VEnvironment.getPackageListFile());\n        mService = service;\n    }\n\n    @Override\n    public int getCurrentVersion() {\n        return CURRENT_VERSION;\n    }\n\n    @Override\n    public void writeMagic(Parcel p) {\n        p.writeCharArray(MAGIC);\n    }\n\n    @Override\n    public boolean verifyMagic(Parcel p) {\n        char[] magic = p.createCharArray();\n        return Arrays.equals(magic, MAGIC);\n    }\n\n    // 将 pkg 的 信息写进 parcel\n    @Override\n    public void writePersistenceData(Parcel p) {\n        synchronized (PackageCacheManager.PACKAGE_CACHE) {\n            p.writeInt(PackageCacheManager.PACKAGE_CACHE.size());\n            for (VPackage pkg : PackageCacheManager.PACKAGE_CACHE.values()) {\n                PackageSetting ps = (PackageSetting) pkg.mExtras;\n                ps.writeToParcel(p, 0);\n            }\n        }\n    }\n\n    @Override\n    public void readPersistenceData(Parcel p) {\n        int count = p.readInt();\n        while (count-- > 0) {\n            PackageSetting setting = new PackageSetting(p);\n            mService.loadPackage(setting);\n        }\n    }\n\n    @Override\n    public boolean onVersionConflict(int fileVersion, int currentVersion) {\n        // I am so lazy to process it...\n        return false;\n    }\n\n    @Override\n    public void onPersistenceFileDamage() {\n        getPersistenceFile().delete();\n        VAppManagerService.get().restoreFactoryState();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/PackageSetting.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.SparseArray;\n\nimport com.lody.virtual.remote.InstalledAppInfo;\n\n/**\n * @author Lody\n */\n\npublic class PackageSetting implements Parcelable {\n\n    public static final Parcelable.Creator<PackageSetting> CREATOR = new Parcelable.Creator<PackageSetting>() {\n        @Override\n        public PackageSetting createFromParcel(Parcel source) {\n            return new PackageSetting(source);\n        }\n\n        @Override\n        public PackageSetting[] newArray(int size) {\n            return new PackageSetting[size];\n        }\n    };\n    private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();\n    public String packageName;\n    public String apkPath;\n    public String libPath;\n    public boolean dependSystem;\n    /**\n     * In this mode we skip the dex2oat so we can load the class.dex very fast.\n     */\n    public boolean skipDexOpt;\n    public int appId;\n    public long firstInstallTime;\n    public long lastUpdateTime;\n    private SparseArray<PackageUserState> userState = new SparseArray<>();\n\n    public PackageSetting() {\n    }\n\n    protected PackageSetting(Parcel in) {\n        this.packageName = in.readString();\n        this.apkPath = in.readString();\n        this.libPath = in.readString();\n        this.dependSystem = in.readByte() != 0;\n        this.appId = in.readInt();\n        //noinspection unchecked\n        this.userState = in.readSparseArray(PackageUserState.class.getClassLoader());\n        this.skipDexOpt = in.readByte() != 0;\n    }\n\n    public InstalledAppInfo getAppInfo() {\n        return new InstalledAppInfo(packageName, apkPath, libPath, dependSystem, skipDexOpt, appId);\n    }\n\n    PackageUserState modifyUserState(int userId) {\n        PackageUserState state = userState.get(userId);\n        if (state == null) {\n            state = new PackageUserState();\n            userState.put(userId, state);\n        }\n        return state;\n    }\n\n    void setUserState(int userId, boolean launched, boolean hidden, boolean installed) {\n        PackageUserState state = modifyUserState(userId);\n        state.launched = launched;\n        state.hidden = hidden;\n        state.installed = installed;\n    }\n\n    PackageUserState readUserState(int userId) {\n        PackageUserState state = userState.get(userId);\n        if (state != null) {\n            return state;\n        }\n        return DEFAULT_USER_STATE;\n    }\n\n    void removeUser(int userId) {\n        userState.delete(userId);\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(this.packageName);\n        dest.writeString(this.apkPath);\n        dest.writeString(this.libPath);\n        dest.writeByte(this.dependSystem ? (byte) 1 : (byte) 0);\n        dest.writeInt(this.appId);\n        //noinspection unchecked\n        dest.writeSparseArray((SparseArray) this.userState);\n        dest.writeByte(this.skipDexOpt ? (byte) 1 : (byte) 0);\n    }\n\n    public boolean isLaunched(int userId) {\n        return readUserState(userId).launched;\n    }\n\n    public boolean isHidden(int userId) {\n        return readUserState(userId).hidden;\n    }\n\n    public boolean isInstalled(int userId) {\n        return readUserState(userId).installed;\n    }\n\n    public void setLaunched(int userId, boolean launched) {\n        modifyUserState(userId).launched = launched;\n    }\n\n    public void setHidden(int userId, boolean hidden) {\n        modifyUserState(userId).hidden = hidden;\n    }\n\n    public void setInstalled(int userId, boolean installed) {\n        modifyUserState(userId).installed = installed;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/PackageUserState.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n */\n\npublic class PackageUserState implements Parcelable {\n\n    public static final Parcelable.Creator<PackageUserState> CREATOR = new Parcelable.Creator<PackageUserState>() {\n        @Override\n        public PackageUserState createFromParcel(Parcel source) {\n            return new PackageUserState(source);\n        }\n\n        @Override\n        public PackageUserState[] newArray(int size) {\n            return new PackageUserState[size];\n        }\n    };\n    public boolean launched;\n    public boolean hidden;\n    public boolean installed;\n\n    public PackageUserState() {\n        installed = false;\n        launched = true;\n        hidden = false;\n    }\n\n    protected PackageUserState(Parcel in) {\n        this.launched = in.readByte() != 0;\n        this.hidden = in.readByte() != 0;\n        this.installed = in.readByte() != 0;\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeByte(this.launched ? (byte) 1 : (byte) 0);\n        dest.writeByte(this.hidden ? (byte) 1 : (byte) 0);\n        dest.writeByte(this.installed ? (byte) 1 : (byte) 0);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/PrivilegeAppOptimizer.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.content.Intent;\n\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.server.am.VActivityManagerService;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author Lody\n */\n\npublic class PrivilegeAppOptimizer {\n\n    private static final PrivilegeAppOptimizer sInstance = new PrivilegeAppOptimizer();\n    private final List<String> privilegeApps = new ArrayList<>();\n\n    private PrivilegeAppOptimizer() {\n        Collections.addAll(privilegeApps, VASettings.PRIVILEGE_APPS);\n    }\n\n    public static PrivilegeAppOptimizer get() {\n        return sInstance;\n    }\n\n    public List<String> getPrivilegeApps() {\n        return Collections.unmodifiableList(privilegeApps);\n    }\n\n    public void addPrivilegeApp(String packageName) {\n        privilegeApps.add(packageName);\n    }\n\n    public void removePrivilegeApp(String packageName) {\n        privilegeApps.remove(packageName);\n    }\n\n    public boolean isPrivilegeApp(String packageName) {\n        return privilegeApps.contains(packageName);\n    }\n\n    public void performOptimizeAllApps() {\n        for (String pkg : privilegeApps) {\n            performOptimize(pkg, VUserHandle.USER_ALL);\n        }\n    }\n\n    public boolean performOptimize(String packageName, int userId) {\n        if (!isPrivilegeApp(packageName)) {\n            return false;\n        }\n        VActivityManagerService.get().sendBroadcastAsUser(\n                specifyApp(new Intent(Intent.ACTION_BOOT_COMPLETED, null), packageName, userId)\n                , new VUserHandle(userId));\n        return true;\n    }\n\n    private Intent specifyApp(Intent intent, String packageName, int userId) {\n        intent.putExtra(\"_VA_|_privilege_pkg_\", packageName);\n        intent.putExtra(\"_VA_|_user_id_\", userId);\n        return intent;\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/ProviderIntentResolver.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.annotation.TargetApi;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ResolveInfo;\nimport android.os.Build;\n\nimport com.lody.virtual.helper.compat.ObjectsCompat;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.server.pm.parser.PackageParserEx;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport static com.lody.virtual.server.pm.VPackageManagerService.TAG;\n\nfinal class ProviderIntentResolver extends IntentResolver<VPackage.ProviderIntentInfo, ResolveInfo> {\n    private final HashMap<ComponentName, VPackage.ProviderComponent> mProviders = new HashMap<>();\n    private int mFlags;\n\n    public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) {\n        mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;\n        return super.queryIntent(intent, resolvedType, defaultOnly, userId);\n    }\n\n    public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) {\n        mFlags = flags;\n        return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);\n    }\n\n    public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags,\n                                                   ArrayList<VPackage.ProviderComponent> packageProviders, int userId) {\n        if (packageProviders == null) {\n            return null;\n        }\n        mFlags = flags;\n        final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;\n        final int N = packageProviders.size();\n        ArrayList<VPackage.ProviderIntentInfo[]> listCut = new ArrayList<>(N);\n\n        ArrayList<VPackage.ProviderIntentInfo> intentFilters;\n        for (int i = 0; i < N; ++i) {\n            intentFilters = packageProviders.get(i).intents;\n            if (intentFilters != null && intentFilters.size() > 0) {\n                VPackage.ProviderIntentInfo[] array = new VPackage.ProviderIntentInfo[intentFilters\n                        .size()];\n                intentFilters.toArray(array);\n                listCut.add(array);\n            }\n        }\n        return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);\n    }\n\n    public final void addProvider(VPackage.ProviderComponent p) {\n        if (mProviders.containsKey(p.getComponentName())) {\n            VLog.w(TAG, \"Provider \" + p.getComponentName() + \" already defined; ignoring\");\n            return;\n        }\n\n        mProviders.put(p.getComponentName(), p);\n        final int NI = p.intents.size();\n        int j;\n        for (j = 0; j < NI; j++) {\n            VPackage.ProviderIntentInfo intent = p.intents.get(j);\n            addFilter(intent);\n        }\n    }\n\n    public final void removeProvider(VPackage.ProviderComponent p) {\n        mProviders.remove(p.getComponentName());\n        final int NI = p.intents.size();\n        int j;\n        for (j = 0; j < NI; j++) {\n            VPackage.ProviderIntentInfo intent = p.intents.get(j);\n            removeFilter(intent);\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    @Override\n    protected boolean allowFilterResult(VPackage.ProviderIntentInfo filter, List<ResolveInfo> dest) {\n        ProviderInfo filterPi = filter.provider.info;\n        for (int i = dest.size() - 1; i >= 0; i--) {\n            ProviderInfo destPi = dest.get(i).providerInfo;\n            if (ObjectsCompat.equals(destPi.name, filterPi.name)\n                    && ObjectsCompat.equals(destPi.packageName, filterPi.packageName)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    protected VPackage.ProviderIntentInfo[] newArray(int size) {\n        return new VPackage.ProviderIntentInfo[size];\n    }\n\n    @Override\n    protected boolean isFilterStopped(VPackage.ProviderIntentInfo filter) {\n        return false;\n    }\n\n    @Override\n    protected boolean isPackageForFilter(String packageName, VPackage.ProviderIntentInfo info) {\n        return packageName.equals(info.provider.owner.packageName);\n    }\n\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    @Override\n    protected ResolveInfo newResult(VPackage.ProviderIntentInfo filter, int match, int userId) {\n        final VPackage.ProviderComponent provider = filter.provider;\n        PackageSetting ps = (PackageSetting) provider.owner.mExtras;\n        ProviderInfo pi = PackageParserEx.generateProviderInfo(provider, mFlags, ps.readUserState(userId), userId);\n        if (pi == null) {\n            return null;\n        }\n        final ResolveInfo res = new ResolveInfo();\n        res.providerInfo = pi;\n        if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {\n            res.filter = filter.filter;\n        }\n        res.priority = filter.filter.getPriority();\n        res.preferredOrder = provider.owner.mPreferredOrder;\n        res.match = match;\n        res.isDefault = filter.hasDefault;\n        res.labelRes = filter.labelRes;\n        res.nonLocalizedLabel = filter.nonLocalizedLabel;\n        res.icon = filter.icon;\n        return res;\n    }\n\n    @Override\n    protected void sortResults(List<ResolveInfo> results) {\n        Collections.sort(results, VPackageManagerService.sResolvePrioritySorter);\n    }\n\n    @Override\n    protected void dumpFilter(PrintWriter out, String prefix, VPackage.ProviderIntentInfo filter) {\n\n    }\n\n    @Override\n    protected Object filterToLabel(VPackage.ProviderIntentInfo filter) {\n        return filter.provider;\n    }\n\n    protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {\n\n    }\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.RemoteCallbackList;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.core.InstallStrategy;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.collection.IntArray;\nimport com.lody.virtual.helper.compat.NativeLibraryHelperCompat;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.FileUtils;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.InstallResult;\nimport com.lody.virtual.remote.InstalledAppInfo;\nimport com.lody.virtual.server.IAppManager;\nimport com.lody.virtual.server.accounts.VAccountManagerService;\nimport com.lody.virtual.server.am.BroadcastSystem;\nimport com.lody.virtual.server.am.UidSystem;\nimport com.lody.virtual.server.am.VActivityManagerService;\nimport com.lody.virtual.server.interfaces.IAppRequestListener;\nimport com.lody.virtual.server.interfaces.IPackageObserver;\nimport com.lody.virtual.server.pm.parser.PackageParserEx;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\n\n/**\n * @author Lody\n */\npublic class VAppManagerService extends IAppManager.Stub {\n\n    private static final String TAG = VAppManagerService.class.getSimpleName();\n    private static final AtomicReference<VAppManagerService> sService = new AtomicReference<>();\n    private final UidSystem mUidSystem = new UidSystem();\n    private final PackagePersistenceLayer mPersistenceLayer = new PackagePersistenceLayer(this);\n    private final Set<String> mVisibleOutsidePackages = new HashSet<>();\n    private boolean mBooting;\n    private RemoteCallbackList<IPackageObserver> mRemoteCallbackList = new RemoteCallbackList<>();\n    private IAppRequestListener mAppRequestListener;\n\n    public static VAppManagerService get() {\n        return sService.get();\n    }\n\n    public static void systemReady() {\n        VEnvironment.systemReady();\n        VAppManagerService instance = new VAppManagerService();\n        instance.mUidSystem.initUidList();\n        sService.set(instance);\n    }\n\n    public boolean isBooting() {\n        return mBooting;\n    }\n\n    @Override\n    public void scanApps() {\n        if (mBooting) {\n            return;\n        }\n        synchronized (this) {\n            mBooting = true;\n            mPersistenceLayer.read();\n            PrivilegeAppOptimizer.get().performOptimizeAllApps();\n            mBooting = false;\n        }\n    }\n\n    private void cleanUpResidualFiles(PackageSetting ps) {\n        File dataAppDir = VEnvironment.getDataAppPackageDirectory(ps.packageName);\n        FileUtils.deleteDir(dataAppDir);\n        for (int userId : VUserManagerService.get().getUserIds()) {\n            FileUtils.deleteDir(VEnvironment.getDataUserPackageDirectory(userId, ps.packageName));\n        }\n    }\n\n    // 加载 Package\n    synchronized void loadPackage(PackageSetting setting) {\n        if (!loadPackageInnerLocked(setting)) {\n            cleanUpResidualFiles(setting);\n        }\n    }\n\n    private boolean loadPackageInnerLocked(PackageSetting ps) {\n        if (ps.dependSystem) {\n            if (!VirtualCore.get().isOutsideInstalled(ps.packageName)) {\n                return false;\n            }\n        }\n        File cacheFile = VEnvironment.getPackageCacheFile(ps.packageName);\n        VPackage pkg = null;\n        try {\n            // 读取安装时解析好的 VPackage Cache\n            pkg = PackageParserEx.readPackageCache(ps.packageName);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        if (pkg == null || pkg.packageName == null) {\n            return false;\n        }\n        // 赋予权限\n        chmodPackageDictionary(cacheFile);\n        // add cache to ram\n        PackageCacheManager.put(pkg, ps);\n        BroadcastSystem.get().startApp(pkg);\n        return true;\n    }\n\n    @Override\n    public boolean isOutsidePackageVisible(String pkg) {\n        return pkg != null && mVisibleOutsidePackages.contains(pkg);\n    }\n\n    @Override\n    public void addVisibleOutsidePackage(String pkg) {\n        if (pkg != null) {\n            mVisibleOutsidePackages.add(pkg);\n        }\n    }\n\n    @Override\n    public void removeVisibleOutsidePackage(String pkg) {\n        if (pkg != null) {\n            mVisibleOutsidePackages.remove(pkg);\n        }\n    }\n\n    @Override\n    public InstallResult installPackage(String path, int flags) {\n        return installPackage(path, flags, true);\n    }\n\n    // 安装 apk 先于 installPackageAsUser，主要目的是生成 VPackage 结构\n    public synchronized InstallResult installPackage(String path, int flags, boolean notify) {\n        long installTime = System.currentTimeMillis();\n        if (path == null) {\n            return InstallResult.makeFailure(\"path = NULL\");\n        }\n        // 是否 OPT 优化(dex -> binary)\n        boolean skipDexOpt = (flags & InstallStrategy.SKIP_DEX_OPT) != 0;\n        // apk path\n        File packageFile = new File(path);\n        if (!packageFile.exists() || !packageFile.isFile()) {\n            return InstallResult.makeFailure(\"Package File is not exist.\");\n        }\n        VPackage pkg = null;\n        try {\n            // 进入解析包结构，该结构是可序列化的，为了持久化在磁盘上\n            pkg = PackageParserEx.parsePackage(packageFile);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        if (pkg == null || pkg.packageName == null) {\n            return InstallResult.makeFailure(\"Unable to parse the package.\");\n        }\n        InstallResult res = new InstallResult();\n        res.packageName = pkg.packageName;\n        // PackageCache holds all packages, try to check if we need to update.\n        VPackage existOne = PackageCacheManager.get(pkg.packageName);\n        PackageSetting existSetting = existOne != null ? (PackageSetting) existOne.mExtras : null;\n        if (existOne != null) {\n            if ((flags & InstallStrategy.IGNORE_NEW_VERSION) != 0) {\n                res.isUpdate = true;\n                return res;\n            }\n            if (!canUpdate(existOne, pkg, flags)) {\n                return InstallResult.makeFailure(\"Not allowed to update the package.\");\n            }\n            res.isUpdate = true;\n        }\n        // 获得 app 安装文件夹\n        File appDir = VEnvironment.getDataAppPackageDirectory(pkg.packageName);\n        // so 文件夹\n        File libDir = new File(appDir, \"lib\");\n        if (res.isUpdate) {\n            FileUtils.deleteDir(libDir);\n            VEnvironment.getOdexFile(pkg.packageName).delete();\n            VActivityManagerService.get().killAppByPkg(pkg.packageName, VUserHandle.USER_ALL);\n        }\n        if (!libDir.exists() && !libDir.mkdirs()) {\n            return InstallResult.makeFailure(\"Unable to create lib dir.\");\n        }\n\n        // 是否基于系统的 apk 加载，前提是安装过的 apk 并且 dependSystem 开关打开\n        boolean dependSystem = (flags & InstallStrategy.DEPEND_SYSTEM_IF_EXIST) != 0\n                && VirtualCore.get().isOutsideInstalled(pkg.packageName);\n\n        if (existSetting != null && existSetting.dependSystem) {\n            dependSystem = false;\n        }\n        // 复制 so 到 sandbox lib\n        NativeLibraryHelperCompat.copyNativeBinaries(new File(path), libDir);\n\n        // 如果不基于系统，一些必要的拷贝工作\n        if (!dependSystem) {\n            File privatePackageFile = new File(appDir, \"base.apk\");\n            File parentFolder = privatePackageFile.getParentFile();\n            if (!parentFolder.exists() && !parentFolder.mkdirs()) {\n                VLog.w(TAG, \"Warning: unable to create folder : \" + privatePackageFile.getPath());\n            } else if (privatePackageFile.exists() && !privatePackageFile.delete()) {\n                VLog.w(TAG, \"Warning: unable to delete file : \" + privatePackageFile.getPath());\n            }\n            try {\n                FileUtils.copyFile(packageFile, privatePackageFile);\n            } catch (IOException e) {\n                privatePackageFile.delete();\n                return InstallResult.makeFailure(\"Unable to copy the package file.\");\n            }\n            packageFile = privatePackageFile;\n        }\n        if (existOne != null) {\n            PackageCacheManager.remove(pkg.packageName);\n        }\n\n        // 给上可执行权限，5.0 之后在 SD 卡上执行 bin 需要可执行权限\n        chmodPackageDictionary(packageFile);\n\n        // PackageSetting 的一些配置，后面会序列化在磁盘上\n        PackageSetting ps;\n        if (existSetting != null) {\n            ps = existSetting;\n        } else {\n            ps = new PackageSetting();\n        }\n        ps.skipDexOpt = skipDexOpt;\n        ps.dependSystem = dependSystem;\n        ps.apkPath = packageFile.getPath();\n        ps.libPath = libDir.getPath();\n        ps.packageName = pkg.packageName;\n        ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));\n        if (res.isUpdate) {\n            ps.lastUpdateTime = installTime;\n        } else {\n            ps.firstInstallTime = installTime;\n            ps.lastUpdateTime = installTime;\n            for (int userId : VUserManagerService.get().getUserIds()) {\n                boolean installed = userId == 0;\n                ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);\n            }\n        }\n        //保存 VPackage Cache 到 Disk\n        PackageParserEx.savePackageCache(pkg);\n        //保存到 RamCache\n        PackageCacheManager.put(pkg, ps);\n        mPersistenceLayer.save();\n        BroadcastSystem.get().startApp(pkg);\n        //发送通知 安装完成\n        if (notify) {\n            notifyAppInstalled(ps, -1);\n        }\n        res.isSuccess = true;\n        return res;\n    }\n\n    // 安装 package\n    @Override\n    public synchronized boolean installPackageAsUser(int userId, String packageName) {\n        // 判断用户是否存在\n        if (VUserManagerService.get().exists(userId)) {\n            // package 的缓存 VPackage 存储着一个 App 的所有信息\n            PackageSetting ps = PackageCacheManager.getSetting(packageName);\n            if (ps != null) {\n                if (!ps.isInstalled(userId)) {\n                    ps.setInstalled(userId, true);\n                    // 发送已安装广播\n                    notifyAppInstalled(ps, userId);\n\n                    mPersistenceLayer.save();\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private void chmodPackageDictionary(File packageFile) {\n        try {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                if (FileUtils.isSymlink(packageFile)) {\n                    return;\n                }\n                FileUtils.chmod(packageFile.getParentFile().getAbsolutePath(), FileUtils.FileMode.MODE_755);\n                FileUtils.chmod(packageFile.getAbsolutePath(), FileUtils.FileMode.MODE_755);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private boolean canUpdate(VPackage existOne, VPackage newOne, int flags) {\n        if ((flags & InstallStrategy.COMPARE_VERSION) != 0) {\n            if (existOne.mVersionCode < newOne.mVersionCode) {\n                return true;\n            }\n        }\n        if ((flags & InstallStrategy.TERMINATE_IF_EXIST) != 0) {\n            return false;\n        }\n        if ((flags & InstallStrategy.UPDATE_IF_EXIST) != 0) {\n            return true;\n        }\n        return false;\n    }\n\n\n    @Override\n    public synchronized boolean uninstallPackage(String packageName) {\n        PackageSetting ps = PackageCacheManager.getSetting(packageName);\n        if (ps != null) {\n            uninstallPackageFully(ps);\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public synchronized boolean uninstallPackageAsUser(String packageName, int userId) {\n        if (!VUserManagerService.get().exists(userId)) {\n            return false;\n        }\n        PackageSetting ps = PackageCacheManager.getSetting(packageName);\n        if (ps != null) {\n            int[] userIds = getPackageInstalledUsers(packageName);\n            if (!ArrayUtils.contains(userIds, userId)) {\n                return false;\n            }\n            if (userIds.length == 1) {\n                uninstallPackageFully(ps);\n            } else {\n                // Just hidden it\n                VActivityManagerService.get().killAppByPkg(packageName, userId);\n                ps.setInstalled(userId, false);\n                notifyAppUninstalled(ps, userId);\n                mPersistenceLayer.save();\n                FileUtils.deleteDir(VEnvironment.getDataUserPackageDirectory(userId, packageName));\n            }\n            return true;\n        }\n        return false;\n    }\n\n    private void uninstallPackageFully(PackageSetting ps) {\n        String packageName = ps.packageName;\n        try {\n            BroadcastSystem.get().stopApp(packageName);\n            VActivityManagerService.get().killAppByPkg(packageName, VUserHandle.USER_ALL);\n            VEnvironment.getPackageResourcePath(packageName).delete();\n            FileUtils.deleteDir(VEnvironment.getDataAppPackageDirectory(packageName));\n            VEnvironment.getOdexFile(packageName).delete();\n            for (int id : VUserManagerService.get().getUserIds()) {\n                FileUtils.deleteDir(VEnvironment.getDataUserPackageDirectory(id, packageName));\n            }\n            PackageCacheManager.remove(packageName);\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            notifyAppUninstalled(ps, -1);\n        }\n    }\n\n    @Override\n    public int[] getPackageInstalledUsers(String packageName) {\n        PackageSetting ps = PackageCacheManager.getSetting(packageName);\n        if (ps != null) {\n            IntArray installedUsers = new IntArray(5);\n            int[] userIds = VUserManagerService.get().getUserIds();\n            for (int userId : userIds) {\n                if (ps.readUserState(userId).installed) {\n                    installedUsers.add(userId);\n                }\n            }\n            return installedUsers.getAll();\n        }\n        return new int[0];\n    }\n\n    @Override\n    public List<InstalledAppInfo> getInstalledApps(int flags) {\n        List<InstalledAppInfo> infoList = new ArrayList<>(getInstalledAppCount());\n        for (VPackage p : PackageCacheManager.PACKAGE_CACHE.values()) {\n            PackageSetting setting = (PackageSetting) p.mExtras;\n            infoList.add(setting.getAppInfo());\n        }\n        return infoList;\n    }\n\n    @Override\n    public List<InstalledAppInfo> getInstalledAppsAsUser(int userId, int flags) {\n        List<InstalledAppInfo> infoList = new ArrayList<>(getInstalledAppCount());\n        for (VPackage p : PackageCacheManager.PACKAGE_CACHE.values()) {\n            PackageSetting setting = (PackageSetting) p.mExtras;\n            boolean visible = setting.isInstalled(userId);\n            if ((flags & VirtualCore.GET_HIDDEN_APP) == 0 && setting.isHidden(userId)) {\n                visible = false;\n            }\n            if (visible) {\n                infoList.add(setting.getAppInfo());\n            }\n        }\n        return infoList;\n    }\n\n    @Override\n    public int getInstalledAppCount() {\n        return PackageCacheManager.PACKAGE_CACHE.size();\n    }\n\n    @Override\n    public boolean isAppInstalled(String packageName) {\n        return packageName != null && PackageCacheManager.PACKAGE_CACHE.containsKey(packageName);\n    }\n\n    @Override\n    public boolean isAppInstalledAsUser(int userId, String packageName) {\n        if (packageName == null || !VUserManagerService.get().exists(userId)) {\n            return false;\n        }\n        PackageSetting setting = PackageCacheManager.getSetting(packageName);\n        if (setting == null) {\n            return false;\n        }\n        return setting.isInstalled(userId);\n    }\n\n    private void notifyAppInstalled(PackageSetting setting, int userId) {\n        final String pkg = setting.packageName;\n        int N = mRemoteCallbackList.beginBroadcast();\n        while (N-- > 0) {\n            try {\n                if (userId == -1) {\n                    sendInstalledBroadcast(pkg);\n                    mRemoteCallbackList.getBroadcastItem(N).onPackageInstalled(pkg);\n                    mRemoteCallbackList.getBroadcastItem(N).onPackageInstalledAsUser(0, pkg);\n\n                } else {\n                    mRemoteCallbackList.getBroadcastItem(N).onPackageInstalledAsUser(userId, pkg);\n                }\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n        mRemoteCallbackList.finishBroadcast();\n        VAccountManagerService.get().refreshAuthenticatorCache(null);\n    }\n\n    private void notifyAppUninstalled(PackageSetting setting, int userId) {\n        final String pkg = setting.packageName;\n        int N = mRemoteCallbackList.beginBroadcast();\n        while (N-- > 0) {\n            try {\n                if (userId == -1) {\n                    sendUninstalledBroadcast(pkg);\n                    mRemoteCallbackList.getBroadcastItem(N).onPackageUninstalled(pkg);\n                    mRemoteCallbackList.getBroadcastItem(N).onPackageUninstalledAsUser(0, pkg);\n                } else {\n                    mRemoteCallbackList.getBroadcastItem(N).onPackageUninstalledAsUser(userId, pkg);\n                }\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n        mRemoteCallbackList.finishBroadcast();\n        VAccountManagerService.get().refreshAuthenticatorCache(null);\n    }\n\n\n    private void sendInstalledBroadcast(String packageName) {\n        Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);\n        intent.setData(Uri.parse(\"package:\" + packageName));\n        VActivityManagerService.get().sendBroadcastAsUser(intent, VUserHandle.ALL);\n    }\n\n    private void sendUninstalledBroadcast(String packageName) {\n        Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);\n        intent.setData(Uri.parse(\"package:\" + packageName));\n        VActivityManagerService.get().sendBroadcastAsUser(intent, VUserHandle.ALL);\n    }\n\n    @Override\n    public void registerObserver(IPackageObserver observer) {\n        try {\n            mRemoteCallbackList.register(observer);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void unregisterObserver(IPackageObserver observer) {\n        try {\n            mRemoteCallbackList.unregister(observer);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public IAppRequestListener getAppRequestListener() {\n        return mAppRequestListener;\n    }\n\n    @Override\n    public void setAppRequestListener(final IAppRequestListener listener) {\n        this.mAppRequestListener = listener;\n        if (listener != null) {\n            try {\n                listener.asBinder().linkToDeath(new DeathRecipient() {\n                    @Override\n                    public void binderDied() {\n                        listener.asBinder().unlinkToDeath(this, 0);\n                        VAppManagerService.this.mAppRequestListener = null;\n                    }\n                }, 0);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    @Override\n    public void clearAppRequestListener() {\n        this.mAppRequestListener = null;\n    }\n\n    @Override\n    public InstalledAppInfo getInstalledAppInfo(String packageName, int flags) {\n        synchronized (PackageCacheManager.class) {\n            if (packageName != null) {\n                PackageSetting setting = PackageCacheManager.getSetting(packageName);\n                if (setting != null) {\n                    return setting.getAppInfo();\n                }\n            }\n            return null;\n        }\n    }\n\n    public boolean isPackageLaunched(int userId, String packageName) {\n        PackageSetting ps = PackageCacheManager.getSetting(packageName);\n        return ps != null && ps.isLaunched(userId);\n    }\n\n    public void setPackageHidden(int userId, String packageName, boolean hidden) {\n        PackageSetting ps = PackageCacheManager.getSetting(packageName);\n        if (ps != null && VUserManagerService.get().exists(userId)) {\n            ps.setHidden(userId, hidden);\n            mPersistenceLayer.save();\n        }\n    }\n\n    public int getAppId(String packageName) {\n        PackageSetting setting = PackageCacheManager.getSetting(packageName);\n        return setting != null ? setting.appId : -1;\n    }\n\n\n    void restoreFactoryState() {\n        VLog.w(TAG, \"Warning: Restore the factory state...\");\n        VEnvironment.getDalvikCacheDirectory().delete();\n        VEnvironment.getUserSystemDirectory().delete();\n        VEnvironment.getDataAppDirectory().delete();\n    }\n\n    public void savePersistenceData() {\n        mPersistenceLayer.save();\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.annotation.TargetApi;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.PermissionGroupInfo;\nimport android.content.pm.PermissionInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ResolveInfo;\nimport android.content.pm.ServiceInfo;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.RemoteException;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.fixer.ComponentFixer;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.compat.ObjectsCompat;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.IPackageInstaller;\nimport com.lody.virtual.server.IPackageManager;\nimport com.lody.virtual.server.pm.installer.VPackageInstallerService;\nimport com.lody.virtual.server.pm.parser.PackageParserEx;\nimport com.lody.virtual.server.pm.parser.VPackage;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;\n\n/**\n * @author Lody\n */\npublic class VPackageManagerService extends IPackageManager.Stub {\n\n    static final String TAG = \"PackageManager\";\n    static final Comparator<ResolveInfo> sResolvePrioritySorter = new Comparator<ResolveInfo>() {\n        public int compare(ResolveInfo r1, ResolveInfo r2) {\n            int v1 = r1.priority;\n            int v2 = r2.priority;\n            if (v1 != v2) {\n                return (v1 > v2) ? -1 : 1;\n            }\n            v1 = r1.preferredOrder;\n            v2 = r2.preferredOrder;\n            if (v1 != v2) {\n                return (v1 > v2) ? -1 : 1;\n            }\n            if (r1.isDefault != r2.isDefault) {\n                return r1.isDefault ? -1 : 1;\n            }\n            v1 = r1.match;\n            v2 = r2.match;\n            if (v1 != v2) {\n                return (v1 > v2) ? -1 : 1;\n            }\n            return 0;\n        }\n    };\n    private static final AtomicReference<VPackageManagerService> gService = new AtomicReference<>();\n    private static final Comparator<ProviderInfo> sProviderInitOrderSorter = new Comparator<ProviderInfo>() {\n        public int compare(ProviderInfo p1, ProviderInfo p2) {\n            final int v1 = p1.initOrder;\n            final int v2 = p2.initOrder;\n            return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);\n        }\n    };\n\n    private final ResolveInfo mResolveInfo;\n\n    private final ActivityIntentResolver mActivities = new ActivityIntentResolver();\n    private final ServiceIntentResolver mServices = new ServiceIntentResolver();\n    private final ActivityIntentResolver mReceivers = new ActivityIntentResolver();\n    private final ProviderIntentResolver mProviders = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? new ProviderIntentResolver() : null;\n\n    private final HashMap<ComponentName, VPackage.ProviderComponent> mProvidersByComponent = new HashMap<>();\n\n    private final HashMap<String, VPackage.PermissionComponent> mPermissions = new HashMap<>();\n    private final HashMap<String, VPackage.PermissionGroupComponent> mPermissionGroups = new HashMap<>();\n    private final HashMap<String, VPackage.ProviderComponent> mProvidersByAuthority = new HashMap<>();\n\n    private final Map<String, VPackage> mPackages = PackageCacheManager.PACKAGE_CACHE;\n\n\n    public VPackageManagerService() {\n        Intent intent = new Intent();\n        intent.setClassName(VirtualCore.get().getHostPkg(), VASettings.RESOLVER_ACTIVITY);\n        mResolveInfo = VirtualCore.get().getUnHookPackageManager().resolveActivity(intent, 0);\n    }\n\n    public static void systemReady() {\n        VPackageManagerService instance = new VPackageManagerService();\n        new VUserManagerService(VirtualCore.get().getContext(), instance, new char[0], instance.mPackages);\n        gService.set(instance);\n    }\n\n    public static VPackageManagerService get() {\n        return gService.get();\n    }\n\n\n    void analyzePackageLocked(VPackage pkg) {\n        int N = pkg.activities.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ActivityComponent a = pkg.activities.get(i);\n            if (a.info.processName == null) {\n                a.info.processName = a.info.packageName;\n            }\n            mActivities.addActivity(a, \"activity\");\n        }\n        N = pkg.services.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ServiceComponent a = pkg.services.get(i);\n            if (a.info.processName == null) {\n                a.info.processName = a.info.packageName;\n            }\n            mServices.addService(a);\n        }\n        N = pkg.receivers.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ActivityComponent a = pkg.receivers.get(i);\n            if (a.info.processName == null) {\n                a.info.processName = a.info.packageName;\n            }\n            mReceivers.addActivity(a, \"receiver\");\n        }\n\n        N = pkg.providers.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ProviderComponent p = pkg.providers.get(i);\n            if (p.info.processName == null) {\n                p.info.processName = p.info.packageName;\n            }\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n                mProviders.addProvider(p);\n            }\n            String names[] = p.info.authority.split(\";\");\n            for (String name : names) {\n                if (!mProvidersByAuthority.containsKey(name)) {\n                    mProvidersByAuthority.put(name, p);\n                }\n            }\n            mProvidersByComponent.put(p.getComponentName(), p);\n        }\n\n        N = pkg.permissions.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.PermissionComponent permission = pkg.permissions.get(i);\n            mPermissions.put(permission.className, permission);\n        }\n        N = pkg.permissionGroups.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.PermissionGroupComponent group = pkg.permissionGroups.get(i);\n            mPermissionGroups.put(group.className, group);\n        }\n    }\n\n    void deletePackageLocked(String packageName) {\n        VPackage pkg = mPackages.get(packageName);\n        if (pkg == null) {\n            return;\n        }\n        int N = pkg.activities.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ActivityComponent a = pkg.activities.get(i);\n            mActivities.removeActivity(a, \"activity\");\n        }\n        N = pkg.services.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ServiceComponent a = pkg.services.get(i);\n            mServices.removeService(a);\n        }\n        N = pkg.receivers.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ActivityComponent a = pkg.receivers.get(i);\n            mReceivers.removeActivity(a, \"receiver\");\n        }\n\n        N = pkg.providers.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.ProviderComponent p = pkg.providers.get(i);\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n                mProviders.removeProvider(p);\n            }\n            String names[] = p.info.authority.split(\";\");\n            for (String name : names) {\n                mProvidersByAuthority.remove(name);\n            }\n            mProvidersByComponent.remove(p.getComponentName());\n        }\n\n        N = pkg.permissions.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.PermissionComponent permission = pkg.permissions.get(i);\n            mPermissions.remove(permission.className);\n        }\n        N = pkg.permissionGroups.size();\n        for (int i = 0; i < N; i++) {\n            VPackage.PermissionGroupComponent group = pkg.permissionGroups.get(i);\n            mPermissionGroups.remove(group.className);\n        }\n    }\n\n    @Override\n    public List<String> getSharedLibraries(String packageName) {\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(packageName);\n            if (p != null) {\n                return p.usesLibraries;\n            }\n            return null;\n        }\n    }\n\n    @Override\n    public int checkPermission(String permName, String pkgName, int userId) {\n        if (\"android.permission.INTERACT_ACROSS_USERS\".equals(permName)\n                || \"android.permission.INTERACT_ACROSS_USERS_FULL\".equals(permName)) {\n            return PackageManager.PERMISSION_DENIED;\n        }\n        return VirtualCore.get().getPackageManager().checkPermission(permName, VirtualCore.get().getHostPkg());\n    }\n\n    @Override\n    public PackageInfo getPackageInfo(String packageName, int flags, int userId) {\n        checkUserId(userId);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(packageName);\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                return generatePackageInfo(p, ps, flags, userId);\n            }\n        }\n        return null;\n    }\n\n    private PackageInfo generatePackageInfo(VPackage p, PackageSetting ps, int flags, int userId) {\n        flags = updateFlagsNought(flags);\n        PackageInfo packageInfo = PackageParserEx.generatePackageInfo(p, flags,\n                ps.firstInstallTime, ps.lastUpdateTime, ps.readUserState(userId), userId);\n        if (packageInfo != null) {\n            return packageInfo;\n        }\n        return null;\n    }\n\n    private int updateFlagsNought(int flags) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            return flags;\n        }\n        if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE\n                | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {\n            // Caller expressed an explicit opinion about what encryption\n            // aware/unaware components they want to see, so fall through and\n            // give them what they want\n        } else {\n            // Caller expressed no opinion, so match based on user state\n            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;\n        }\n        return flags;\n    }\n\n    private void checkUserId(int userId) {\n        if (!VUserManagerService.get().exists(userId)) {\n            throw new SecurityException(\"Invalid userId \" + userId);\n        }\n    }\n\n    @Override\n    public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(component.getPackageName());\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                VPackage.ActivityComponent a = mActivities.mActivities.get(component);\n                if (a != null) {\n                    ActivityInfo activityInfo = PackageParserEx.generateActivityInfo(a, flags, ps.readUserState(userId), userId);\n                    ComponentFixer.fixComponentInfo(ps, activityInfo, userId);\n                    return activityInfo;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) {\n        synchronized (mPackages) {\n            VPackage.ActivityComponent a = mActivities.mActivities.get(component);\n            if (a == null) {\n                return false;\n            }\n            for (int i = 0; i < a.intents.size(); i++) {\n                if (a.intents.get(i).filter.match(intent.getAction(), resolvedType, intent.getScheme(), intent.getData(),\n                        intent.getCategories(), TAG) >= 0) {\n                    return true;\n                }\n            }\n            return false;\n        }\n    }\n\n    @Override\n    public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(component.getPackageName());\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                VPackage.ActivityComponent a = mReceivers.mActivities.get(component);\n                if (a != null) {\n                    ActivityInfo receiverInfo = PackageParserEx.generateActivityInfo(a, flags, ps.readUserState(userId), userId);\n                    ComponentFixer.fixComponentInfo(ps, receiverInfo, userId);\n                    return receiverInfo;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(component.getPackageName());\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                VPackage.ServiceComponent s = mServices.mServices.get(component);\n                if (s != null) {\n                    ServiceInfo serviceInfo = PackageParserEx.generateServiceInfo(s, flags, ps.readUserState(userId), userId);\n                    ComponentFixer.fixComponentInfo(ps, serviceInfo, userId);\n                    return serviceInfo;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(component.getPackageName());\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                VPackage.ProviderComponent provider = mProvidersByComponent.get(component);\n                if (provider != null) {\n                    ProviderInfo providerInfo = PackageParserEx.generateProviderInfo(provider, flags, ps.readUserState(userId), userId);\n                    ComponentFixer.fixComponentInfo(ps, providerInfo, userId);\n                    return providerInfo;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, 0);\n        return chooseBestActivity(intent, resolvedType, flags, query);\n    }\n\n    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List<ResolveInfo> query) {\n        if (query != null) {\n            final int N = query.size();\n            if (N == 1) {\n                return query.get(0);\n            } else if (N > 1) {\n                // If there is more than one activity with the same priority,\n                // then let the user decide between them.\n                ResolveInfo r0 = query.get(0);\n                ResolveInfo r1 = query.get(1);\n                // If the first activity has a higher priority, or a different\n                // default, then it is always desireable to pick it.\n                if (r0.priority != r1.priority || r0.preferredOrder != r1.preferredOrder\n                        || r0.isDefault != r1.isDefault) {\n                    return query.get(0);\n                }\n                // If we have saved a preference for a preferred activity for\n                // this Intent, use that.\n\n                ResolveInfo ri = findPreferredActivity(intent, resolvedType,\n                        flags, query, r0.priority);\n                //noinspection ConstantConditions\n                if (ri != null) {\n                    return ri;\n                }\n                return query.get(0);\n            }\n        }\n        return null;\n    }\n\n    private ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, int priority) {\n        return null;\n    }\n\n    @Override\n    public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        ComponentName comp = intent.getComponent();\n        if (comp == null) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                if (intent.getSelector() != null) {\n                    intent = intent.getSelector();\n                    comp = intent.getComponent();\n                }\n            }\n        }\n        if (comp != null) {\n            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);\n            final ActivityInfo ai = getActivityInfo(comp, flags, userId);\n            if (ai != null) {\n                final ResolveInfo ri = new ResolveInfo();\n                ri.activityInfo = ai;\n                list.add(ri);\n            }\n            return list;\n        }\n\n        // reader\n        synchronized (mPackages) {\n            final String pkgName = intent.getPackage();\n            if (pkgName == null) {\n                return mActivities.queryIntent(intent, resolvedType, flags, userId);\n            }\n            final VPackage pkg = mPackages.get(pkgName);\n            if (pkg != null) {\n                return mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities, userId);\n            }\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        ComponentName comp = intent.getComponent();\n        if (comp == null) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                if (intent.getSelector() != null) {\n                    intent = intent.getSelector();\n                    comp = intent.getComponent();\n                }\n            }\n        }\n        if (comp != null) {\n            List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);\n            ActivityInfo ai = getReceiverInfo(comp, flags, userId);\n            if (ai != null) {\n                ResolveInfo ri = new ResolveInfo();\n                ri.activityInfo = ai;\n                list.add(ri);\n            }\n            return list;\n        }\n\n        // reader\n        synchronized (mPackages) {\n            String pkgName = intent.getPackage();\n            if (pkgName == null) {\n                return mReceivers.queryIntent(intent, resolvedType, flags, userId);\n            }\n            final VPackage pkg = mPackages.get(pkgName);\n            if (pkg != null) {\n                return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers, userId);\n            }\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);\n        if (query != null) {\n            if (query.size() >= 1) {\n                // If there is more than one service with the same priority,\n                // just arbitrarily pick the first one.\n                return query.get(0);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        ComponentName comp = intent.getComponent();\n        if (comp == null) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                if (intent.getSelector() != null) {\n                    intent = intent.getSelector();\n                    comp = intent.getComponent();\n                }\n            }\n        }\n        if (comp != null) {\n            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);\n            final ServiceInfo si = getServiceInfo(comp, flags, userId);\n            if (si != null) {\n                final ResolveInfo ri = new ResolveInfo();\n                ri.serviceInfo = si;\n                list.add(ri);\n            }\n            return list;\n        }\n\n        // reader\n        synchronized (mPackages) {\n            String pkgName = intent.getPackage();\n            if (pkgName == null) {\n                return mServices.queryIntent(intent, resolvedType, flags, userId);\n            }\n            final VPackage pkg = mPackages.get(pkgName);\n            if (pkg != null) {\n                return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services, userId);\n            }\n            return Collections.emptyList();\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    @Override\n    public List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        ComponentName comp = intent.getComponent();\n        if (comp == null) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                if (intent.getSelector() != null) {\n                    intent = intent.getSelector();\n                    comp = intent.getComponent();\n                }\n            }\n        }\n        if (comp != null) {\n            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);\n            final ProviderInfo pi = getProviderInfo(comp, flags, userId);\n            if (pi != null) {\n                final ResolveInfo ri = new ResolveInfo();\n                ri.providerInfo = pi;\n                list.add(ri);\n            }\n            return list;\n        }\n        // reader\n        synchronized (mPackages) {\n            String pkgName = intent.getPackage();\n            if (pkgName == null) {\n                return mProviders.queryIntent(intent, resolvedType, flags, userId);\n            }\n            final VPackage pkg = mPackages.get(pkgName);\n            if (pkg != null) {\n                return mProviders.queryIntentForPackage(intent, resolvedType, flags, pkg.providers, userId);\n            }\n            return Collections.emptyList();\n        }\n    }\n\n    @Override\n    public VParceledListSlice<ProviderInfo> queryContentProviders(String processName, int vuid, int flags) {\n        int userId = VUserHandle.getUserId(vuid);\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        ArrayList<ProviderInfo> finalList = new ArrayList<>(3);\n        // reader\n        synchronized (mPackages) {\n            for (VPackage.ProviderComponent p : mProvidersByComponent.values()) {\n                PackageSetting ps = (PackageSetting) p.owner.mExtras;\n                if (processName == null || ps.appId == VUserHandle.getAppId(vuid) && p.info.processName.equals(processName)) {\n                    ProviderInfo providerInfo = PackageParserEx.generateProviderInfo(p, flags, ps.readUserState(userId), userId);\n                    finalList.add(providerInfo);\n                }\n            }\n        }\n        if (!finalList.isEmpty()) {\n            Collections.sort(finalList, sProviderInitOrderSorter);\n        }\n        return new VParceledListSlice<>(finalList);\n    }\n\n    @Override\n    public VParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {\n        checkUserId(userId);\n        ArrayList<PackageInfo> pkgList = new ArrayList<>(mPackages.size());\n        synchronized (mPackages) {\n            for (VPackage p : mPackages.values()) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                PackageInfo info = generatePackageInfo(p, ps, flags, userId);\n                if (info != null) {\n                    pkgList.add(info);\n                }\n            }\n        }\n        return new VParceledListSlice<>(pkgList);\n    }\n\n    @Override\n    public VParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        ArrayList<ApplicationInfo> list = new ArrayList<>(mPackages.size());\n        synchronized (mPackages) {\n            for (VPackage p : mPackages.values()) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                ApplicationInfo info = PackageParserEx.generateApplicationInfo(p, flags, ps.readUserState(userId), userId);\n                list.add(info);\n            }\n        }\n        return new VParceledListSlice<>(list);\n    }\n\n    @Override\n    public PermissionInfo getPermissionInfo(String name, int flags) {\n        synchronized (mPackages) {\n            VPackage.PermissionComponent p = mPermissions.get(name);\n            if (p != null) {\n                return new PermissionInfo(p.info);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {\n        synchronized (mPackages) {\n            return null;\n        }\n    }\n\n    @Override\n    public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {\n        synchronized (mPackages) {\n            VPackage.PermissionGroupComponent p = mPermissionGroups.get(name);\n            if (p != null) {\n                return new PermissionGroupInfo(p.info);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {\n        synchronized (mPackages) {\n            final int N = mPermissionGroups.size();\n            ArrayList<PermissionGroupInfo> out = new ArrayList<>(N);\n            for (VPackage.PermissionGroupComponent pg : mPermissionGroups.values()) {\n                out.add(new PermissionGroupInfo(pg.info));\n            }\n            return out;\n        }\n    }\n\n    @Override\n    public ProviderInfo resolveContentProvider(String name, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        synchronized (mPackages) {\n            final VPackage.ProviderComponent provider = mProvidersByAuthority.get(name);\n            if (provider != null) {\n                PackageSetting ps = (PackageSetting) provider.owner.mExtras;\n                ProviderInfo providerInfo = PackageParserEx.generateProviderInfo(provider, flags, ps.readUserState(userId), userId);\n                if (providerInfo != null) {\n                    VPackage p = mPackages.get(providerInfo.packageName);\n                    PackageSetting settings = (PackageSetting) p.mExtras;\n                    ComponentFixer.fixComponentInfo(settings, providerInfo, userId);\n                    return providerInfo;\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {\n        checkUserId(userId);\n        flags = updateFlagsNought(flags);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(packageName);\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                return PackageParserEx.generateApplicationInfo(p, flags, ps.readUserState(userId), userId);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public String[] getPackagesForUid(int uid) {\n        int userId = VUserHandle.getUserId(uid);\n        checkUserId(userId);\n        synchronized (this) {\n            List<String> pkgList = new ArrayList<>(2);\n            for (VPackage p : mPackages.values()) {\n                PackageSetting settings = (PackageSetting) p.mExtras;\n                if (VUserHandle.getUid(userId, settings.appId) == uid) {\n                    pkgList.add(p.packageName);\n                }\n            }\n            return pkgList.toArray(new String[pkgList.size()]);\n        }\n    }\n\n    @Override\n    public int getPackageUid(String packageName, int userId) {\n        checkUserId(userId);\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(packageName);\n            if (p != null) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                return VUserHandle.getUid(userId, ps.appId);\n            }\n            return -1;\n        }\n    }\n\n    @Override\n    public String getNameForUid(int uid) {\n        int appId = VUserHandle.getAppId(uid);\n        synchronized (mPackages) {\n            for (VPackage p : mPackages.values()) {\n                PackageSetting ps = (PackageSetting) p.mExtras;\n                if (ps.appId == appId) {\n                    return ps.packageName;\n                }\n            }\n            return null;\n        }\n    }\n\n\n    @Override\n    public List<String> querySharedPackages(String packageName) {\n        synchronized (mPackages) {\n            VPackage p = mPackages.get(packageName);\n            if (p == null || p.mSharedUserId == null) {\n                // noinspection unchecked\n                return Collections.EMPTY_LIST;\n            }\n            ArrayList<String> list = new ArrayList<>();\n            for (VPackage one : mPackages.values()) {\n                if (TextUtils.equals(one.mSharedUserId, p.mSharedUserId)) {\n                    list.add(one.packageName);\n                }\n            }\n            return list;\n        }\n    }\n\n    @Override\n    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        try {\n            return super.onTransact(code, data, reply, flags);\n        } catch (Throwable e) {\n            e.printStackTrace();\n            throw e;\n        }\n    }\n\n    @Override\n    public IPackageInstaller getPackageInstaller() {\n        return VPackageInstallerService.get();\n    }\n\n    void createNewUser(int userId, File userPath) {\n        for (VPackage p : mPackages.values()) {\n            PackageSetting setting = (PackageSetting) p.mExtras;\n            setting.modifyUserState(userId);\n        }\n    }\n\n    void cleanUpUser(int userId) {\n        for (VPackage p : mPackages.values()) {\n            PackageSetting ps = (PackageSetting) p.mExtras;\n            ps.removeUser(userId);\n        }\n    }\n\n    private final class ActivityIntentResolver extends IntentResolver<VPackage.ActivityIntentInfo, ResolveInfo> {\n        // Keys are String (activity class name), values are Activity.\n        private final HashMap<ComponentName, VPackage.ActivityComponent> mActivities = new HashMap<>();\n        private int mFlags;\n\n        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) {\n            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;\n            return super.queryIntent(intent, resolvedType, defaultOnly, userId);\n        }\n\n        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) {\n            mFlags = flags;\n            return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);\n        }\n\n        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags,\n                                                ArrayList<VPackage.ActivityComponent> packageActivities, int userId) {\n            if (packageActivities == null) {\n                return null;\n            }\n            mFlags = flags;\n            final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;\n            final int N = packageActivities.size();\n            ArrayList<VPackage.ActivityIntentInfo[]> listCut = new ArrayList<VPackage.ActivityIntentInfo[]>(\n                    N);\n\n            ArrayList<VPackage.ActivityIntentInfo> intentFilters;\n            for (int i = 0; i < N; ++i) {\n                intentFilters = packageActivities.get(i).intents;\n                if (intentFilters != null && intentFilters.size() > 0) {\n                    VPackage.ActivityIntentInfo[] array = new VPackage.ActivityIntentInfo[intentFilters\n                            .size()];\n                    intentFilters.toArray(array);\n                    listCut.add(array);\n                }\n            }\n            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);\n        }\n\n        public final void addActivity(VPackage.ActivityComponent a, String type) {\n            mActivities.put(a.getComponentName(), a);\n            final int NI = a.intents.size();\n            for (int j = 0; j < NI; j++) {\n                VPackage.ActivityIntentInfo intent = a.intents.get(j);\n                if (intent.filter.getPriority() > 0 && \"activity\".equals(type)) {\n                    intent.filter.setPriority(0);\n                    Log.w(TAG, \"Package \" + a.info.applicationInfo.packageName + \" has activity \" + a.className\n                            + \" with priority > 0, forcing to 0\");\n                }\n                addFilter(intent);\n            }\n        }\n\n        public final void removeActivity(VPackage.ActivityComponent a, String type) {\n            mActivities.remove(a.getComponentName());\n            final int NI = a.intents.size();\n            for (int j = 0; j < NI; j++) {\n                VPackage.ActivityIntentInfo intent = a.intents.get(j);\n                removeFilter(intent);\n            }\n        }\n\n        @Override\n        protected boolean allowFilterResult(VPackage.ActivityIntentInfo filter, List<ResolveInfo> dest) {\n            ActivityInfo filterAi = filter.activity.info;\n            for (int i = dest.size() - 1; i >= 0; i--) {\n                ActivityInfo destAi = dest.get(i).activityInfo;\n                if (ObjectsCompat.equals(destAi.name, filterAi.name) && ObjectsCompat.equals(destAi.packageName, filterAi.packageName)) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        @Override\n        protected VPackage.ActivityIntentInfo[] newArray(int size) {\n            return new VPackage.ActivityIntentInfo[size];\n        }\n\n        @Override\n        protected boolean isFilterStopped(VPackage.ActivityIntentInfo filter) {\n            return false;\n        }\n\n        @Override\n        protected boolean isPackageForFilter(String packageName, VPackage.ActivityIntentInfo info) {\n            return packageName.equals(info.activity.owner.packageName);\n        }\n\n        @Override\n        protected ResolveInfo newResult(VPackage.ActivityIntentInfo info, int match, int userId) {\n            final VPackage.ActivityComponent activity = info.activity;\n            PackageSetting ps = (PackageSetting) activity.owner.mExtras;\n            ActivityInfo ai = PackageParserEx.generateActivityInfo(activity, mFlags, ps.readUserState(userId), userId);\n            if (ai == null) {\n                return null;\n            }\n            final ResolveInfo res = new ResolveInfo();\n            res.activityInfo = ai;\n            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {\n                res.filter = info.filter;\n            }\n            res.priority = info.filter.getPriority();\n            res.preferredOrder = activity.owner.mPreferredOrder;\n            res.match = match;\n            res.isDefault = info.hasDefault;\n            res.labelRes = info.labelRes;\n            res.nonLocalizedLabel = info.nonLocalizedLabel;\n            res.icon = info.icon;\n            return res;\n        }\n\n        @Override\n        protected void sortResults(List<ResolveInfo> results) {\n            Collections.sort(results, sResolvePrioritySorter);\n        }\n\n        @Override\n        protected void dumpFilter(PrintWriter out, String prefix, VPackage.ActivityIntentInfo filter) {\n\n        }\n\n        @Override\n        protected Object filterToLabel(VPackage.ActivityIntentInfo filter) {\n            return filter.activity;\n        }\n\n        protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {\n\n        }\n    }\n\n    private final class ServiceIntentResolver extends IntentResolver<VPackage.ServiceIntentInfo, ResolveInfo> {\n        // Keys are String (activity class name), values are Activity.\n        private final HashMap<ComponentName, VPackage.ServiceComponent> mServices = new HashMap<>();\n        private int mFlags;\n\n        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) {\n            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;\n            return super.queryIntent(intent, resolvedType, defaultOnly, userId);\n        }\n\n        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) {\n            mFlags = flags;\n            return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);\n        }\n\n        public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags,\n                                                       ArrayList<VPackage.ServiceComponent> packageServices, int userId) {\n            if (packageServices == null) {\n                return null;\n            }\n            mFlags = flags;\n            final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;\n            final int N = packageServices.size();\n            ArrayList<VPackage.ServiceIntentInfo[]> listCut = new ArrayList<VPackage.ServiceIntentInfo[]>(N);\n\n            ArrayList<VPackage.ServiceIntentInfo> intentFilters;\n            for (int i = 0; i < N; ++i) {\n                intentFilters = packageServices.get(i).intents;\n                if (intentFilters != null && intentFilters.size() > 0) {\n                    VPackage.ServiceIntentInfo[] array = new VPackage.ServiceIntentInfo[intentFilters.size()];\n                    intentFilters.toArray(array);\n                    listCut.add(array);\n                }\n            }\n            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);\n        }\n\n        public final void addService(VPackage.ServiceComponent s) {\n            mServices.put(s.getComponentName(), s);\n            final int NI = s.intents.size();\n            int j;\n            for (j = 0; j < NI; j++) {\n                VPackage.ServiceIntentInfo intent = s.intents.get(j);\n                addFilter(intent);\n            }\n        }\n\n        public final void removeService(VPackage.ServiceComponent s) {\n            mServices.remove(s.getComponentName());\n            final int NI = s.intents.size();\n            int j;\n            for (j = 0; j < NI; j++) {\n                VPackage.ServiceIntentInfo intent = s.intents.get(j);\n                removeFilter(intent);\n            }\n        }\n\n        @Override\n        protected boolean allowFilterResult(VPackage.ServiceIntentInfo filter, List<ResolveInfo> dest) {\n            ServiceInfo filterSi = filter.service.info;\n            for (int i = dest.size() - 1; i >= 0; i--) {\n                ServiceInfo destAi = dest.get(i).serviceInfo;\n                if (ObjectsCompat.equals(destAi.name, filterSi.name)\n                        && ObjectsCompat.equals(destAi.packageName, filterSi.packageName)) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        @Override\n        protected VPackage.ServiceIntentInfo[] newArray(int size) {\n            return new VPackage.ServiceIntentInfo[size];\n        }\n\n        @Override\n        protected boolean isFilterStopped(VPackage.ServiceIntentInfo filter) {\n            return false;\n        }\n\n        @Override\n        protected boolean isPackageForFilter(String packageName, VPackage.ServiceIntentInfo info) {\n            return packageName.equals(info.service.owner.packageName);\n        }\n\n        @Override\n        protected ResolveInfo newResult(VPackage.ServiceIntentInfo filter, int match, int userId) {\n            final VPackage.ServiceComponent service = filter.service;\n            PackageSetting ps = (PackageSetting) service.owner.mExtras;\n            ServiceInfo si = PackageParserEx.generateServiceInfo(service, mFlags, ps.readUserState(userId), userId);\n            if (si == null) {\n                return null;\n            }\n            final ResolveInfo res = new ResolveInfo();\n            res.serviceInfo = si;\n            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {\n                res.filter = filter.filter;\n            }\n            res.priority = filter.filter.getPriority();\n            res.preferredOrder = service.owner.mPreferredOrder;\n            res.match = match;\n            res.isDefault = filter.hasDefault;\n            res.labelRes = filter.labelRes;\n            res.nonLocalizedLabel = filter.nonLocalizedLabel;\n            res.icon = filter.icon;\n            return res;\n        }\n\n        @Override\n        protected void sortResults(List<ResolveInfo> results) {\n            Collections.sort(results, sResolvePrioritySorter);\n        }\n\n        @Override\n        protected void dumpFilter(PrintWriter out, String prefix, VPackage.ServiceIntentInfo filter) {\n\n        }\n\n        @Override\n        protected Object filterToLabel(VPackage.ServiceIntentInfo filter) {\n            return filter.service;\n        }\n\n        protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/VUserManagerService.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.app.Activity;\nimport android.app.IStopUserCallback;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.os.Binder;\nimport android.util.SparseArray;\nimport android.util.Xml;\n\nimport com.lody.virtual.R;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.env.Constants;\nimport com.lody.virtual.helper.compat.ActivityManagerCompat;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.AtomicFile;\nimport com.lody.virtual.helper.utils.FastXmlSerializer;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VBinder;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.os.VUserInfo;\nimport com.lody.virtual.os.VUserManager;\nimport com.lody.virtual.server.am.VActivityManagerService;\nimport com.lody.virtual.server.IUserManager;\n\nimport org.xmlpull.v1.XmlPullParser;\nimport org.xmlpull.v1.XmlPullParserException;\nimport org.xmlpull.v1.XmlSerializer;\n\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileDescriptor;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\n\n\n/**\n * @author Lody\n */\npublic class VUserManagerService extends IUserManager.Stub {\n\n    private static final String LOG_TAG = \"VUserManagerService\";\n\n    private static final boolean DBG = false;\n\n    private static final String TAG_NAME = \"name\";\n    private static final String ATTR_FLAGS = \"flags\";\n    private static final String ATTR_ICON_PATH = \"icon\";\n    private static final String ATTR_ID = \"id\";\n    private static final String ATTR_CREATION_TIME = \"created\";\n    private static final String ATTR_LAST_LOGGED_IN_TIME = \"lastLoggedIn\";\n    private static final String ATTR_SERIAL_NO = \"serialNumber\";\n    private static final String ATTR_NEXT_SERIAL_NO = \"nextSerialNumber\";\n    private static final String ATTR_PARTIAL = \"partial\";\n    private static final String ATTR_USER_VERSION = \"version\";\n    private static final String TAG_USERS = \"users\";\n    private static final String TAG_USER = \"user\";\n\n    private static final String USER_INFO_DIR = \"system\" + File.separator + \"users\";\n    private static final String USER_LIST_FILENAME = \"userlist.xml\";\n    private static final String USER_PHOTO_FILENAME = \"photo.png\";\n\n    private static final int MIN_USER_ID = 1;\n\n    private static final int USER_VERSION = 1;\n\n    private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms\n    private static VUserManagerService sInstance;\n    private final Context mContext;\n    private final VPackageManagerService mPm;\n    private final Object mInstallLock;\n    private final Object mPackagesLock;\n    private final File mUsersDir;\n    private final File mUserListFile;\n    private final File mBaseUserPath;\n    private SparseArray<VUserInfo> mUsers = new SparseArray<VUserInfo>();\n    private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>();\n    private int[] mUserIds;\n    private boolean mGuestEnabled;\n    private int mNextSerialNumber;\n    // This resets on a reboot. Otherwise it keeps incrementing so that user ids are\n    // not reused in quick succession\n    private int mNextUserId = MIN_USER_ID;\n    private int mUserVersion = 0;\n\n    /**\n     * Called by package manager to create the service.  This is closely\n     * associated with the package manager, and the given lock is the\n     * package manager's own lock.\n     */\n    VUserManagerService(Context context, VPackageManagerService pm,\n                        Object installLock, Object packagesLock) {\n        this(context, pm, installLock, packagesLock,\n                VEnvironment.getDataDirectory(),\n                new File(VEnvironment.getDataDirectory(), \"user\"));\n    }\n\n    /**\n     * Available for testing purposes.\n     */\n    private VUserManagerService(Context context, VPackageManagerService pm,\n                                Object installLock, Object packagesLock,\n                                File dataDir, File baseUserPath) {\n        mContext = context;\n        mPm = pm;\n        mInstallLock = installLock;\n        mPackagesLock = packagesLock;\n        synchronized (mInstallLock) {\n            synchronized (mPackagesLock) {\n                mUsersDir = new File(dataDir, USER_INFO_DIR);\n                mUsersDir.mkdirs();\n                // Make zeroth user directory, for services to migrate their files to that location\n                File userZeroDir = new File(mUsersDir, \"0\");\n                userZeroDir.mkdirs();\n                mBaseUserPath = baseUserPath;\n//                FileUtils.setPermissions(mUsersDir.toString(),\n//                        FileUtils.S_IRWXU|FileUtils.S_IRWXG\n//                        |FileUtils.S_IROTH|FileUtils.S_IXOTH,\n//                        -1, -1);\n                mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);\n                readUserListLocked();\n                // Prune out any partially created/partially removed users.\n                ArrayList<VUserInfo> partials = new ArrayList<VUserInfo>();\n                for (int i = 0; i < mUsers.size(); i++) {\n                    VUserInfo ui = mUsers.valueAt(i);\n                    if (ui.partial && i != 0) {\n                        partials.add(ui);\n                    }\n                }\n                for (int i = 0; i < partials.size(); i++) {\n                    VUserInfo ui = partials.get(i);\n                    VLog.w(LOG_TAG, \"Removing partially created user #\" + i\n                            + \" (name=\" + ui.name + \")\");\n                    removeUserStateLocked(ui.id);\n                }\n                sInstance = this;\n            }\n        }\n    }\n\n    public static VUserManagerService get() {\n        synchronized (VUserManagerService.class) {\n            return sInstance;\n        }\n    }\n\n    /**\n     * Enforces that only the system UID or root's UID or apps that have the\n     * {android.Manifest.permission.MANAGE_USERS MANAGE_USERS}\n     * permission can make certain calls to the VUserManager.\n     *\n     * @param message used as message if SecurityException is thrown\n     * @throws SecurityException if the caller is not system or root\n     */\n    private static void checkManageUsersPermission(String message) {\n        final int uid = VBinder.getCallingUid();\n        if (uid != VirtualCore.get().myUid()) {\n            throw new SecurityException(\"You need MANAGE_USERS permission to: \" + message);\n        }\n    }\n\n    @Override\n    public List<VUserInfo> getUsers(boolean excludeDying) {\n        //checkManageUsersPermission(\"query users\");\n        synchronized (mPackagesLock) {\n            ArrayList<VUserInfo> users = new ArrayList<VUserInfo>(mUsers.size());\n            for (int i = 0; i < mUsers.size(); i++) {\n                VUserInfo ui = mUsers.valueAt(i);\n                if (ui.partial) {\n                    continue;\n                }\n                if (!excludeDying || !mRemovingUserIds.contains(ui.id)) {\n                    users.add(ui);\n                }\n            }\n            return users;\n        }\n    }\n\n    @Override\n    public VUserInfo getUserInfo(int userId) {\n        //checkManageUsersPermission(\"query user\");\n        synchronized (mPackagesLock) {\n            return getUserInfoLocked(userId);\n        }\n    }\n\n    /*\n     * Should be locked on mUsers before calling this.\n     */\n    private VUserInfo getUserInfoLocked(int userId) {\n        VUserInfo ui = mUsers.get(userId);\n        // If it is partial and not in the process of being removed, return as unknown user.\n        if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) {\n            VLog.w(LOG_TAG, \"getUserInfo: unknown user #\" + userId);\n            return null;\n        }\n        return ui;\n    }\n\n    public boolean exists(int userId) {\n        synchronized (mPackagesLock) {\n            return ArrayUtils.contains(mUserIds, userId);\n        }\n    }\n\n    @Override\n    public void setUserName(int userId, String name) {\n        checkManageUsersPermission(\"rename users\");\n        boolean changed = false;\n        synchronized (mPackagesLock) {\n            VUserInfo info = mUsers.get(userId);\n            if (info == null || info.partial) {\n                VLog.w(LOG_TAG, \"setUserName: unknown user #\" + userId);\n                return;\n            }\n            if (name != null && !name.equals(info.name)) {\n                info.name = name;\n                writeUserLocked(info);\n                changed = true;\n            }\n        }\n        if (changed) {\n            sendUserInfoChangedBroadcast(userId);\n        }\n    }\n\n    @Override\n    public void setUserIcon(int userId, Bitmap bitmap) {\n        checkManageUsersPermission(\"update users\");\n        synchronized (mPackagesLock) {\n            VUserInfo info = mUsers.get(userId);\n            if (info == null || info.partial) {\n                VLog.w(LOG_TAG, \"setUserIcon: unknown user #\" + userId);\n                return;\n            }\n            writeBitmapLocked(info, bitmap);\n            writeUserLocked(info);\n        }\n        sendUserInfoChangedBroadcast(userId);\n    }\n\n    private void sendUserInfoChangedBroadcast(int userId) {\n        Intent changedIntent = new Intent(Constants.ACTION_USER_INFO_CHANGED);\n        changedIntent.putExtra(Constants.EXTRA_USER_HANDLE, userId);\n        changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);\n        VActivityManagerService.get().sendBroadcastAsUser(changedIntent, new VUserHandle(userId));\n    }\n\n    @Override\n    public Bitmap getUserIcon(int userId) {\n        //checkManageUsersPermission(\"read users\");\n        synchronized (mPackagesLock) {\n            VUserInfo info = mUsers.get(userId);\n            if (info == null || info.partial) {\n                VLog.w(LOG_TAG, \"getUserIcon: unknown user #\" + userId);\n                return null;\n            }\n            if (info.iconPath == null) {\n                return null;\n            }\n            return BitmapFactory.decodeFile(info.iconPath);\n        }\n    }\n\n    @Override\n    public boolean isGuestEnabled() {\n        synchronized (mPackagesLock) {\n            return mGuestEnabled;\n        }\n    }\n\n    @Override\n    public void setGuestEnabled(boolean enable) {\n        checkManageUsersPermission(\"enable guest users\");\n        synchronized (mPackagesLock) {\n            if (mGuestEnabled != enable) {\n                mGuestEnabled = enable;\n                // Erase any guest user that currently exists\n                for (int i = 0; i < mUsers.size(); i++) {\n                    VUserInfo user = mUsers.valueAt(i);\n                    if (!user.partial && user.isGuest()) {\n                        if (!enable) {\n                            removeUser(user.id);\n                        }\n                        return;\n                    }\n                }\n                // No guest was found\n                if (enable) {\n                    createUser(\"Guest\", VUserInfo.FLAG_GUEST);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void wipeUser(int userHandle) {\n        checkManageUsersPermission(\"wipe user\");\n        // TODO:\n    }\n\n    public void makeInitialized(int userId) {\n        checkManageUsersPermission(\"makeInitialized\");\n        synchronized (mPackagesLock) {\n            VUserInfo info = mUsers.get(userId);\n            if (info == null || info.partial) {\n                VLog.w(LOG_TAG, \"makeInitialized: unknown user #\" + userId);\n            }\n            if ((info.flags& VUserInfo.FLAG_INITIALIZED) == 0) {\n                info.flags |= VUserInfo.FLAG_INITIALIZED;\n                writeUserLocked(info);\n            }\n        }\n    }\n\n    /**\n     * Check if we've hit the limit of how many users can be created.\n     */\n    private boolean isUserLimitReachedLocked() {\n        int nUsers = mUsers.size();\n        return nUsers >= VUserManager.getMaxSupportedUsers();\n    }\n\n    private void writeBitmapLocked(VUserInfo info, Bitmap bitmap) {\n        try {\n            File dir = new File(mUsersDir, Integer.toString(info.id));\n            File file = new File(dir, USER_PHOTO_FILENAME);\n            if (!dir.exists()) {\n                dir.mkdir();\n//                FileUtils.setPermissions(\n//                        dir.getPath(),\n//                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,\n//                        -1, -1);\n            }\n            FileOutputStream os;\n            if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {\n                info.iconPath = file.getAbsolutePath();\n            }\n            try {\n                os.close();\n            } catch (IOException ioe) {\n                // What the ... !\n            }\n        } catch (FileNotFoundException e) {\n            VLog.w(LOG_TAG, \"Error setting photo for user \", e);\n        }\n    }\n\n    /**\n     * Returns an array of user ids. This array is cached here for quick access, so do not modify or\n     * cache it elsewhere.\n     * @return the array of user ids.\n     */\n    public int[] getUserIds() {\n        synchronized (mPackagesLock) {\n            return mUserIds;\n        }\n    }\n\n    int[] getUserIdsLPr() {\n        return mUserIds;\n    }\n\n    private void readUserList() {\n        synchronized (mPackagesLock) {\n            readUserListLocked();\n        }\n    }\n\n    private void readUserListLocked() {\n        mGuestEnabled = false;\n        if (!mUserListFile.exists()) {\n            fallbackToSingleUserLocked();\n            return;\n        }\n        FileInputStream fis = null;\n        AtomicFile userListFile = new AtomicFile(mUserListFile);\n        try {\n            fis = userListFile.openRead();\n            XmlPullParser parser = Xml.newPullParser();\n            parser.setInput(fis, null);\n            int type;\n            while ((type = parser.next()) != XmlPullParser.START_TAG\n                    && type != XmlPullParser.END_DOCUMENT) {\n                ;\n            }\n\n            if (type != XmlPullParser.START_TAG) {\n                VLog.e(LOG_TAG, \"Unable to read user list\");\n                fallbackToSingleUserLocked();\n                return;\n            }\n\n            mNextSerialNumber = -1;\n            if (parser.getName().equals(TAG_USERS)) {\n                String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);\n                if (lastSerialNumber != null) {\n                    mNextSerialNumber = Integer.parseInt(lastSerialNumber);\n                }\n                String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);\n                if (versionNumber != null) {\n                    mUserVersion = Integer.parseInt(versionNumber);\n                }\n            }\n\n            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {\n                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {\n                    String id = parser.getAttributeValue(null, ATTR_ID);\n                    VUserInfo user = readUser(Integer.parseInt(id));\n\n                    if (user != null) {\n                        mUsers.put(user.id, user);\n                        if (user.isGuest()) {\n                            mGuestEnabled = true;\n                        }\n                        if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {\n                            mNextSerialNumber = user.id + 1;\n                        }\n                    }\n                }\n            }\n            updateUserIdsLocked();\n            upgradeIfNecessary();\n        } catch (IOException ioe) {\n            fallbackToSingleUserLocked();\n        } catch (XmlPullParserException pe) {\n            fallbackToSingleUserLocked();\n        } finally {\n            if (fis != null) {\n                try {\n                    fis.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    /**\n     * This fixes an incorrect initialization of user name for the owner.\n     * TODO: Remove in the next release.\n     */\n    private void upgradeIfNecessary() {\n        int userVersion = mUserVersion;\n        if (userVersion < 1) {\n            // Assign a proper name for the owner, if not initialized correctly before\n            VUserInfo user = mUsers.get(VUserHandle.USER_OWNER);\n            if (\"Primary\".equals(user.name)) {\n                user.name = \"Admin\";\n                writeUserLocked(user);\n            }\n            userVersion = 1;\n        }\n\n        if (userVersion < USER_VERSION) {\n            VLog.w(LOG_TAG, \"User version \" + mUserVersion + \" didn't upgrade as expected to \"\n                    + USER_VERSION);\n        } else {\n            mUserVersion = userVersion;\n            writeUserListLocked();\n        }\n    }\n\n    private void fallbackToSingleUserLocked() {\n        // Create the primary user\n        VUserInfo primary = new VUserInfo(0,\n                mContext.getResources().getString(R.string.owner_name), null,\n                VUserInfo.FLAG_ADMIN | VUserInfo.FLAG_PRIMARY | VUserInfo.FLAG_INITIALIZED);\n        mUsers.put(0, primary);\n        mNextSerialNumber = MIN_USER_ID;\n        updateUserIdsLocked();\n\n        writeUserListLocked();\n        writeUserLocked(primary);\n    }\n\n    /*\n     * Writes the user file in this format:\n     *\n     * <user flags=\"20039023\" id=\"0\">\n     *   <name>Primary</name>\n     * </user>\n     */\n    private void writeUserLocked(VUserInfo userInfo) {\n        FileOutputStream fos = null;\n        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + \".xml\"));\n        try {\n            fos = userFile.startWrite();\n            final BufferedOutputStream bos = new BufferedOutputStream(fos);\n\n            // XmlSerializer serializer = XmlUtils.serializerInstance();\n            final XmlSerializer serializer = new FastXmlSerializer();\n            serializer.setOutput(bos, \"utf-8\");\n            serializer.startDocument(null, true);\n            serializer.setFeature(\"http://xmlpull.org/v1/doc/features.html#indent-output\", true);\n\n            serializer.startTag(null, TAG_USER);\n            serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));\n            serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));\n            serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));\n            serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));\n            serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,\n                    Long.toString(userInfo.lastLoggedInTime));\n            if (userInfo.iconPath != null) {\n                serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);\n            }\n            if (userInfo.partial) {\n                serializer.attribute(null, ATTR_PARTIAL, \"true\");\n            }\n\n            serializer.startTag(null, TAG_NAME);\n            serializer.text(userInfo.name);\n            serializer.endTag(null, TAG_NAME);\n\n            serializer.endTag(null, TAG_USER);\n\n            serializer.endDocument();\n            userFile.finishWrite(fos);\n        } catch (Exception ioe) {\n            VLog.e(LOG_TAG, \"Error writing user info \" + userInfo.id + \"\\n\" + ioe);\n            userFile.failWrite(fos);\n        }\n    }\n\n    /*\n     * Writes the user list file in this format:\n     *\n     * <users nextSerialNumber=\"3\">\n     *   <user id=\"0\"></user>\n     *   <user id=\"2\"></user>\n     * </users>\n     */\n    private void writeUserListLocked() {\n        FileOutputStream fos = null;\n        AtomicFile userListFile = new AtomicFile(mUserListFile);\n        try {\n            fos = userListFile.startWrite();\n            final BufferedOutputStream bos = new BufferedOutputStream(fos);\n\n            // XmlSerializer serializer = XmlUtils.serializerInstance();\n            final XmlSerializer serializer = new FastXmlSerializer();\n            serializer.setOutput(bos, \"utf-8\");\n            serializer.startDocument(null, true);\n            serializer.setFeature(\"http://xmlpull.org/v1/doc/features.html#indent-output\", true);\n\n            serializer.startTag(null, TAG_USERS);\n            serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));\n            serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));\n\n            for (int i = 0; i < mUsers.size(); i++) {\n                VUserInfo user = mUsers.valueAt(i);\n                serializer.startTag(null, TAG_USER);\n                serializer.attribute(null, ATTR_ID, Integer.toString(user.id));\n                serializer.endTag(null, TAG_USER);\n            }\n\n            serializer.endTag(null, TAG_USERS);\n\n            serializer.endDocument();\n            userListFile.finishWrite(fos);\n        } catch (Exception e) {\n            userListFile.failWrite(fos);\n            VLog.e(LOG_TAG, \"Error writing user list\");\n        }\n    }\n\n    private VUserInfo readUser(int id) {\n        int flags = 0;\n        int serialNumber = id;\n        String name = null;\n        String iconPath = null;\n        long creationTime = 0L;\n        long lastLoggedInTime = 0L;\n        boolean partial = false;\n\n        FileInputStream fis = null;\n        try {\n            AtomicFile userFile =\n                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + \".xml\"));\n            fis = userFile.openRead();\n            XmlPullParser parser = Xml.newPullParser();\n            parser.setInput(fis, null);\n            int type;\n            while ((type = parser.next()) != XmlPullParser.START_TAG\n                    && type != XmlPullParser.END_DOCUMENT) {\n                ;\n            }\n\n            if (type != XmlPullParser.START_TAG) {\n                VLog.e(LOG_TAG, \"Unable to read user \" + id);\n                return null;\n            }\n\n            if (parser.getName().equals(TAG_USER)) {\n                int storedId = readIntAttribute(parser, ATTR_ID, -1);\n                if (storedId != id) {\n                    VLog.e(LOG_TAG, \"User id does not match the file name\");\n                    return null;\n                }\n                serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);\n                flags = readIntAttribute(parser, ATTR_FLAGS, 0);\n                iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);\n                creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);\n                lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);\n                String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);\n                if (\"true\".equals(valueString)) {\n                    partial = true;\n                }\n\n                while ((type = parser.next()) != XmlPullParser.START_TAG\n                        && type != XmlPullParser.END_DOCUMENT) {\n                }\n                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {\n                    type = parser.next();\n                    if (type == XmlPullParser.TEXT) {\n                        name = parser.getText();\n                    }\n                }\n            }\n\n            VUserInfo userInfo = new VUserInfo(id, name, iconPath, flags);\n            userInfo.serialNumber = serialNumber;\n            userInfo.creationTime = creationTime;\n            userInfo.lastLoggedInTime = lastLoggedInTime;\n            userInfo.partial = partial;\n            return userInfo;\n\n        } catch (IOException ioe) {\n        } catch (XmlPullParserException pe) {\n        } finally {\n            if (fis != null) {\n                try {\n                    fis.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n        return null;\n    }\n\n    private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {\n        String valueString = parser.getAttributeValue(null, attr);\n        if (valueString == null) return defaultValue;\n        try {\n            return Integer.parseInt(valueString);\n        } catch (NumberFormatException nfe) {\n            return defaultValue;\n        }\n    }\n\n    private long readLongAttribute(XmlPullParser parser, String attr, long defaultValue) {\n        String valueString = parser.getAttributeValue(null, attr);\n        if (valueString == null) return defaultValue;\n        try {\n            return Long.parseLong(valueString);\n        } catch (NumberFormatException nfe) {\n            return defaultValue;\n        }\n    }\n\n    @Override\n    public VUserInfo createUser(String name, int flags) {\n        checkManageUsersPermission(\"Only the system can create users\");\n\n        final long ident = Binder.clearCallingIdentity();\n        final VUserInfo userInfo;\n        try {\n            synchronized (mInstallLock) {\n                synchronized (mPackagesLock) {\n                    if (isUserLimitReachedLocked()) return null;\n                    int userId = getNextAvailableIdLocked();\n                    userInfo = new VUserInfo(userId, name, null, flags);\n                    File userPath = new File(mBaseUserPath, Integer.toString(userId));\n                    userInfo.serialNumber = mNextSerialNumber++;\n                    long now = System.currentTimeMillis();\n                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;\n                    userInfo.partial = true;\n                    VEnvironment.getUserSystemDirectory(userInfo.id).mkdirs();\n                    mUsers.put(userId, userInfo);\n                    writeUserListLocked();\n                    writeUserLocked(userInfo);\n                    mPm.createNewUser(userId, userPath);\n                    userInfo.partial = false;\n                    writeUserLocked(userInfo);\n                    updateUserIdsLocked();\n                }\n            }\n            Intent addedIntent = new Intent(Constants.ACTION_USER_ADDED);\n            addedIntent.putExtra(Constants.EXTRA_USER_HANDLE, userInfo.id);\n            VActivityManagerService.get().sendBroadcastAsUser(addedIntent, VUserHandle.ALL,\n                        null);\n        } finally {\n            Binder.restoreCallingIdentity(ident);\n        }\n        return userInfo;\n    }\n\n    /**\n     * Removes a user and all data directories created for that user. This method should be called\n     * after the user's processes have been terminated.\n     * @param userHandle the user's id\n     */\n    public boolean removeUser(int userHandle) {\n        checkManageUsersPermission(\"Only the system can remove users\");\n        final VUserInfo user;\n        synchronized (mPackagesLock) {\n            user = mUsers.get(userHandle);\n            if (userHandle == 0 || user == null) {\n                return false;\n            }\n            mRemovingUserIds.add(userHandle);\n            // Set this to a partially created user, so that the user will be purged\n            // on next startup, in case the runtime stops now before stopping and\n            // removing the user completely.\n            user.partial = true;\n            writeUserLocked(user);\n        }\n        if (DBG) VLog.i(LOG_TAG, \"Stopping user \" + userHandle);\n        int res = VActivityManagerService.get().stopUser(userHandle,\n                    new IStopUserCallback.Stub() {\n                        @Override\n                        public void userStopped(int userId) {\n                            finishRemoveUser(userId);\n                        }\n                        @Override\n                        public void userStopAborted(int userId) {\n                        }\n            });\n        return res == ActivityManagerCompat.USER_OP_SUCCESS;\n    }\n\n    void finishRemoveUser(final int userHandle) {\n        if (DBG) VLog.i(LOG_TAG, \"finishRemoveUser \" + userHandle);\n        // Let other services shutdown any activity and clean up their state before completely\n        // wiping the user's system directory and removing from the user list\n        long identity = Binder.clearCallingIdentity();\n        try {\n            Intent addedIntent = new Intent(Constants.ACTION_USER_REMOVED);\n            addedIntent.putExtra(Constants.EXTRA_USER_HANDLE, userHandle);\n            VActivityManagerService.get().sendOrderedBroadcastAsUser(addedIntent, VUserHandle.ALL,\n                   null,\n                    new BroadcastReceiver() {\n                        @Override\n                        public void onReceive(Context context, Intent intent) {\n                            if (DBG) {\n                                VLog.i(LOG_TAG,\n                                        \"USER_REMOVED broadcast sent, cleaning up user data \"\n                                        + userHandle);\n                            }\n                            new Thread() {\n                                public void run() {\n                                    synchronized (mInstallLock) {\n                                        synchronized (mPackagesLock) {\n                                            removeUserStateLocked(userHandle);\n                                        }\n                                    }\n                                }\n                            }.start();\n                        }\n                    },\n                    null, Activity.RESULT_OK, null, null);\n        } finally {\n            Binder.restoreCallingIdentity(identity);\n        }\n    }\n\n    private void removeUserStateLocked(int userHandle) {\n        // Cleanup package manager settings\n        mPm.cleanUpUser(userHandle);\n\n        // Remove this user from the list\n        mUsers.remove(userHandle);\n        mRemovingUserIds.remove(userHandle);\n        // Remove user file\n        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + \".xml\"));\n        userFile.delete();\n        // Update the user list\n        writeUserListLocked();\n        updateUserIdsLocked();\n        removeDirectoryRecursive(VEnvironment.getUserSystemDirectory(userHandle));\n    }\n\n    private void removeDirectoryRecursive(File parent) {\n        if (parent.isDirectory()) {\n            String[] files = parent.list();\n            for (String filename : files) {\n                File child = new File(parent, filename);\n                removeDirectoryRecursive(child);\n            }\n        }\n        parent.delete();\n    }\n\n    @Override\n    public int getUserSerialNumber(int userHandle) {\n        synchronized (mPackagesLock) {\n            if (!exists(userHandle)) return -1;\n            return getUserInfoLocked(userHandle).serialNumber;\n        }\n    }\n\n    @Override\n    public int getUserHandle(int userSerialNumber) {\n        synchronized (mPackagesLock) {\n            for (int userId : mUserIds) {\n                if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;\n            }\n            // Not found\n            return -1;\n        }\n    }\n\n    /**\n     * Caches the list of user ids in an array, adjusting the array size when necessary.\n     */\n    private void updateUserIdsLocked() {\n        int num = 0;\n        for (int i = 0; i < mUsers.size(); i++) {\n            if (!mUsers.valueAt(i).partial) {\n                num++;\n            }\n        }\n        final int[] newUsers = new int[num];\n        int n = 0;\n        for (int i = 0; i < mUsers.size(); i++) {\n            if (!mUsers.valueAt(i).partial) {\n                newUsers[n++] = mUsers.keyAt(i);\n            }\n        }\n        mUserIds = newUsers;\n    }\n\n    /**\n     * Make a note of the last started time of a user.\n     * @param userId the user that was just foregrounded\n     */\n    public void userForeground(int userId) {\n        synchronized (mPackagesLock) {\n            VUserInfo user = mUsers.get(userId);\n            long now = System.currentTimeMillis();\n            if (user == null || user.partial) {\n                VLog.w(LOG_TAG, \"userForeground: unknown user #\" + userId);\n                return;\n            }\n            if (now > EPOCH_PLUS_30_YEARS) {\n                user.lastLoggedInTime = now;\n                writeUserLocked(user);\n            }\n        }\n    }\n\n    /**\n     * Returns the next available user id, filling in any holes in the ids.\n     * TODO: May not be a good idea to recycle ids, in case it results in confusion\n     * for data and battery stats collection, or unexpected cross-talk.\n     * @return\n     */\n    private int getNextAvailableIdLocked() {\n        synchronized (mPackagesLock) {\n            int i = mNextUserId;\n            while (i < Integer.MAX_VALUE) {\n                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) {\n                    break;\n                }\n                i++;\n            }\n            mNextUserId = i + 1;\n            return i;\n        }\n    }\n\n    @Override\n    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {\n\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/FileBridge.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.system.OsConstants;\nimport android.util.Log;\n\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.FileUtils;\n\nimport java.io.FileDescriptor;\nimport java.io.IOException;\nimport java.nio.ByteOrder;\n\nimport static android.system.OsConstants.AF_UNIX;\nimport static android.system.OsConstants.SOCK_STREAM;\n\n/**\n * Simple bridge that allows file access across process boundaries without\n * returning the underlying {@link FileDescriptor}. This is useful when the\n * server side needs to strongly assert that a client side is completely\n * hands-off.\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class FileBridge extends Thread {\n    private static final String TAG = \"FileBridge\";\n\n    // TODO: consider extending to support bidirectional IO\n\n    private static final int MSG_LENGTH = 8;\n\n    /**\n     * CMD_WRITE [len] [data]\n     */\n    private static final int CMD_WRITE = 1;\n    /**\n     * CMD_FSYNC\n     */\n    private static final int CMD_FSYNC = 2;\n    /**\n     * CMD_CLOSE\n     */\n    private static final int CMD_CLOSE = 3;\n\n    private FileDescriptor mTarget;\n\n    private final FileDescriptor mServer = new FileDescriptor();\n    private final FileDescriptor mClient = new FileDescriptor();\n\n    private volatile boolean mClosed;\n\n\n    public FileBridge() {\n        try {\n            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);\n        } catch (ErrnoException e) {\n            throw new RuntimeException(\"Failed to create bridge\");\n        }\n    }\n\n    public boolean isClosed() {\n        return mClosed;\n    }\n\n    public void forceClose() {\n        closeQuietly(mTarget);\n        closeQuietly(mServer);\n        closeQuietly(mClient);\n        mClosed = true;\n    }\n\n    public void setTargetFile(FileDescriptor target) {\n        mTarget = target;\n    }\n\n    public FileDescriptor getClientSocket() {\n        return mClient;\n    }\n\n    @Override\n    public void run() {\n        final byte[] temp = new byte[8192];\n        try {\n            while (read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {\n                final int cmd = FileUtils.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);\n                if (cmd == CMD_WRITE) {\n                    // Shuttle data into local file\n                    int len = FileUtils.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);\n                    while (len > 0) {\n                        int n = read(mServer, temp, 0, Math.min(temp.length, len));\n                        if (n == -1) {\n                            throw new IOException(\n                                    \"Unexpected EOF; still expected \" + len + \" bytes\");\n                        }\n                        write(mTarget, temp, 0, n);\n                        len -= n;\n                    }\n\n                } else if (cmd == CMD_FSYNC) {\n                    // Sync and echo back to confirm\n                    Os.fsync(mTarget);\n                    write(mServer, temp, 0, MSG_LENGTH);\n\n                } else if (cmd == CMD_CLOSE) {\n                    // Close and echo back to confirm\n                    Os.fsync(mTarget);\n                    Os.close(mTarget);\n                    mClosed = true;\n                    write(mServer, temp, 0, MSG_LENGTH);\n                    break;\n                }\n            }\n\n        } catch (ErrnoException | IOException e) {\n            Log.wtf(TAG, \"Failed during bridge\", e);\n        } finally {\n            forceClose();\n        }\n    }\n\n\n    public static void closeQuietly(FileDescriptor fd) {\n\n        if (fd != null && fd.valid()) {\n            try {\n                Os.close(fd);\n            } catch (ErrnoException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional\n     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).\n     */\n    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {\n        ArrayUtils.checkOffsetAndCount(bytes.length, byteOffset, byteCount);\n        if (byteCount == 0) {\n            return 0;\n        }\n        try {\n            int readCount = Os.read(fd, bytes, byteOffset, byteCount);\n            if (readCount == 0) {\n                return -1;\n            }\n            return readCount;\n        } catch (ErrnoException errnoException) {\n            if (errnoException.errno == OsConstants.EAGAIN) {\n                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.\n                return 0;\n            }\n            throw new IOException(errnoException);\n        }\n    }\n\n    /**\n     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike\n     * Unix it never just writes as many bytes as happens to be convenient.)\n     */\n    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {\n        ArrayUtils.checkOffsetAndCount(bytes.length, byteOffset, byteCount);\n        if (byteCount == 0) {\n            return;\n        }\n        try {\n            while (byteCount > 0) {\n                int bytesWritten = Os.write(fd, bytes, byteOffset, byteCount);\n                byteCount -= bytesWritten;\n                byteOffset += bytesWritten;\n            }\n        } catch (ErrnoException errnoException) {\n            throw new IOException(errnoException);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/PackageHelper.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.lody.virtual.server.pm.installer;\n\nimport android.annotation.TargetApi;\nimport android.content.pm.PackageInstaller;\nimport android.os.Build;\n\n/**\n * Constants used internally between the PackageManager\n * and media container service transports.\n * Some utility methods to invoke MountService api.\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class PackageHelper {\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver on success.\n     *\n     * @hide\n     */\n    public static final int INSTALL_SUCCEEDED = 1;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the package is already installed.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the package archive file is invalid.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_INVALID_APK = -2;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the URI passed in is invalid.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_INVALID_URI = -3;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the package manager service found that\n     * the device didn't have enough storage space to install the app.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if a package is already installed with\n     * the same name.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the requested shared user does not\n     * exist.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_NO_SHARED_USER = -6;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if a previously installed package of the\n     * same name has a different signature than the new package (and the old\n     * package's data was not removed).\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package is requested a shared\n     * user which is already installed on the device and does not have matching\n     * signature.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package uses a shared library\n     * that is not available.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package uses a shared library\n     * that is not available.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package failed while\n     * optimizing and validating its dex files, either because there was not\n     * enough storage or the validation failed.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_DEXOPT = -11;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package failed because the\n     * current SDK version is older than that required by the package.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_OLDER_SDK = -12;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package failed because it\n     * contains a content provider with the same authority as a provider already\n     * installed in the system.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package failed because the\n     * current SDK version is newer than that required by the package.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_NEWER_SDK = -14;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package failed because it has\n     * specified that it is a test-only package and the caller has not supplied\n     * the NSTALL_ALLOW_TEST flag.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_TEST_ONLY = -15;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the package being installed contains\n     * native code, but none that is compatible with the device's CPU_ABI.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package uses a feature that is\n     * not available.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_MISSING_FEATURE = -17;\n\n    // ------ Errors related to sdcard\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if a secure container mount point\n     * couldn't be accessed on external media.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package couldn't be installed\n     * in the specified install location.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package couldn't be installed\n     * in the specified install location because the media is not available.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package couldn't be installed\n     * because the verification timed out.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package couldn't be installed\n     * because the verification did not succeed.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the package changed from what the\n     * calling program expected.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package is assigned a\n     * different UID than it previously held.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_UID_CHANGED = -24;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the new package has an older version\n     * code than the currently installed package.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;\n\n    /**\n     * Installation return code: this is passed to the\n     * IPackageInstallObserver if the old package has target SDK high\n     * enough to support runtime permission and the new package has target SDK\n     * low enough to not support runtime permissions.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser was given a path that is\n     * not a file, or does not end with the expected '.apk' extension.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser was unable to retrieve the\n     * AndroidManifest.xml file.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser encountered an unexpected\n     * exception.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser did not find any\n     * certificates in the .apk.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser found inconsistent\n     * certificates on the files in the .apk.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser encountered a\n     * CertificateEncodingException in one of the files in the .apk.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser encountered a bad or\n     * missing package name in the manifest.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser encountered a bad shared\n     * user id name in the manifest.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser encountered some structural\n     * problem in the manifest.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;\n\n    /**\n     * Installation parse return code: this is passed to the\n     * IPackageInstallObserver if the parser did not find any actionable\n     * tags (instrumentation or application) in the manifest.\n     *\n     * @hide\n     */\n    public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;\n\n    /**\n     * Installation failed return code: this is passed to the\n     * IPackageInstallObserver if the system failed to install the\n     * package because of system issues.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;\n\n    /**\n     * Installation failed return code: this is passed to the\n     * IPackageInstallObserver if the system failed to install the\n     * package because the user is restricted from installing apps.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_USER_RESTRICTED = -111;\n\n    /**\n     * Installation failed return code: this is passed to the\n     * IPackageInstallObserver if the system failed to install the\n     * package because it is attempting to define a permission that is already\n     * defined by some existing package.\n     * <p>\n     * The package name of the app which has already defined the permission is\n     * passed to a PackageInstallObserver, if any, as the\n     * EXTRA_FAILURE_EXISTING_PACKAGE string extra; and the name of the\n     * permission being redefined is passed in the\n     * EXTRA_FAILURE_EXISTING_PERMISSION string extra.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;\n\n    /**\n     * Installation failed return code: this is passed to the\n     * IPackageInstallObserver if the system failed to install the\n     * package because its packaged native code did not match any of the ABIs\n     * supported by the system.\n     *\n     * @hide\n     */\n    public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -113;\n\n    /**\n     * Internal return code for NativeLibraryHelper methods to indicate that the package\n     * being processed did not contain any native code. This is placed here only so that\n     * it can belong to the same value space as the other install failure codes.\n     *\n     * @hide\n     */\n    public static final int NO_NATIVE_LIBRARIES = -114;\n\n    /** {@hide} */\n    public static final int INSTALL_FAILED_ABORTED = -115;\n\n    /**\n     * Installation failed return code: ephemeral app installs are incompatible with some\n     * other installation flags supplied for the operation; or other circumstances such\n     * as trying to upgrade a system app via an ephemeral install.\n     * @hide\n     */\n    public static final int INSTALL_FAILED_EPHEMERAL_INVALID = -116;\n\n    \n    \n    \n    /**\n            * Return code for when package deletion succeeds. This is passed to the\n     * IPackageDeleteObserver if the system succeeded in deleting the\n     * package.\n             *\n             * @hide\n     */\n    public static final int DELETE_SUCCEEDED = 1;\n\n    /**\n     * Deletion failed return code: this is passed to the\n     * IPackageDeleteObserver if the system failed to delete the package\n     * for an unspecified reason.\n     *\n     * @hide\n     */\n    public static final int DELETE_FAILED_INTERNAL_ERROR = -1;\n\n    /**\n     * Deletion failed return code: this is passed to the\n     * IPackageDeleteObserver if the system failed to delete the package\n     * because it is the active DevicePolicy manager.\n     *\n     * @hide\n     */\n    public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;\n\n    /**\n     * Deletion failed return code: this is passed to the\n     * IPackageDeleteObserver if the system failed to delete the package\n     * since the user is restricted.\n     *\n     * @hide\n     */\n    public static final int DELETE_FAILED_USER_RESTRICTED = -3;\n\n    /**\n     * Deletion failed return code: this is passed to the\n     * IPackageDeleteObserver if the system failed to delete the package\n     * because a profile or device owner has marked the package as\n     * uninstallable.\n     *\n     * @hide\n     */\n    public static final int DELETE_FAILED_OWNER_BLOCKED = -4;\n\n    /** {@hide} */\n    public static final int DELETE_FAILED_ABORTED = -5;\n\n    /**\n     * Return code that is passed to the IPackageMoveObserver when the\n     * package has been successfully moved by the system.\n     *\n     * @hide\n     */\n    public static final int MOVE_SUCCEEDED = -100;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver when the\n     * package hasn't been successfully moved by the system because of\n     * insufficient memory on specified media.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_INSUFFICIENT_STORAGE = -1;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package doesn't exist.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_DOESNT_EXIST = -2;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package cannot be moved since its a system package.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_SYSTEM_PACKAGE = -3;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package cannot be moved since its forward locked.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_FORWARD_LOCKED = -4;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package cannot be moved to the specified location.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_INVALID_LOCATION = -5;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package cannot be moved to the specified location.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_INTERNAL_ERROR = -6;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package already has an operation pending in the queue.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_OPERATION_PENDING = -7;\n\n    /**\n     * Error code that is passed to the IPackageMoveObserver if the\n     * specified package cannot be moved since it contains a device admin.\n     *\n     * @hide\n     */\n    public static final int MOVE_FAILED_DEVICE_ADMIN = -8;\n\n\n    public static String installStatusToString(int status, String msg) {\n        final String str = installStatusToString(status);\n        if (msg != null) {\n            return str + \": \" + msg;\n        } else {\n            return str;\n        }\n    }\n\n    /** {@hide} */\n    public static String installStatusToString(int status) {\n        switch (status) {\n            case INSTALL_SUCCEEDED: return \"INSTALL_SUCCEEDED\";\n            case INSTALL_FAILED_ALREADY_EXISTS: return \"INSTALL_FAILED_ALREADY_EXISTS\";\n            case INSTALL_FAILED_INVALID_APK: return \"INSTALL_FAILED_INVALID_APK\";\n            case INSTALL_FAILED_INVALID_URI: return \"INSTALL_FAILED_INVALID_URI\";\n            case INSTALL_FAILED_INSUFFICIENT_STORAGE: return \"INSTALL_FAILED_INSUFFICIENT_STORAGE\";\n            case INSTALL_FAILED_DUPLICATE_PACKAGE: return \"INSTALL_FAILED_DUPLICATE_PACKAGE\";\n            case INSTALL_FAILED_NO_SHARED_USER: return \"INSTALL_FAILED_NO_SHARED_USER\";\n            case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return \"INSTALL_FAILED_UPDATE_INCOMPATIBLE\";\n            case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return \"INSTALL_FAILED_SHARED_USER_INCOMPATIBLE\";\n            case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return \"INSTALL_FAILED_MISSING_SHARED_LIBRARY\";\n            case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return \"INSTALL_FAILED_REPLACE_COULDNT_DELETE\";\n            case INSTALL_FAILED_DEXOPT: return \"INSTALL_FAILED_DEXOPT\";\n            case INSTALL_FAILED_OLDER_SDK: return \"INSTALL_FAILED_OLDER_SDK\";\n            case INSTALL_FAILED_CONFLICTING_PROVIDER: return \"INSTALL_FAILED_CONFLICTING_PROVIDER\";\n            case INSTALL_FAILED_NEWER_SDK: return \"INSTALL_FAILED_NEWER_SDK\";\n            case INSTALL_FAILED_TEST_ONLY: return \"INSTALL_FAILED_TEST_ONLY\";\n            case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return \"INSTALL_FAILED_CPU_ABI_INCOMPATIBLE\";\n            case INSTALL_FAILED_MISSING_FEATURE: return \"INSTALL_FAILED_MISSING_FEATURE\";\n            case INSTALL_FAILED_CONTAINER_ERROR: return \"INSTALL_FAILED_CONTAINER_ERROR\";\n            case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return \"INSTALL_FAILED_INVALID_INSTALL_LOCATION\";\n            case INSTALL_FAILED_MEDIA_UNAVAILABLE: return \"INSTALL_FAILED_MEDIA_UNAVAILABLE\";\n            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return \"INSTALL_FAILED_VERIFICATION_TIMEOUT\";\n            case INSTALL_FAILED_VERIFICATION_FAILURE: return \"INSTALL_FAILED_VERIFICATION_FAILURE\";\n            case INSTALL_FAILED_PACKAGE_CHANGED: return \"INSTALL_FAILED_PACKAGE_CHANGED\";\n            case INSTALL_FAILED_UID_CHANGED: return \"INSTALL_FAILED_UID_CHANGED\";\n            case INSTALL_FAILED_VERSION_DOWNGRADE: return \"INSTALL_FAILED_VERSION_DOWNGRADE\";\n            case INSTALL_PARSE_FAILED_NOT_APK: return \"INSTALL_PARSE_FAILED_NOT_APK\";\n            case INSTALL_PARSE_FAILED_BAD_MANIFEST: return \"INSTALL_PARSE_FAILED_BAD_MANIFEST\";\n            case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return \"INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION\";\n            case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return \"INSTALL_PARSE_FAILED_NO_CERTIFICATES\";\n            case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return \"INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES\";\n            case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return \"INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING\";\n            case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return \"INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME\";\n            case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return \"INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID\";\n            case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return \"INSTALL_PARSE_FAILED_MANIFEST_MALFORMED\";\n            case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return \"INSTALL_PARSE_FAILED_MANIFEST_EMPTY\";\n            case INSTALL_FAILED_INTERNAL_ERROR: return \"INSTALL_FAILED_INTERNAL_ERROR\";\n            case INSTALL_FAILED_USER_RESTRICTED: return \"INSTALL_FAILED_USER_RESTRICTED\";\n            case INSTALL_FAILED_DUPLICATE_PERMISSION: return \"INSTALL_FAILED_DUPLICATE_PERMISSION\";\n            case INSTALL_FAILED_NO_MATCHING_ABIS: return \"INSTALL_FAILED_NO_MATCHING_ABIS\";\n            case INSTALL_FAILED_ABORTED: return \"INSTALL_FAILED_ABORTED\";\n            default: return Integer.toString(status);\n        }\n    }\n\n    /** {@hide} */\n    public static int installStatusToPublicStatus(int status) {\n        switch (status) {\n            case INSTALL_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;\n            case INSTALL_FAILED_ALREADY_EXISTS: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_INVALID_APK: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_INVALID_URI: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_INSUFFICIENT_STORAGE: return PackageInstaller.STATUS_FAILURE_STORAGE;\n            case INSTALL_FAILED_DUPLICATE_PACKAGE: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_NO_SHARED_USER: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_DEXOPT: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_OLDER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_CONFLICTING_PROVIDER: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_NEWER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_TEST_ONLY: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_MISSING_FEATURE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_CONTAINER_ERROR: return PackageInstaller.STATUS_FAILURE_STORAGE;\n            case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return PackageInstaller.STATUS_FAILURE_STORAGE;\n            case INSTALL_FAILED_MEDIA_UNAVAILABLE: return PackageInstaller.STATUS_FAILURE_STORAGE;\n            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return PackageInstaller.STATUS_FAILURE_ABORTED;\n            case INSTALL_FAILED_VERIFICATION_FAILURE: return PackageInstaller.STATUS_FAILURE_ABORTED;\n            case INSTALL_FAILED_PACKAGE_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_UID_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_VERSION_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_NOT_APK: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_BAD_MANIFEST: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;\n            case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;\n            case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;\n            case INSTALL_FAILED_NO_MATCHING_ABIS: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;\n            case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;\n            default: return PackageInstaller.STATUS_FAILURE;\n        }\n    }\n\n\n    public static String deleteStatusToString(boolean status) {\n        return status ? \"DELETE_SUCCEEDED\" : \"DELETE_FAILED\";\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallInfo.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\n/**\n * @author Lody\n */\n\npublic class PackageInstallInfo {\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallObserver.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\nimport android.content.Intent;\nimport android.content.pm.IPackageInstallObserver2;\nimport android.os.Bundle;\n\npublic class PackageInstallObserver {\n    private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() {\n        @Override\n        public void onUserActionRequired(Intent intent) {\n            PackageInstallObserver.this.onUserActionRequired(intent);\n        }\n\n        @Override\n        public void onPackageInstalled(String basePackageName, int returnCode,\n                                       String msg, Bundle extras) {\n            PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg,\n                    extras);\n        }\n    };\n\n    /**\n     * {@hide}\n     */\n    public IPackageInstallObserver2 getBinder() {\n        return mBinder;\n    }\n\n    public void onUserActionRequired(Intent intent) {\n    }\n\n    /**\n     * This method will be called to report the result of the package\n     * installation attempt.\n     *\n     * @param basePackageName Name of the package whose installation was\n     *                        attempted\n     * @param extras          If non-null, this Bundle contains extras providing\n     *                        additional information about an install failure. See\n     *                        {@link android.content.pm.PackageManager} for documentation\n     *                        about which extras apply to various failures; in particular\n     *                        the strings named EXTRA_FAILURE_*.\n     * @param returnCode      The numeric success or failure code indicating the\n     *                        basic outcome\n     * @hide\n     */\n    public void onPackageInstalled(String basePackageName, int returnCode, String msg,\n                                   Bundle extras) {\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentSender;\nimport android.content.pm.IPackageInstallObserver2;\nimport android.content.pm.IPackageInstallerSession;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.ParcelFileDescriptor;\nimport android.os.RemoteException;\nimport android.system.ErrnoException;\nimport android.system.Os;\nimport android.system.OsConstants;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.helper.utils.FileUtils;\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.io.File;\nimport java.io.FileDescriptor;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static android.system.OsConstants.O_CREAT;\nimport static android.system.OsConstants.O_RDONLY;\nimport static android.system.OsConstants.O_WRONLY;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class PackageInstallerSession extends IPackageInstallerSession.Stub {\n\n    public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;\n    public static final int INSTALL_FAILED_ABORTED = -115;\n    public static final int INSTALL_SUCCEEDED = 1;\n    public static final int INSTALL_FAILED_INVALID_APK = -2;\n\n    private static final String TAG = \"PackageInstaller\";\n    private static final String REMOVE_SPLIT_MARKER_EXTENSION = \".removed\";\n\n    private static final int MSG_COMMIT = 0;\n\n\n    private final VPackageInstallerService.InternalCallback mCallback;\n    private final Context mContext;\n    private final Handler mHandler;\n\n    final int sessionId;\n    final int userId;\n    final int installerUid;\n\n    final SessionParams params;\n    final String installerPackageName;\n    private boolean mPermissionsAccepted;\n\n    /**\n     * Staging location where client data is written.\n     */\n    final File stageDir;\n\n    private final AtomicInteger mActiveCount = new AtomicInteger();\n\n    private final Object mLock = new Object();\n\n    private float mClientProgress = 0;\n    private float mInternalProgress = 0;\n    private float mProgress = 0;\n    private float mReportedProgress = -1;\n    private boolean mPrepared = false;\n    private boolean mSealed = false;\n    private boolean mDestroyed = false;\n    private int mFinalStatus;\n    private String mFinalMessage;\n\n    private IPackageInstallObserver2 mRemoteObserver;\n\n    private ArrayList<FileBridge> mBridges = new ArrayList<>();\n\n    private File mResolvedStageDir;\n\n    /**\n     * Fields derived from commit parsing\n     */\n    private String mPackageName;\n\n    private File mResolvedBaseFile;\n    private final List<File> mResolvedStagedFiles = new ArrayList<>();\n\n\n    private final Handler.Callback mHandlerCallback = new Handler.Callback() {\n        @Override\n        public boolean handleMessage(Message msg) {\n            synchronized (mLock) {\n                if (msg.obj != null) {\n                    mRemoteObserver = (IPackageInstallObserver2) msg.obj;\n                }\n                try {\n                    commitLocked();\n                } catch (PackageManagerException e) {\n                    final String completeMsg = getCompleteMessage(e);\n                    VLog.e(TAG, \"Commit of session \" + sessionId + \" failed: \" + completeMsg);\n                    destroyInternal();\n                    dispatchSessionFinished(e.error, completeMsg, null);\n                }\n\n                return true;\n            }\n        }\n    };\n\n    public PackageInstallerSession(VPackageInstallerService.InternalCallback callback, Context context, Looper looper, String installerPackageName, int sessionId, int userId, int installerUid, SessionParams params, File stageDir) {\n        this.mCallback = callback;\n        this.mContext = context;\n        this.mHandler = new Handler(looper, mHandlerCallback);\n        this.installerPackageName = installerPackageName;\n        this.sessionId = sessionId;\n        this.userId = userId;\n        this.installerUid = installerUid;\n        this.mPackageName = params.appPackageName;\n        this.params = params;\n        this.stageDir = stageDir;\n    }\n\n    public SessionInfo generateInfo() {\n        final SessionInfo info = new SessionInfo();\n        synchronized (mLock) {\n            info.sessionId = sessionId;\n            info.installerPackageName = installerPackageName;\n            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?\n                    mResolvedBaseFile.getAbsolutePath() : null;\n            info.progress = mProgress;\n            info.sealed = mSealed;\n            info.active = mActiveCount.get() > 0;\n\n            info.mode = params.mode;\n            info.sizeBytes = params.sizeBytes;\n            info.appPackageName = params.appPackageName;\n            info.appIcon = params.appIcon;\n            info.appLabel = params.appLabel;\n        }\n        return info;\n    }\n\n    private void commitLocked() throws PackageManagerException {\n        if (mDestroyed) {\n            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, \"Session destroyed\");\n        }\n        if (!mSealed) {\n            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, \"Session not sealed\");\n        }\n        try {\n            resolveStageDir();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        validateInstallLocked();\n        mInternalProgress = 0.5f;\n        computeProgressLocked(true);\n        // We've reached point of no return; call into PMS to install the stage.\n        // Regardless of success or failure we always destroy session.\n        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {\n            @Override\n            public void onUserActionRequired(Intent intent) {\n                throw new IllegalStateException();\n            }\n\n            @Override\n            public void onPackageInstalled(String basePackageName, int returnCode, String msg,\n                                           Bundle extras) {\n                destroyInternal();\n                dispatchSessionFinished(returnCode, msg, extras);\n            }\n        };\n    }\n\n    private void validateInstallLocked() throws PackageManagerException {\n        mResolvedBaseFile = null;\n        mResolvedStagedFiles.clear();\n        File[] addedFiles = this.mResolvedStageDir.listFiles();\n        if (addedFiles == null || addedFiles.length == 0) {\n            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, \"No packages staged\");\n        }\n        for (File addedFile : addedFiles) {\n            if (!addedFile.isDirectory()) {\n                final String targetName = \"base.apk\";\n                final File targetFile = new File(mResolvedStageDir, targetName);\n                if (!addedFile.equals(targetFile)) {\n                    addedFile.renameTo(targetFile);\n                }\n                mResolvedBaseFile = targetFile;\n                mResolvedStagedFiles.add(targetFile);\n            }\n        }\n        if (mResolvedBaseFile == null) {\n            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,\n                    \"Full install must include a base package\");\n        }\n    }\n\n    @Override\n    public void setClientProgress(float progress) throws RemoteException {\n        synchronized (mLock) {\n            // Always publish first staging movement\n            final boolean forcePublish = (mClientProgress == 0);\n            mClientProgress = progress;\n            computeProgressLocked(forcePublish);\n        }\n    }\n\n\n    private static float constrain(float amount, float low, float high) {\n        return amount < low ? low : (amount > high ? high : amount);\n    }\n\n    private void computeProgressLocked(boolean forcePublish) {\n        mProgress = constrain(mClientProgress * 0.8f, 0f, 0.8f)\n                + constrain(mInternalProgress * 0.2f, 0f, 0.2f);\n\n        // Only publish when meaningful change\n        if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {\n            mReportedProgress = mProgress;\n            mCallback.onSessionProgressChanged(this, mProgress);\n        }\n    }\n\n    @Override\n    public void addClientProgress(float progress) throws RemoteException {\n        synchronized (mLock) {\n            setClientProgress(mClientProgress + progress);\n        }\n    }\n\n    @Override\n    public String[] getNames() throws RemoteException {\n        assertPreparedAndNotSealed(\"getNames\");\n        try {\n            return resolveStageDir().list();\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    /**\n     * Resolve the actual location where staged data should be written. This\n     * might point at an ASEC mount point, which is why we delay path resolution\n     * until someone actively works with the session.\n     */\n    private File resolveStageDir() throws IOException {\n        synchronized (mLock) {\n            if (mResolvedStageDir == null && stageDir != null) {\n                mResolvedStageDir = stageDir;\n                if (!stageDir.exists()) {\n                    stageDir.mkdirs();\n                }\n            }\n            return mResolvedStageDir;\n        }\n    }\n\n    @Override\n    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) throws RemoteException {\n        try {\n            return openWriteInternal(name, offsetBytes, lengthBytes);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    private void assertPreparedAndNotSealed(String cookie) {\n        synchronized (mLock) {\n            if (!mPrepared) {\n                throw new IllegalStateException(cookie + \" before prepared\");\n            }\n            if (mSealed) {\n                throw new SecurityException(cookie + \" not allowed after commit\");\n            }\n        }\n    }\n\n\n    private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)\n            throws IOException {\n        // Quick sanity check of state, and allocate a pipe for ourselves. We\n        // then do heavy disk allocation outside the lock, but this open pipe\n        // will block any attempted install transitions.\n        final FileBridge bridge;\n        synchronized (mLock) {\n            assertPreparedAndNotSealed(\"openWrite\");\n\n            bridge = new FileBridge();\n            mBridges.add(bridge);\n        }\n        try {\n            final File target = new File(resolveStageDir(), name);\n            // TODO: this should delegate to DCS so the system process avoids\n            // holding open FDs into containers.\n            final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),\n                    O_CREAT | O_WRONLY, 0644);\n            // If caller specified a total length, allocate it for them. Free up\n            // cache space to grow, if needed.\n            if (lengthBytes > 0) {\n                Os.posix_fallocate(targetFd, 0, lengthBytes);\n            }\n            if (offsetBytes > 0) {\n                Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);\n            }\n            bridge.setTargetFile(targetFd);\n            bridge.start();\n            return ParcelFileDescriptor.dup(bridge.getClientSocket());\n\n        } catch (ErrnoException e) {\n            throw new IOException(e);\n        }\n    }\n\n    @Override\n    public ParcelFileDescriptor openRead(String name) throws RemoteException {\n        try {\n            return openReadInternal(name);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    private ParcelFileDescriptor openReadInternal(String name) throws IOException {\n        assertPreparedAndNotSealed(\"openRead\");\n\n        try {\n            if (!FileUtils.isValidExtFilename(name)) {\n                throw new IllegalArgumentException(\"Invalid name: \" + name);\n            }\n            final File target = new File(resolveStageDir(), name);\n\n            final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);\n            return ParcelFileDescriptor.dup(targetFd);\n\n        } catch (ErrnoException e) {\n            throw new IOException(e);\n        }\n    }\n\n    @Override\n    public void removeSplit(String splitName) throws RemoteException {\n        if (TextUtils.isEmpty(params.appPackageName)) {\n            throw new IllegalStateException(\"Must specify package name to remove a split\");\n        }\n        try {\n            createRemoveSplitMarker(splitName);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    private void createRemoveSplitMarker(String splitName) throws IOException {\n        try {\n            final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;\n            if (!FileUtils.isValidExtFilename(markerName)) {\n                throw new IllegalArgumentException(\"Invalid marker: \" + markerName);\n            }\n            final File target = new File(resolveStageDir(), markerName);\n            target.createNewFile();\n            Os.chmod(target.getAbsolutePath(), 0 /*mode*/);\n        } catch (ErrnoException e) {\n            throw new IOException(e);\n        }\n    }\n\n    @Override\n    public void close() throws RemoteException {\n        if (mActiveCount.decrementAndGet() == 0) {\n            mCallback.onSessionActiveChanged(this, false);\n        }\n    }\n\n    @Override\n    public void commit(IntentSender statusReceiver) throws RemoteException {\n        final boolean wasSealed;\n        synchronized (mLock) {\n            wasSealed = mSealed;\n            if (!mSealed) {\n                // Verify that all writers are hands-off\n                for (FileBridge bridge : mBridges) {\n                    if (!bridge.isClosed()) {\n                        throw new SecurityException(\"Files still open\");\n                    }\n                }\n                mSealed = true;\n            }\n\n            // Client staging is fully done at this point\n            mClientProgress = 1f;\n            computeProgressLocked(true);\n        }\n\n        if (!wasSealed) {\n            // Persist the fact that we've sealed ourselves to prevent\n            // mutations of any hard links we create. We do this without holding\n            // the session lock, since otherwise it's a lock inversion.\n            mCallback.onSessionSealedBlocking(this);\n        }\n\n        // This ongoing commit should keep session active, even though client\n        // will probably close their end.\n        mActiveCount.incrementAndGet();\n\n        final VPackageInstallerService.PackageInstallObserverAdapter adapter\n                = new VPackageInstallerService.PackageInstallObserverAdapter(mContext,\n                statusReceiver, sessionId, userId);\n        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();\n    }\n\n    @Override\n    public void abandon() throws RemoteException {\n        destroyInternal();\n        dispatchSessionFinished(INSTALL_FAILED_ABORTED, \"Session was abandoned\", null);\n    }\n\n    private void destroyInternal() {\n        synchronized (mLock) {\n            mSealed = true;\n            mDestroyed = true;\n\n            // Force shut down all bridges\n            for (FileBridge bridge : mBridges) {\n                bridge.forceClose();\n            }\n        }\n        if (stageDir != null) {\n            FileUtils.deleteDir(stageDir.getAbsolutePath());\n        }\n    }\n\n    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {\n        mFinalStatus = returnCode;\n        mFinalMessage = msg;\n\n        if (mRemoteObserver != null) {\n            try {\n                mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);\n            } catch (RemoteException ignored) {\n            }\n        }\n\n        final boolean success = (returnCode == INSTALL_SUCCEEDED);\n        mCallback.onSessionFinished(this, success);\n    }\n\n    void setPermissionsResult(boolean accepted) {\n        if (!mSealed) {\n            throw new SecurityException(\"Must be sealed to accept permissions\");\n        }\n\n        if (accepted) {\n            // Mark and kick off another install pass\n            synchronized (mLock) {\n                mPermissionsAccepted = true;\n            }\n            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();\n        } else {\n            destroyInternal();\n            dispatchSessionFinished(INSTALL_FAILED_ABORTED, \"User rejected permissions\", null);\n        }\n    }\n\n    public void open() throws IOException {\n        if (mActiveCount.getAndIncrement() == 0) {\n            mCallback.onSessionActiveChanged(this, true);\n        }\n\n        synchronized (mLock) {\n            if (!mPrepared) {\n                if (stageDir == null) {\n                    throw new IllegalArgumentException(\n                            \"Exactly one of stageDir or stageCid stage must be set\");\n                }\n                mPrepared = true;\n                mCallback.onSessionPrepared(this);\n            }\n        }\n    }\n\n\n    public static String getCompleteMessage(Throwable t) {\n        final StringBuilder builder = new StringBuilder();\n        builder.append(t.getMessage());\n        while ((t = t.getCause()) != null) {\n            builder.append(\": \").append(t.getMessage());\n        }\n        return builder.toString();\n    }\n\n    private class PackageManagerException extends Exception {\n        public final int error;\n\n        PackageManagerException(int error, String detailMessage) {\n            super(detailMessage);\n            this.error = error;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/SessionInfo.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\nimport android.graphics.Bitmap;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport mirror.android.content.pm.PackageInstaller;\n\n/**\n * @author Lody\n */\n\npublic class SessionInfo implements Parcelable {\n    \n    public int sessionId;\n    public String installerPackageName;\n    public String resolvedBaseCodePath;\n    public float progress;\n    public boolean sealed;\n    public boolean active;\n    public int mode;\n    public long sizeBytes;\n    public String appPackageName;\n    public Bitmap appIcon;\n    public CharSequence appLabel;\n\n\n    public android.content.pm.PackageInstaller.SessionInfo alloc() {\n        android.content.pm.PackageInstaller.SessionInfo sessionInfo = PackageInstaller.SessionInfo.ctor.newInstance();\n        PackageInstaller.SessionInfo.sessionId.set(sessionInfo, sessionId);\n        PackageInstaller.SessionInfo.installerPackageName.set(sessionInfo, installerPackageName);\n        PackageInstaller.SessionInfo.resolvedBaseCodePath.set(sessionInfo, resolvedBaseCodePath);\n        PackageInstaller.SessionInfo.progress.set(sessionInfo, progress);\n        PackageInstaller.SessionInfo.sealed.set(sessionInfo, sealed);\n        PackageInstaller.SessionInfo.active.set(sessionInfo, active);\n        PackageInstaller.SessionInfo.mode.set(sessionInfo, mode);\n        PackageInstaller.SessionInfo.sizeBytes.set(sessionInfo, sizeBytes);\n        PackageInstaller.SessionInfo.appPackageName.set(sessionInfo, appPackageName);\n        PackageInstaller.SessionInfo.appIcon.set(sessionInfo, appIcon);\n        PackageInstaller.SessionInfo.appLabel.set(sessionInfo, appLabel);\n        return sessionInfo;\n    }\n\n    public static SessionInfo realloc(android.content.pm.PackageInstaller.SessionInfo sessionInfo) {\n        SessionInfo info = new SessionInfo();\n        info.sessionId = PackageInstaller.SessionInfo.sessionId.get(sessionInfo);\n        info.installerPackageName = PackageInstaller.SessionInfo.installerPackageName.get(sessionInfo);\n        info.resolvedBaseCodePath = PackageInstaller.SessionInfo.resolvedBaseCodePath.get(sessionInfo);\n        info.progress = PackageInstaller.SessionInfo.progress.get(sessionInfo);\n        info.sealed = PackageInstaller.SessionInfo.sealed.get(sessionInfo);\n        info.active = PackageInstaller.SessionInfo.active.get(sessionInfo);\n        info.mode = PackageInstaller.SessionInfo.mode.get(sessionInfo);\n        info.sizeBytes = PackageInstaller.SessionInfo.sizeBytes.get(sessionInfo);\n        info.appPackageName = PackageInstaller.SessionInfo.appPackageName.get(sessionInfo);\n        info.appIcon = PackageInstaller.SessionInfo.appIcon.get(sessionInfo);\n        info.appLabel = PackageInstaller.SessionInfo.appLabel.get(sessionInfo);\n        return info;\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(this.sessionId);\n        dest.writeString(this.installerPackageName);\n        dest.writeString(this.resolvedBaseCodePath);\n        dest.writeFloat(this.progress);\n        dest.writeByte(this.sealed ? (byte) 1 : (byte) 0);\n        dest.writeByte(this.active ? (byte) 1 : (byte) 0);\n        dest.writeInt(this.mode);\n        dest.writeLong(this.sizeBytes);\n        dest.writeString(this.appPackageName);\n        dest.writeParcelable(this.appIcon, flags);\n        if (appLabel != null) {\n            dest.writeString(appLabel.toString());\n        }\n    }\n\n    public SessionInfo() {\n    }\n\n    protected SessionInfo(Parcel in) {\n        this.sessionId = in.readInt();\n        this.installerPackageName = in.readString();\n        this.resolvedBaseCodePath = in.readString();\n        this.progress = in.readFloat();\n        this.sealed = in.readByte() != 0;\n        this.active = in.readByte() != 0;\n        this.mode = in.readInt();\n        this.sizeBytes = in.readLong();\n        this.appPackageName = in.readString();\n        this.appIcon = in.readParcelable(Bitmap.class.getClassLoader());\n        this.appLabel = in.readString();\n    }\n\n    public static final Parcelable.Creator<SessionInfo> CREATOR = new Parcelable.Creator<SessionInfo>() {\n        @Override\n        public SessionInfo createFromParcel(Parcel source) {\n            return new SessionInfo(source);\n        }\n\n        @Override\n        public SessionInfo[] newArray(int size) {\n            return new SessionInfo[size];\n        }\n    };\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/SessionParams.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\n\nimport android.annotation.TargetApi;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageInstaller;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport mirror.android.content.pm.PackageInstaller.SessionParamsLOLLIPOP;\nimport mirror.android.content.pm.PackageInstaller.SessionParamsMarshmallow;\n\n\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class SessionParams implements Parcelable {\n\n    public static final int MODE_INVALID = -1;\n\n    /**\n     * Mode for an install session whose staged APKs should fully replace any\n     * existing APKs for the target app.\n     */\n    public static final int MODE_FULL_INSTALL = 1;\n\n    /**\n     * Mode for an install session that should inherit any existing APKs for the\n     * target app, unless they have been explicitly overridden (based on split\n     * name) by the session. For example, this can be used to add one or more\n     * split APKs to an existing installation.\n     * <p>\n     * If there are no existing APKs for the target app, this behaves like\n     * {@link #MODE_FULL_INSTALL}.\n     */\n    public static final int MODE_INHERIT_EXISTING = 2;\n\n    public int mode = MODE_INVALID;\n    public int installFlags;\n    public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;\n    public long sizeBytes = -1;\n    public String appPackageName;\n    public Bitmap appIcon;\n    public String appLabel;\n    public long appIconLastModified = -1;\n    public Uri originatingUri;\n    public Uri referrerUri;\n    public String abiOverride;\n    public String volumeUuid;\n    public String[] grantedRuntimePermissions;\n\n    public SessionParams(int mode) {\n        this.mode = mode;\n    }\n\n\n    public PackageInstaller.SessionParams build() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(mode);\n            SessionParamsMarshmallow.installFlags.set(params, installFlags);\n            SessionParamsMarshmallow.installLocation.set(params, installLocation);\n            SessionParamsMarshmallow.sizeBytes.set(params, sizeBytes);\n            SessionParamsMarshmallow.appPackageName.set(params, appPackageName);\n            SessionParamsMarshmallow.appIcon.set(params, appIcon);\n            SessionParamsMarshmallow.appLabel.set(params, appLabel);\n            SessionParamsMarshmallow.appIconLastModified.set(params, appIconLastModified);\n            SessionParamsMarshmallow.originatingUri.set(params, originatingUri);\n            SessionParamsMarshmallow.referrerUri.set(params, referrerUri);\n            SessionParamsMarshmallow.abiOverride.set(params, abiOverride);\n            SessionParamsMarshmallow.volumeUuid.set(params, volumeUuid);\n            SessionParamsMarshmallow.grantedRuntimePermissions.set(params, grantedRuntimePermissions);\n            return params;\n        }\n        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(mode);\n        SessionParamsLOLLIPOP.installFlags.set(params, installFlags);\n        SessionParamsLOLLIPOP.installLocation.set(params, installLocation);\n        SessionParamsLOLLIPOP.sizeBytes.set(params, sizeBytes);\n        SessionParamsLOLLIPOP.appPackageName.set(params, appPackageName);\n        SessionParamsLOLLIPOP.appIcon.set(params, appIcon);\n        SessionParamsLOLLIPOP.appLabel.set(params, appLabel);\n        SessionParamsLOLLIPOP.appIconLastModified.set(params, appIconLastModified);\n        SessionParamsLOLLIPOP.originatingUri.set(params, originatingUri);\n        SessionParamsLOLLIPOP.referrerUri.set(params, referrerUri);\n        SessionParamsLOLLIPOP.abiOverride.set(params, abiOverride);\n        return params;\n    }\n\n    public static SessionParams create(PackageInstaller.SessionParams sessionParams) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            SessionParams params = new SessionParams(SessionParamsMarshmallow.mode.get(sessionParams));\n            params.installFlags = SessionParamsMarshmallow.installFlags.get(sessionParams);\n            params.installLocation = SessionParamsMarshmallow.installLocation.get(sessionParams);\n            params.sizeBytes = SessionParamsMarshmallow.sizeBytes.get(sessionParams);\n            params.appPackageName = SessionParamsMarshmallow.appPackageName.get(sessionParams);\n            params.appIcon = SessionParamsMarshmallow.appIcon.get(sessionParams);\n            params.appLabel = SessionParamsMarshmallow.appLabel.get(sessionParams);\n            params.appIconLastModified = SessionParamsMarshmallow.appIconLastModified.get(sessionParams);\n            params.originatingUri = SessionParamsMarshmallow.originatingUri.get(sessionParams);\n            params.referrerUri = SessionParamsMarshmallow.referrerUri.get(sessionParams);\n            params.abiOverride = SessionParamsMarshmallow.abiOverride.get(sessionParams);\n            params.volumeUuid = SessionParamsMarshmallow.volumeUuid.get(sessionParams);\n            params.grantedRuntimePermissions = SessionParamsMarshmallow.grantedRuntimePermissions.get(sessionParams);\n            return params;\n        }\n        SessionParams params = new SessionParams(SessionParamsLOLLIPOP.mode.get(sessionParams));\n        params.installFlags = SessionParamsLOLLIPOP.installFlags.get(sessionParams);\n        params.installLocation = SessionParamsLOLLIPOP.installLocation.get(sessionParams);\n        params.sizeBytes = SessionParamsLOLLIPOP.sizeBytes.get(sessionParams);\n        params.appPackageName = SessionParamsLOLLIPOP.appPackageName.get(sessionParams);\n        params.appIcon = SessionParamsLOLLIPOP.appIcon.get(sessionParams);\n        params.appLabel = SessionParamsLOLLIPOP.appLabel.get(sessionParams);\n        params.appIconLastModified = SessionParamsLOLLIPOP.appIconLastModified.get(sessionParams);\n        params.originatingUri = SessionParamsLOLLIPOP.originatingUri.get(sessionParams);\n        params.referrerUri = SessionParamsLOLLIPOP.referrerUri.get(sessionParams);\n        params.abiOverride = SessionParamsLOLLIPOP.abiOverride.get(sessionParams);\n        return params;\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(this.mode);\n        dest.writeInt(this.installFlags);\n        dest.writeInt(this.installLocation);\n        dest.writeLong(this.sizeBytes);\n        dest.writeString(this.appPackageName);\n        dest.writeParcelable(this.appIcon, flags);\n        dest.writeString(this.appLabel);\n        dest.writeLong(this.appIconLastModified);\n        dest.writeParcelable(this.originatingUri, flags);\n        dest.writeParcelable(this.referrerUri, flags);\n        dest.writeString(this.abiOverride);\n        dest.writeString(this.volumeUuid);\n        dest.writeStringArray(this.grantedRuntimePermissions);\n    }\n\n    protected SessionParams(Parcel in) {\n        this.mode = in.readInt();\n        this.installFlags = in.readInt();\n        this.installLocation = in.readInt();\n        this.sizeBytes = in.readLong();\n        this.appPackageName = in.readString();\n        this.appIcon = in.readParcelable(Bitmap.class.getClassLoader());\n        this.appLabel = in.readString();\n        this.appIconLastModified = in.readLong();\n        this.originatingUri = in.readParcelable(Uri.class.getClassLoader());\n        this.referrerUri = in.readParcelable(Uri.class.getClassLoader());\n        this.abiOverride = in.readString();\n        this.volumeUuid = in.readString();\n        this.grantedRuntimePermissions = in.createStringArray();\n    }\n\n    public static final Parcelable.Creator<SessionParams> CREATOR = new Parcelable.Creator<SessionParams>() {\n        @Override\n        public SessionParams createFromParcel(Parcel source) {\n            return new SessionParams(source);\n        }\n\n        @Override\n        public SessionParams[] newArray(int size) {\n            return new SessionParams[size];\n        }\n    };\n}"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/installer/VPackageInstallerService.java",
    "content": "package com.lody.virtual.server.pm.installer;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentSender;\nimport android.content.pm.IPackageInstallerCallback;\nimport android.content.pm.IPackageInstallerSession;\nimport android.content.pm.PackageInstaller;\nimport android.graphics.Bitmap;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.RemoteCallbackList;\nimport android.os.RemoteException;\nimport android.text.TextUtils;\nimport android.util.SparseArray;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.compat.ObjectsCompat;\nimport com.lody.virtual.helper.utils.Singleton;\nimport com.lody.virtual.os.VBinder;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.IPackageInstaller;\nimport com.lody.virtual.server.pm.VAppManagerService;\n\nimport java.io.IOException;\nimport java.security.SecureRandom;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\nimport static com.lody.virtual.server.pm.installer.PackageHelper.installStatusToPublicStatus;\nimport static com.lody.virtual.server.pm.installer.PackageHelper.installStatusToString;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class VPackageInstallerService extends IPackageInstaller.Stub {\n\n    private static final String TAG = \"PackageInstaller\";\n\n    /**\n     * Upper bound on number of active sessions for a UID\n     */\n    private static final long MAX_ACTIVE_SESSIONS = 1024;\n    private static final Singleton<VPackageInstallerService> gDefault = new Singleton<VPackageInstallerService>() {\n        @Override\n        protected VPackageInstallerService create() {\n            return new VPackageInstallerService();\n        }\n    };\n    /**\n     * Used for generating session IDs. Since this is created at boot time,\n     * normal random might be predictable.\n     */\n    private final Random mRandom = new SecureRandom();\n    private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();\n    private final Handler mInstallHandler;\n    private final Callbacks mCallbacks;\n    private final HandlerThread mInstallThread;\n    private final InternalCallback mInternalCallback = new InternalCallback();\n    private Context mContext;\n\n    private VPackageInstallerService() {\n        mContext = VirtualCore.get().getContext();\n        mInstallThread = new HandlerThread(\"PackageInstaller\");\n        mInstallThread.start();\n        mInstallHandler = new Handler(mInstallThread.getLooper());\n        mCallbacks = new Callbacks(mInstallThread.getLooper());\n    }\n\n    public static VPackageInstallerService get() {\n        return gDefault.get();\n    }\n\n    private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,\n                                       int installerUid) {\n        int count = 0;\n        final int size = sessions.size();\n        for (int i = 0; i < size; i++) {\n            final PackageInstallerSession session = sessions.valueAt(i);\n            if (session.installerUid == installerUid) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    @Override\n    public int createSession(SessionParams params, String installerPackageName, int userId) throws RemoteException {\n        try {\n            return createSessionInternal(params, installerPackageName, userId);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    private int createSessionInternal(SessionParams params, String installerPackageName, int userId)\n            throws IOException {\n        final int callingUid = VBinder.getCallingUid();\n        final int sessionId;\n        final PackageInstallerSession session;\n        synchronized (mSessions) {\n            // Sanity check that installer isn't going crazy\n            final int activeCount = getSessionCount(mSessions, callingUid);\n            if (activeCount >= MAX_ACTIVE_SESSIONS) {\n                throw new IllegalStateException(\n                        \"Too many active sessions for UID \" + callingUid);\n            }\n            sessionId = allocateSessionIdLocked();\n            session = new PackageInstallerSession(mInternalCallback, mContext, mInstallHandler.getLooper(), installerPackageName, sessionId, userId, callingUid, params, VEnvironment.getPackageInstallerStageDir());\n        }\n        mCallbacks.notifySessionCreated(session.sessionId, session.userId);\n        return sessionId;\n    }\n\n    @Override\n    public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {\n        synchronized (mSessions) {\n            final PackageInstallerSession session = mSessions.get(sessionId);\n            if (session == null || !isCallingUidOwner(session)) {\n                throw new SecurityException(\"Caller has no access to session \" + sessionId);\n            }\n\n            session.params.appIcon = appIcon;\n            session.params.appIconLastModified = -1;\n\n            mInternalCallback.onSessionBadgingChanged(session);\n        }\n    }\n\n    @Override\n    public void updateSessionAppLabel(int sessionId, String appLabel) throws RemoteException {\n        synchronized (mSessions) {\n            final PackageInstallerSession session = mSessions.get(sessionId);\n            if (session == null || !isCallingUidOwner(session)) {\n                throw new SecurityException(\"Caller has no access to session \" + sessionId);\n            }\n            session.params.appLabel = appLabel;\n            mInternalCallback.onSessionBadgingChanged(session);\n        }\n    }\n\n    @Override\n    public void abandonSession(int sessionId) throws RemoteException {\n        synchronized (mSessions) {\n            final PackageInstallerSession session = mSessions.get(sessionId);\n            if (session == null || !isCallingUidOwner(session)) {\n                throw new SecurityException(\"Caller has no access to session \" + sessionId);\n            }\n            session.abandon();\n        }\n    }\n\n    @Override\n    public IPackageInstallerSession openSession(int sessionId) throws RemoteException {\n        try {\n            return openSessionInternal(sessionId);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {\n        synchronized (mSessions) {\n            final PackageInstallerSession session = mSessions.get(sessionId);\n            if (session == null || !isCallingUidOwner(session)) {\n                throw new SecurityException(\"Caller has no access to session \" + sessionId);\n            }\n            session.open();\n            return session;\n        }\n    }\n\n    @Override\n    public SessionInfo getSessionInfo(int sessionId) throws RemoteException {\n        synchronized (mSessions) {\n            final PackageInstallerSession session = mSessions.get(sessionId);\n            return session != null ? session.generateInfo() : null;\n        }\n    }\n\n    @Override\n    public VParceledListSlice getAllSessions(int userId) throws RemoteException {\n        final List<SessionInfo> result = new ArrayList<>();\n        synchronized (mSessions) {\n            for (int i = 0; i < mSessions.size(); i++) {\n                final PackageInstallerSession session = mSessions.valueAt(i);\n                if (session.userId == userId) {\n                    result.add(session.generateInfo());\n                }\n            }\n        }\n        return new VParceledListSlice<>(result);\n    }\n\n    @Override\n    public VParceledListSlice getMySessions(String installerPackageName, int userId) throws RemoteException {\n        final List<SessionInfo> result = new ArrayList<>();\n        synchronized (mSessions) {\n            for (int i = 0; i < mSessions.size(); i++) {\n                final PackageInstallerSession session = mSessions.valueAt(i);\n                if (ObjectsCompat.equals(session.installerPackageName, installerPackageName)\n                        && session.userId == userId) {\n                    result.add(session.generateInfo());\n                }\n            }\n        }\n        return new VParceledListSlice<>(result);\n    }\n\n    @Override\n    public void registerCallback(IPackageInstallerCallback callback, int userId) throws RemoteException {\n        mCallbacks.register(callback, userId);\n    }\n\n    @Override\n    public void unregisterCallback(IPackageInstallerCallback callback) throws RemoteException {\n        mCallbacks.unregister(callback);\n    }\n\n    @Override\n    public void uninstall(String packageName, String callerPackageName, int flags, IntentSender statusReceiver, int userId) throws RemoteException {\n        boolean success = VAppManagerService.get().uninstallPackage(packageName);\n        if (statusReceiver != null) {\n            final Intent fillIn = new Intent();\n            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);\n            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, success ? PackageInstaller.STATUS_SUCCESS : PackageInstaller.STATUS_FAILURE);\n            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, PackageHelper.deleteStatusToString(success));\n            fillIn.putExtra(\"android.content.pm.extra.LEGACY_STATUS\", success ? 1 : -1);\n            try {\n                statusReceiver.sendIntent(mContext, 0, fillIn, null, null);\n            } catch (IntentSender.SendIntentException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    @Override\n    public void setPermissionsResult(int sessionId, boolean accepted) throws RemoteException {\n        synchronized (mSessions) {\n            PackageInstallerSession session = mSessions.get(sessionId);\n            if (session != null) {\n                session.setPermissionsResult(accepted);\n            }\n        }\n    }\n\n    private boolean isCallingUidOwner(PackageInstallerSession session) {\n        return true;\n    }\n\n    private int allocateSessionIdLocked() {\n        int n = 0;\n        int sessionId;\n        do {\n            sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;\n            if (mSessions.get(sessionId) == null) {\n                return sessionId;\n            }\n        } while (n++ < 32);\n\n        throw new IllegalStateException(\"Failed to allocate session ID\");\n    }\n\n    private static class Callbacks extends Handler {\n        private static final int MSG_SESSION_CREATED = 1;\n        private static final int MSG_SESSION_BADGING_CHANGED = 2;\n        private static final int MSG_SESSION_ACTIVE_CHANGED = 3;\n        private static final int MSG_SESSION_PROGRESS_CHANGED = 4;\n        private static final int MSG_SESSION_FINISHED = 5;\n\n        private final RemoteCallbackList<IPackageInstallerCallback>\n                mCallbacks = new RemoteCallbackList<>();\n\n        public Callbacks(Looper looper) {\n            super(looper);\n        }\n\n        public void register(IPackageInstallerCallback callback, int userId) {\n            mCallbacks.register(callback, new VUserHandle(userId));\n        }\n\n        public void unregister(IPackageInstallerCallback callback) {\n            mCallbacks.unregister(callback);\n        }\n\n        @Override\n        public void handleMessage(Message msg) {\n            final int userId = msg.arg2;\n            final int n = mCallbacks.beginBroadcast();\n            for (int i = 0; i < n; i++) {\n                final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);\n                final VUserHandle user = (VUserHandle) mCallbacks.getBroadcastCookie(i);\n                // TODO: dispatch notifications for slave profiles\n                if (userId == user.getIdentifier()) {\n                    try {\n                        invokeCallback(callback, msg);\n                    } catch (RemoteException ignored) {\n                    }\n                }\n            }\n            mCallbacks.finishBroadcast();\n        }\n\n        private void invokeCallback(IPackageInstallerCallback callback, Message msg)\n                throws RemoteException {\n            final int sessionId = msg.arg1;\n            switch (msg.what) {\n                case MSG_SESSION_CREATED:\n                    callback.onSessionCreated(sessionId);\n                    break;\n                case MSG_SESSION_BADGING_CHANGED:\n                    callback.onSessionBadgingChanged(sessionId);\n                    break;\n                case MSG_SESSION_ACTIVE_CHANGED:\n                    callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);\n                    break;\n                case MSG_SESSION_PROGRESS_CHANGED:\n                    callback.onSessionProgressChanged(sessionId, (float) msg.obj);\n                    break;\n                case MSG_SESSION_FINISHED:\n                    callback.onSessionFinished(sessionId, (boolean) msg.obj);\n                    break;\n            }\n        }\n\n        private void notifySessionCreated(int sessionId, int userId) {\n            obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();\n        }\n\n        private void notifySessionBadgingChanged(int sessionId, int userId) {\n            obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();\n        }\n\n        private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {\n            obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();\n        }\n\n        private void notifySessionProgressChanged(int sessionId, int userId, float progress) {\n            obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();\n        }\n\n        public void notifySessionFinished(int sessionId, int userId, boolean success) {\n            obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();\n        }\n    }\n\n    static class PackageInstallObserverAdapter extends PackageInstallObserver {\n        private final Context mContext;\n        private final IntentSender mTarget;\n        private final int mSessionId;\n        private final int mUserId;\n\n        PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId, int userId) {\n            mContext = context;\n            mTarget = target;\n            mSessionId = sessionId;\n            mUserId = userId;\n        }\n\n        @Override\n        public void onUserActionRequired(Intent intent) {\n            final Intent fillIn = new Intent();\n            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);\n            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,\n                    PackageInstaller.STATUS_PENDING_USER_ACTION);\n            fillIn.putExtra(Intent.EXTRA_INTENT, intent);\n            try {\n                mTarget.sendIntent(mContext, 0, fillIn, null, null);\n            } catch (IntentSender.SendIntentException ignored) {\n            }\n        }\n\n        @Override\n        public void onPackageInstalled(String basePackageName, int returnCode, String msg,\n                                       Bundle extras) {\n            final Intent fillIn = new Intent();\n            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);\n            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);\n            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,\n                    installStatusToPublicStatus(returnCode));\n            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,\n                    installStatusToString(returnCode, msg));\n            fillIn.putExtra(\"android.content.pm.extra.LEGACY_STATUS\", returnCode);\n            if (extras != null) {\n                final String existing = extras.getString(\"android.content.pm.extra.FAILURE_EXISTING_PACKAGE\");\n                if (!TextUtils.isEmpty(existing)) {\n                    fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);\n                }\n            }\n            try {\n                mTarget.sendIntent(mContext, 0, fillIn, null, null);\n            } catch (IntentSender.SendIntentException ignored) {\n            }\n        }\n    }\n\n    class InternalCallback {\n        public void onSessionBadgingChanged(PackageInstallerSession session) {\n            mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);\n        }\n\n        public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {\n            mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);\n        }\n\n        public void onSessionProgressChanged(PackageInstallerSession session, float progress) {\n            mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);\n        }\n\n        public void onSessionFinished(final PackageInstallerSession session, boolean success) {\n            mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);\n\n            mInstallHandler.post(new Runnable() {\n                @Override\n                public void run() {\n                    synchronized (mSessions) {\n                        mSessions.remove(session.sessionId);\n                    }\n                }\n            });\n        }\n\n        public void onSessionPrepared(PackageInstallerSession session) {\n            // We prepared the destination to write into; we want to persist\n            // this, but it's not critical enough to block for.\n        }\n\n        public void onSessionSealedBlocking(PackageInstallerSession session) {\n            // It's very important that we block until we've recorded the\n            // session as being sealed, since we never want to allow mutation\n            // after sealing.\n        }\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/parser/PackageParserEx.java",
    "content": "package com.lody.virtual.server.pm.parser;\n\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ConfigurationInfo;\nimport android.content.pm.FeatureInfo;\nimport android.content.pm.InstrumentationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.PackageParser;\nimport android.content.pm.PermissionGroupInfo;\nimport android.content.pm.PermissionInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.pm.Signature;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.text.TextUtils;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.fixer.ComponentFixer;\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.helper.compat.PackageParserCompat;\nimport com.lody.virtual.helper.utils.FileUtils;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.os.VEnvironment;\nimport com.lody.virtual.server.pm.PackageSetting;\nimport com.lody.virtual.server.pm.PackageUserState;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mirror.android.content.pm.ApplicationInfoL;\nimport mirror.android.content.pm.ApplicationInfoN;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserEx {\n\n    private static final String TAG = PackageParserEx.class.getSimpleName();\n\n    private static final ArrayMap<String, String[]> sSharedLibCache = new ArrayMap<>();\n\n    // 解析包结构\n    public static VPackage parsePackage(File packageFile) throws Throwable {\n        PackageParser parser = PackageParserCompat.createParser(packageFile);\n        // 调用对应系统版本的 parsePackage 方法\n        PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);\n        // 包含此信息代表其是 debug 签名或者其他签名\n        if (p.requestedPermissions.contains(\"android.permission.FAKE_PACKAGE_SIGNATURE\")\n                && p.mAppMetaData != null\n                && p.mAppMetaData.containsKey(\"fake-signature\")) {\n            String sig = p.mAppMetaData.getString(\"fake-signature\");\n            p.mSignatures = new Signature[]{new Signature(sig)};\n            VLog.d(TAG, \"Using fake-signature feature on : \" + p.packageName);\n        } else {\n            // 验证签名\n            PackageParserCompat.collectCertificates(parser, p, PackageParser.PARSE_IS_SYSTEM);\n        }\n        // 转换成可以序列化在磁盘上的 Cache\n        return buildPackageCache(p);\n    }\n\n    public static VPackage readPackageCache(String packageName) {\n        Parcel p = Parcel.obtain();\n        try {\n            File cacheFile = VEnvironment.getPackageCacheFile(packageName);\n            FileInputStream is = new FileInputStream(cacheFile);\n            byte[] bytes = FileUtils.toByteArray(is);\n            is.close();\n            p.unmarshall(bytes, 0, bytes.length);\n            p.setDataPosition(0);\n            if (p.readInt() != 4) {\n                throw new IllegalStateException(\"Invalid version.\");\n            }\n            VPackage pkg = new VPackage(p);\n            addOwner(pkg);\n            return pkg;\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            p.recycle();\n        }\n        return null;\n    }\n\n    public static void readSignature(VPackage pkg) {\n        File signatureFile = VEnvironment.getSignatureFile(pkg.packageName);\n        if (!signatureFile.exists()) {\n            return;\n        }\n        Parcel p = Parcel.obtain();\n        try {\n            FileInputStream fis = new FileInputStream(signatureFile);\n            byte[] bytes = FileUtils.toByteArray(fis);\n            fis.close();\n            p.unmarshall(bytes, 0, bytes.length);\n            p.setDataPosition(0);\n            pkg.mSignatures = p.createTypedArray(Signature.CREATOR);\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            p.recycle();\n        }\n    }\n\n    public static void savePackageCache(VPackage pkg) {\n        final String packageName = pkg.packageName;\n        Parcel p = Parcel.obtain();\n        try {\n            p.writeInt(4);\n            pkg.writeToParcel(p, 0);\n            FileOutputStream fos = new FileOutputStream(VEnvironment.getPackageCacheFile(packageName));\n            fos.write(p.marshall());\n            fos.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            p.recycle();\n        }\n        Signature[] signatures = pkg.mSignatures;\n        if (signatures != null) {\n            File signatureFile = VEnvironment.getSignatureFile(packageName);\n            if (signatureFile.exists() && !signatureFile.delete()) {\n                VLog.w(TAG, \"Unable to delete the signatures of \" + packageName);\n            }\n            p = Parcel.obtain();\n            try {\n                p.writeTypedArray(signatures, 0);\n                FileUtils.writeParcelToFile(p, signatureFile);\n            } catch (IOException e) {\n                e.printStackTrace();\n            } finally {\n                p.recycle();\n            }\n        }\n    }\n\n    private static VPackage buildPackageCache(PackageParser.Package p) {\n        // VPackage 是可序列化的\n        VPackage cache = new VPackage();\n        cache.activities = new ArrayList<>(p.activities.size());\n        cache.services = new ArrayList<>(p.services.size());\n        cache.receivers = new ArrayList<>(p.receivers.size());\n        cache.providers = new ArrayList<>(p.providers.size());\n        cache.instrumentation = new ArrayList<>(p.instrumentation.size());\n        cache.permissions = new ArrayList<>(p.permissions.size());\n        cache.permissionGroups = new ArrayList<>(p.permissionGroups.size());\n\n        for (PackageParser.Activity activity : p.activities) {\n            cache.activities.add(new VPackage.ActivityComponent(activity));\n        }\n        for (PackageParser.Service service : p.services) {\n            cache.services.add(new VPackage.ServiceComponent(service));\n        }\n        for (PackageParser.Activity receiver : p.receivers) {\n            cache.receivers.add(new VPackage.ActivityComponent(receiver));\n        }\n        for (PackageParser.Provider provider : p.providers) {\n            cache.providers.add(new VPackage.ProviderComponent(provider));\n        }\n        for (PackageParser.Instrumentation instrumentation : p.instrumentation) {\n            cache.instrumentation.add(new VPackage.InstrumentationComponent(instrumentation));\n        }\n        cache.requestedPermissions = new ArrayList<>(p.requestedPermissions.size());\n        cache.requestedPermissions.addAll(p.requestedPermissions);\n        if (mirror.android.content.pm.PackageParser.Package.protectedBroadcasts != null) {\n            List<String> protectedBroadcasts = mirror.android.content.pm.PackageParser.Package.protectedBroadcasts.get(p);\n            if (protectedBroadcasts != null) {\n                cache.protectedBroadcasts = new ArrayList<>(protectedBroadcasts);\n                cache.protectedBroadcasts.addAll(protectedBroadcasts);\n            }\n        }\n        cache.applicationInfo = p.applicationInfo;\n        cache.mSignatures = p.mSignatures;\n        cache.mAppMetaData = p.mAppMetaData;\n        cache.packageName = p.packageName;\n        cache.mPreferredOrder = p.mPreferredOrder;\n        cache.mVersionName = p.mVersionName;\n        cache.mSharedUserId = p.mSharedUserId;\n        cache.mSharedUserLabel = p.mSharedUserLabel;\n        cache.usesLibraries = p.usesLibraries;\n        cache.mVersionCode = p.mVersionCode;\n        cache.mAppMetaData = p.mAppMetaData;\n        cache.configPreferences = p.configPreferences;\n        cache.reqFeatures = p.reqFeatures;\n        // 给每个组件添加 package\n        addOwner(cache);\n        return cache;\n    }\n\n    public static void initApplicationInfoBase(PackageSetting ps, VPackage p) {\n        ApplicationInfo ai = p.applicationInfo;\n        ai.flags |= ApplicationInfo.FLAG_HAS_CODE;\n        if (TextUtils.isEmpty(ai.processName)) {\n            ai.processName = ai.packageName;\n        }\n        ai.enabled = true;\n        ai.nativeLibraryDir = ps.libPath;\n        ai.uid = ps.appId;\n        ai.name = ComponentFixer.fixComponentClassName(ps.packageName, ai.name);\n        ai.publicSourceDir = ps.apkPath;\n        ai.sourceDir = ps.apkPath;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            ai.splitSourceDirs = new String[]{ps.apkPath};\n            ai.splitPublicSourceDirs = ai.splitSourceDirs;\n            ApplicationInfoL.scanSourceDir.set(ai, ai.dataDir);\n            ApplicationInfoL.scanPublicSourceDir.set(ai, ai.dataDir);\n            String hostPrimaryCpuAbi = ApplicationInfoL.primaryCpuAbi.get(VirtualCore.get().getContext().getApplicationInfo());\n            ApplicationInfoL.primaryCpuAbi.set(ai, hostPrimaryCpuAbi);\n        }\n\n        if (ps.dependSystem) {\n            String[] sharedLibraryFiles = sSharedLibCache.get(ps.packageName);\n            if (sharedLibraryFiles == null) {\n                PackageManager hostPM = VirtualCore.get().getUnHookPackageManager();\n                try {\n                    ApplicationInfo hostInfo = hostPM.getApplicationInfo(ps.packageName, PackageManager.GET_SHARED_LIBRARY_FILES);\n                    sharedLibraryFiles = hostInfo.sharedLibraryFiles;\n                    if (sharedLibraryFiles == null) sharedLibraryFiles = new String[0];\n                    sSharedLibCache.put(ps.packageName, sharedLibraryFiles);\n                } catch (PackageManager.NameNotFoundException e) {\n                    e.printStackTrace();\n                }\n            }\n            ai.sharedLibraryFiles = sharedLibraryFiles;\n        }\n    }\n\n    private static void initApplicationAsUser(ApplicationInfo ai, int userId) {\n        ai.dataDir = VEnvironment.getDataUserPackageDirectory(userId, ai.packageName).getPath();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            ApplicationInfoL.scanSourceDir.set(ai, ai.dataDir);\n            ApplicationInfoL.scanPublicSourceDir.set(ai, ai.dataDir);\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            ApplicationInfoN.deviceEncryptedDataDir.set(ai, ai.dataDir);\n            ApplicationInfoN.deviceProtectedDataDir.set(ai, ai.dataDir);\n            ApplicationInfoN.credentialEncryptedDataDir.set(ai, ai.dataDir);\n            ApplicationInfoN.credentialProtectedDataDir.set(ai, ai.dataDir);\n        }\n    }\n\n    private static void addOwner(VPackage p) {\n        for (VPackage.ActivityComponent activity : p.activities) {\n            activity.owner = p;\n            for (VPackage.ActivityIntentInfo info : activity.intents) {\n                info.activity = activity;\n            }\n        }\n        for (VPackage.ServiceComponent service : p.services) {\n            service.owner = p;\n            for (VPackage.ServiceIntentInfo info : service.intents) {\n                info.service = service;\n            }\n        }\n        for (VPackage.ActivityComponent receiver : p.receivers) {\n            receiver.owner = p;\n            for (VPackage.ActivityIntentInfo info : receiver.intents) {\n                info.activity = receiver;\n            }\n        }\n        for (VPackage.ProviderComponent provider : p.providers) {\n            provider.owner = p;\n            for (VPackage.ProviderIntentInfo info : provider.intents) {\n                info.provider = provider;\n            }\n        }\n        for (VPackage.InstrumentationComponent instrumentation : p.instrumentation) {\n            instrumentation.owner = p;\n        }\n        for (VPackage.PermissionComponent permission : p.permissions) {\n            permission.owner = p;\n        }\n        for (VPackage.PermissionGroupComponent group : p.permissionGroups) {\n            group.owner = p;\n        }\n    }\n\n    public static PackageInfo generatePackageInfo(VPackage p, int flags, long firstInstallTime, long lastUpdateTime, PackageUserState state, int userId) {\n        if (!checkUseInstalledOrHidden(state, flags)) {\n            return null;\n        }\n        if (p.mSignatures == null) {\n            readSignature(p);\n        }\n        PackageInfo pi = new PackageInfo();\n        pi.packageName = p.packageName;\n        pi.versionCode = p.mVersionCode;\n        pi.sharedUserLabel = p.mSharedUserLabel;\n        pi.versionName = p.mVersionName;\n        pi.sharedUserId = p.mSharedUserId;\n        pi.sharedUserLabel = p.mSharedUserLabel;\n        pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);\n        pi.firstInstallTime = firstInstallTime;\n        pi.lastUpdateTime = lastUpdateTime;\n        if (p.requestedPermissions != null && !p.requestedPermissions.isEmpty()) {\n            String[] requestedPermissions = new String[p.requestedPermissions.size()];\n            p.requestedPermissions.toArray(requestedPermissions);\n            pi.requestedPermissions = requestedPermissions;\n        }\n        if ((flags & PackageManager.GET_GIDS) != 0) {\n            pi.gids = PackageParserCompat.GIDS;\n        }\n        if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {\n            int N = p.configPreferences != null ? p.configPreferences.size() : 0;\n            if (N > 0) {\n                pi.configPreferences = new ConfigurationInfo[N];\n                p.configPreferences.toArray(pi.configPreferences);\n            }\n            N = p.reqFeatures != null ? p.reqFeatures.size() : 0;\n            if (N > 0) {\n                pi.reqFeatures = new FeatureInfo[N];\n                p.reqFeatures.toArray(pi.reqFeatures);\n            }\n        }\n        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {\n            final int N = p.activities.size();\n            if (N > 0) {\n                int num = 0;\n                final ActivityInfo[] res = new ActivityInfo[N];\n                for (int i = 0; i < N; i++) {\n                    final VPackage.ActivityComponent a = p.activities.get(i);\n                    res[num++] = generateActivityInfo(a, flags, state, userId);\n                }\n                pi.activities = res;\n            }\n        }\n        if ((flags & PackageManager.GET_RECEIVERS) != 0) {\n            final int N = p.receivers.size();\n            if (N > 0) {\n                int num = 0;\n                final ActivityInfo[] res = new ActivityInfo[N];\n                for (int i = 0; i < N; i++) {\n                    final VPackage.ActivityComponent a = p.receivers.get(i);\n                    res[num++] = generateActivityInfo(a, flags, state, userId);\n                }\n                pi.receivers = res;\n            }\n        }\n        if ((flags & PackageManager.GET_SERVICES) != 0) {\n            final int N = p.services.size();\n            if (N > 0) {\n                int num = 0;\n                final ServiceInfo[] res = new ServiceInfo[N];\n                for (int i = 0; i < N; i++) {\n                    final VPackage.ServiceComponent s = p.services.get(i);\n                    res[num++] = generateServiceInfo(s, flags, state, userId);\n                }\n                pi.services = res;\n            }\n        }\n        if ((flags & PackageManager.GET_PROVIDERS) != 0) {\n            final int N = p.providers.size();\n            if (N > 0) {\n                int num = 0;\n                final ProviderInfo[] res = new ProviderInfo[N];\n                for (int i = 0; i < N; i++) {\n                    final VPackage.ProviderComponent pr = p.providers.get(i);\n                    res[num++] = generateProviderInfo(pr, flags, state, userId);\n                }\n                pi.providers = res;\n            }\n        }\n        if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {\n            int N = p.instrumentation.size();\n            if (N > 0) {\n                pi.instrumentation = new InstrumentationInfo[N];\n                for (int i = 0; i < N; i++) {\n                    pi.instrumentation[i] = generateInstrumentationInfo(\n                            p.instrumentation.get(i), flags);\n                }\n            }\n        }\n        if ((flags & PackageManager.GET_SIGNATURES) != 0) {\n            int N = (p.mSignatures != null) ? p.mSignatures.length : 0;\n            if (N > 0) {\n                pi.signatures = new Signature[N];\n                System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);\n            }\n        }\n        return pi;\n    }\n\n    public static ApplicationInfo generateApplicationInfo(VPackage p, int flags,\n                                                          PackageUserState state, int userId) {\n        if (p == null) return null;\n        if (!checkUseInstalledOrHidden(state, flags)) {\n            return null;\n        }\n\n        // Make shallow copy so we can store the metadata/libraries safely\n        ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);\n        if ((flags & PackageManager.GET_META_DATA) != 0) {\n            ai.metaData = p.mAppMetaData;\n        }\n        initApplicationAsUser(ai, userId);\n        return ai;\n    }\n\n\n    public static ActivityInfo generateActivityInfo(VPackage.ActivityComponent a, int flags,\n                                                    PackageUserState state, int userId) {\n        if (a == null) return null;\n        if (!checkUseInstalledOrHidden(state, flags)) {\n            return null;\n        }\n        // Make shallow copies so we can store the metadata safely\n        ActivityInfo ai = new ActivityInfo(a.info);\n        if ((flags & PackageManager.GET_META_DATA) != 0\n                && (a.metaData != null)) {\n            ai.metaData = a.metaData;\n        }\n        ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);\n        return ai;\n    }\n\n    public static ServiceInfo generateServiceInfo(VPackage.ServiceComponent s, int flags,\n                                                  PackageUserState state, int userId) {\n        if (s == null) return null;\n        if (!checkUseInstalledOrHidden(state, flags)) {\n            return null;\n        }\n        ServiceInfo si = new ServiceInfo(s.info);\n        // Make shallow copies so we can store the metadata safely\n        if ((flags & PackageManager.GET_META_DATA) != 0 && s.metaData != null) {\n            si.metaData = s.metaData;\n        }\n        si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);\n        return si;\n    }\n\n    public static ProviderInfo generateProviderInfo(VPackage.ProviderComponent p, int flags,\n                                                    PackageUserState state, int userId) {\n        if (p == null) return null;\n        if (!checkUseInstalledOrHidden(state, flags)) {\n            return null;\n        }\n        // Make shallow copies so we can store the metadata safely\n        ProviderInfo pi = new ProviderInfo(p.info);\n        if ((flags & PackageManager.GET_META_DATA) != 0\n                && (p.metaData != null)) {\n            pi.metaData = p.metaData;\n        }\n\n        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {\n            pi.uriPermissionPatterns = null;\n        }\n        pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);\n        return pi;\n    }\n\n    public static InstrumentationInfo generateInstrumentationInfo(\n            VPackage.InstrumentationComponent i, int flags) {\n        if (i == null) return null;\n        if ((flags & PackageManager.GET_META_DATA) == 0) {\n            return i.info;\n        }\n        InstrumentationInfo ii = new InstrumentationInfo(i.info);\n        ii.metaData = i.metaData;\n        return ii;\n    }\n\n    public static PermissionInfo generatePermissionInfo(\n            VPackage.PermissionComponent p, int flags) {\n        if (p == null) return null;\n        if ((flags & PackageManager.GET_META_DATA) == 0) {\n            return p.info;\n        }\n        PermissionInfo pi = new PermissionInfo(p.info);\n        pi.metaData = p.metaData;\n        return pi;\n    }\n\n    public static PermissionGroupInfo generatePermissionGroupInfo(\n            VPackage.PermissionGroupComponent pg, int flags) {\n        if (pg == null) return null;\n        if ((flags & PackageManager.GET_META_DATA) == 0) {\n            return pg.info;\n        }\n        PermissionGroupInfo pgi = new PermissionGroupInfo(pg.info);\n        pgi.metaData = pg.metaData;\n        return pgi;\n    }\n\n    private static boolean checkUseInstalledOrHidden(PackageUserState state, int flags) {\n        //noinspection deprecation\n        return (state.installed && !state.hidden)\n                || (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/pm/parser/VPackage.java",
    "content": "package com.lody.virtual.server.pm.parser;\n\nimport android.content.ComponentName;\nimport android.content.IntentFilter;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ConfigurationInfo;\nimport android.content.pm.FeatureInfo;\nimport android.content.pm.InstrumentationInfo;\nimport android.content.pm.PackageParser;\nimport android.content.pm.PermissionGroupInfo;\nimport android.content.pm.PermissionInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.pm.Signature;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport java.util.ArrayList;\n\n/**\n * @author Lody\n */\n\npublic class VPackage implements Parcelable {\n\n        public static final Creator<VPackage> CREATOR = new Creator<VPackage>() {\n            @Override\n            public VPackage createFromParcel(Parcel source) {\n                return new VPackage(source);\n            }\n\n            @Override\n            public VPackage[] newArray(int size) {\n                return new VPackage[size];\n            }\n        };\n        public ArrayList<ActivityComponent> activities;\n        public ArrayList<ActivityComponent> receivers;\n        public ArrayList<ProviderComponent> providers;\n        public ArrayList<ServiceComponent> services;\n        public ArrayList<InstrumentationComponent> instrumentation;\n        public ArrayList<PermissionComponent> permissions;\n        public ArrayList<PermissionGroupComponent> permissionGroups;\n        public ArrayList<String> requestedPermissions;\n        public ArrayList<String> protectedBroadcasts;\n        public ApplicationInfo applicationInfo;\n        public Signature[] mSignatures;\n        public Bundle mAppMetaData;\n        public String packageName;\n        public int mPreferredOrder;\n        public String mVersionName;\n        public String mSharedUserId;\n        public ArrayList<String> usesLibraries;\n        public int mVersionCode;\n        public int mSharedUserLabel;\n        // Applications hardware preferences\n        public ArrayList<ConfigurationInfo> configPreferences = null;\n        // Applications requested features\n        public ArrayList<FeatureInfo> reqFeatures = null;\n        public Object mExtras;\n\n    public VPackage() {\n    }\n\n    protected VPackage(Parcel in) {\n        int N = in.readInt();\n        this.activities = new ArrayList<>(N);\n        while (N-- > 0) {\n            activities.add(new ActivityComponent(in));\n        }\n        N = in.readInt();\n        this.receivers = new ArrayList<>(N);\n        while (N-- > 0) {\n            receivers.add(new ActivityComponent(in));\n        }\n        N = in.readInt();\n        this.providers = new ArrayList<>(N);\n        while (N-- > 0) {\n            providers.add(new ProviderComponent(in));\n        }\n        N = in.readInt();\n        this.services = new ArrayList<>(N);\n        while (N-- > 0) {\n            services.add(new ServiceComponent(in));\n        }\n        N = in.readInt();\n        this.instrumentation = new ArrayList<>(N);\n        while (N-- > 0) {\n            instrumentation.add(new InstrumentationComponent(in));\n        }\n        N = in.readInt();\n        this.permissions = new ArrayList<>(N);\n        while (N-- > 0) {\n            permissions.add(new PermissionComponent(in));\n        }\n        N = in.readInt();\n        this.permissionGroups = new ArrayList<>(N);\n        while (N-- > 0) {\n            permissionGroups.add(new PermissionGroupComponent(in));\n        }\n        this.requestedPermissions = in.createStringArrayList();\n        this.protectedBroadcasts = in.createStringArrayList();\n        this.applicationInfo = in.readParcelable(ApplicationInfo.class.getClassLoader());\n        this.mAppMetaData = in.readBundle(Bundle.class.getClassLoader());\n        this.packageName = in.readString();\n        this.mPreferredOrder = in.readInt();\n        this.mVersionName = in.readString();\n        this.mSharedUserId = in.readString();\n        this.usesLibraries = in.createStringArrayList();\n        this.mVersionCode = in.readInt();\n        this.mSharedUserLabel = in.readInt();\n        this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);\n        this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(this.activities.size());\n        for (ActivityComponent component : activities) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (ActivityIntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeInt(this.receivers.size());\n        for (ActivityComponent component : receivers) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (ActivityIntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeInt(this.providers.size());\n        for (ProviderComponent component : providers) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (ProviderIntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeInt(this.services.size());\n        for (ServiceComponent component : services) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (ServiceIntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeInt(this.instrumentation.size());\n        for (InstrumentationComponent component : instrumentation) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (IntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeInt(this.permissions.size());\n        for (PermissionComponent component : permissions) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (IntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeInt(this.permissionGroups.size());\n        for (PermissionGroupComponent component : permissionGroups) {\n            dest.writeParcelable(component.info, 0);\n            dest.writeString(component.className);\n            dest.writeBundle(component.metaData);\n            dest.writeInt(component.intents != null ? component.intents.size() : 0);\n            if (component.intents != null) {\n                for (IntentInfo info : component.intents) {\n                    info.writeToParcel(dest, flags);\n                }\n            }\n        }\n        dest.writeStringList(this.requestedPermissions);\n        dest.writeStringList(this.protectedBroadcasts);\n        dest.writeParcelable(this.applicationInfo, flags);\n        dest.writeBundle(this.mAppMetaData);\n        dest.writeString(this.packageName);\n        dest.writeInt(this.mPreferredOrder);\n        dest.writeString(this.mVersionName);\n        dest.writeString(this.mSharedUserId);\n        dest.writeStringList(this.usesLibraries);\n        dest.writeInt(this.mVersionCode);\n        dest.writeInt(this.mSharedUserLabel);\n        dest.writeTypedList(this.configPreferences);\n        dest.writeTypedList(this.reqFeatures);\n    }\n\n    public static class ActivityIntentInfo extends IntentInfo {\n\n        public ActivityComponent activity;\n\n        public ActivityIntentInfo(PackageParser.IntentInfo info) {\n            super(info);\n        }\n\n        protected ActivityIntentInfo(Parcel in) {\n            super(in);\n        }\n    }\n\n    public static class ServiceIntentInfo extends IntentInfo {\n        public ServiceComponent service;\n\n        public ServiceIntentInfo(PackageParser.IntentInfo info) {\n            super(info);\n        }\n\n        protected ServiceIntentInfo(Parcel in) {\n            super(in);\n        }\n    }\n\n    public static class ProviderIntentInfo extends IntentInfo {\n        public ProviderComponent provider;\n\n        public ProviderIntentInfo(PackageParser.IntentInfo info) {\n            super(info);\n        }\n\n        protected ProviderIntentInfo(Parcel in) {\n            super(in);\n        }\n    }\n\n    public static class IntentInfo implements Parcelable {\n        public static final Creator<IntentInfo> CREATOR = new Creator<IntentInfo>() {\n            @Override\n            public IntentInfo createFromParcel(Parcel source) {\n                return new IntentInfo(source);\n            }\n\n            @Override\n            public IntentInfo[] newArray(int size) {\n                return new IntentInfo[size];\n            }\n        };\n        public IntentFilter filter;\n        public boolean hasDefault;\n        public int labelRes;\n        public String nonLocalizedLabel;\n        public int icon;\n        public int logo;\n        public int banner;\n\n        public IntentInfo(PackageParser.IntentInfo info) {\n            this.filter = info;\n            this.hasDefault = info.hasDefault;\n            this.labelRes = info.labelRes;\n            if (info.nonLocalizedLabel != null) {\n                this.nonLocalizedLabel = info.nonLocalizedLabel.toString();\n            }\n            this.icon = info.icon;\n            this.logo = info.logo;\n            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {\n                this.banner = info.banner;\n            }\n\n        }\n\n        protected IntentInfo(Parcel in) {\n            this.filter = in.readParcelable(VPackage.class.getClassLoader());\n            this.hasDefault = in.readByte() != 0;\n            this.labelRes = in.readInt();\n            this.nonLocalizedLabel = in.readString();\n            this.icon = in.readInt();\n            this.logo = in.readInt();\n            this.banner = in.readInt();\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeParcelable(this.filter, flags);\n            dest.writeByte(this.hasDefault ? (byte) 1 : (byte) 0);\n            dest.writeInt(this.labelRes);\n            dest.writeString(this.nonLocalizedLabel);\n            dest.writeInt(this.icon);\n            dest.writeInt(this.logo);\n            dest.writeInt(this.banner);\n        }\n    }\n\n    public static class Component<II extends IntentInfo> {\n        public VPackage owner;\n        public ArrayList<II> intents;\n        public String className;\n        public Bundle metaData;\n        private ComponentName componentName;\n\n        protected Component() {\n\n        }\n\n        public Component(PackageParser.Component component) {\n            this.className = component.className;\n            this.metaData = component.metaData;\n        }\n\n        public ComponentName getComponentName() {\n            if (componentName != null) {\n                return componentName;\n            }\n            if (className != null) {\n                componentName = new ComponentName(owner.packageName,\n                        className);\n            }\n            return componentName;\n        }\n\n    }\n\n    public static class ActivityComponent extends Component<ActivityIntentInfo> {\n        public ActivityInfo info;\n\n        public ActivityComponent(PackageParser.Activity activity) {\n            super(activity);\n            if (activity.intents != null) {\n                this.intents = new ArrayList<>(activity.intents.size());\n                for (Object o : activity.intents) {\n                    intents.add(new ActivityIntentInfo((PackageParser.IntentInfo) o));\n                }\n            }\n            info = activity.info;\n        }\n\n        protected ActivityComponent(Parcel src) {\n            info = src.readParcelable(ActivityInfo.class.getClassLoader());\n            className = src.readString();\n            metaData = src.readBundle(Bundle.class.getClassLoader());\n            int N = src.readInt();\n            intents = new ArrayList<>(N);\n            while (N-- > 0) {\n                intents.add(new ActivityIntentInfo(src));\n            }\n        }\n    }\n\n    public static class ServiceComponent extends Component<ServiceIntentInfo> {\n        public ServiceInfo info;\n\n        public ServiceComponent(PackageParser.Service service) {\n            super(service);\n            if (service.intents != null) {\n                this.intents = new ArrayList<>(service.intents.size());\n                for (Object o : service.intents) {\n                    intents.add(new ServiceIntentInfo((PackageParser.IntentInfo) o));\n                }\n            }\n            this.info = service.info;\n        }\n\n        protected ServiceComponent(Parcel src) {\n            info = src.readParcelable(ActivityInfo.class.getClassLoader());\n            className = src.readString();\n            metaData = src.readBundle(Bundle.class.getClassLoader());\n            int N = src.readInt();\n            intents = new ArrayList<>(N);\n            while (N-- > 0) {\n                intents.add(new ServiceIntentInfo(src));\n            }\n        }\n    }\n\n    public static class ProviderComponent extends Component<ProviderIntentInfo> {\n        public ProviderInfo info;\n\n        public ProviderComponent(PackageParser.Provider provider) {\n            super(provider);\n            if (provider.intents != null) {\n                this.intents = new ArrayList<>(provider.intents.size());\n                for (Object o : provider.intents) {\n                    intents.add(new ProviderIntentInfo((PackageParser.IntentInfo) o));\n                }\n            }\n            this.info = provider.info;\n        }\n\n        protected ProviderComponent(Parcel src) {\n            info = src.readParcelable(ActivityInfo.class.getClassLoader());\n            className = src.readString();\n            metaData = src.readBundle(Bundle.class.getClassLoader());\n            int N = src.readInt();\n            intents = new ArrayList<>(N);\n            while (N-- > 0) {\n                intents.add(new ProviderIntentInfo(src));\n            }\n        }\n    }\n\n    public static class InstrumentationComponent extends Component<IntentInfo> {\n        public InstrumentationInfo info;\n\n        public InstrumentationComponent(PackageParser.Instrumentation i) {\n            super(i);\n            this.info = i.info;\n        }\n\n        protected InstrumentationComponent(Parcel src) {\n            info = src.readParcelable(ActivityInfo.class.getClassLoader());\n            className = src.readString();\n            metaData = src.readBundle(Bundle.class.getClassLoader());\n            int N = src.readInt();\n            intents = new ArrayList<>(N);\n            while (N-- > 0) {\n                intents.add(new IntentInfo(src));\n            }\n        }\n    }\n\n    public static class PermissionComponent extends Component<IntentInfo> {\n        public PermissionInfo info;\n\n        public PermissionComponent(PackageParser.Permission p) {\n            super(p);\n            this.info = p.info;\n        }\n\n        protected PermissionComponent(Parcel src) {\n            info = src.readParcelable(ActivityInfo.class.getClassLoader());\n            className = src.readString();\n            metaData = src.readBundle(Bundle.class.getClassLoader());\n            int N = src.readInt();\n            intents = new ArrayList<>(N);\n            while (N-- > 0) {\n                intents.add(new IntentInfo(src));\n            }\n        }\n    }\n\n    public static class PermissionGroupComponent extends Component<IntentInfo> {\n        public PermissionGroupInfo info;\n\n\n        public PermissionGroupComponent(PackageParser.PermissionGroup p) {\n            super(p);\n            this.info = p.info;\n        }\n\n        protected PermissionGroupComponent(Parcel src) {\n            info = src.readParcelable(ActivityInfo.class.getClassLoader());\n            className = src.readString();\n            metaData = src.readBundle(Bundle.class.getClassLoader());\n            int N = src.readInt();\n            intents = new ArrayList<>(N);\n            while (N-- > 0) {\n                intents.add(new IntentInfo(src));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/secondary/BinderDelegateService.java",
    "content": "package com.lody.virtual.server.secondary;\n\nimport android.content.ComponentName;\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.server.IBinderDelegateService;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class BinderDelegateService extends IBinderDelegateService.Stub {\n\n    private ComponentName name;\n    private IBinder service;\n\n    private interface ProxyBinderFactory {\n        IBinder create(Binder binder);\n    }\n    private static final Map<String, ProxyBinderFactory> mFactories = new HashMap<>();\n    static {\n        mFactories.put(\"android.accounts.IAccountAuthenticator\", new ProxyBinderFactory() {\n            @Override\n            public IBinder create(Binder binder) {\n                return new FakeIdentityBinder(binder);\n            }\n        });\n    }\n\n    public BinderDelegateService(ComponentName name, IBinder service) {\n        this.name = name;\n        if (service instanceof Binder) {\n            Binder localService = (Binder) service;\n            ProxyBinderFactory factory = mFactories.get(localService.getInterfaceDescriptor());\n            if (factory != null) {\n                service = factory.create(localService);\n            }\n        }\n        this.service = service;\n    }\n\n    @Override\n    public ComponentName getComponent() throws RemoteException {\n        return name;\n    }\n\n    @Override\n    public IBinder getService() throws RemoteException {\n        return service;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/secondary/FakeIdentityBinder.java",
    "content": "package com.lody.virtual.server.secondary;\n\nimport android.os.Binder;\nimport android.os.IInterface;\nimport android.os.Parcel;\nimport android.os.Process;\nimport android.os.RemoteException;\n\n/**\n * @author Lody\n */\npublic class FakeIdentityBinder extends Binder {\n\n    private Binder mBase;\n\n    public FakeIdentityBinder(Binder binder) {\n        this.mBase = binder;\n    }\n\n    public final void attachInterface(IInterface owner, String descriptor) {\n        mBase.attachInterface(owner, descriptor);\n    }\n\n    public final String getInterfaceDescriptor() {\n        return mBase.getInterfaceDescriptor();\n    }\n\n    public final boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        long clearCallingIdentity = Binder.clearCallingIdentity();\n        try {\n            Binder.restoreCallingIdentity(getFakeIdentity());\n            return mBase.transact(code, data, reply, flags);\n        } finally {\n            Binder.restoreCallingIdentity(clearCallingIdentity);\n        }\n    }\n\n    /**\n     * See: http://androidxref.com/6.0.1_r10/xref/frameworks/native/libs/binder/IPCThreadState.cpp#356\n     */\n    protected long getFakeIdentity() {\n        return (long) getFakeUid() << 32 | (long) getFakePid();\n    }\n\n    protected int getFakeUid() {\n        return Process.myUid();\n    }\n\n    protected int getFakePid() {\n        return Process.myPid();\n    }\n\n    public final IInterface queryLocalInterface(String descriptor) {\n        return mBase.queryLocalInterface(descriptor);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/vs/VSConfig.java",
    "content": "package com.lody.virtual.server.vs;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n *\n * Config of virtual storage.\n *\n */\npublic class VSConfig implements Parcelable {\n\n    public boolean enable;\n\n    public String vsPath;\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeByte(this.enable ? (byte) 1 : (byte) 0);\n        dest.writeString(this.vsPath);\n    }\n\n    public VSConfig() {\n    }\n\n    protected VSConfig(Parcel in) {\n        this.enable = in.readByte() != 0;\n        this.vsPath = in.readString();\n    }\n\n    public static final Parcelable.Creator<VSConfig> CREATOR = new Parcelable.Creator<VSConfig>() {\n        @Override\n        public VSConfig createFromParcel(Parcel source) {\n            return new VSConfig(source);\n        }\n\n        @Override\n        public VSConfig[] newArray(int size) {\n            return new VSConfig[size];\n        }\n    };\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/vs/VSPersistenceLayer.java",
    "content": "package com.lody.virtual.server.vs;\n\nimport android.os.Parcel;\nimport android.util.SparseArray;\n\nimport com.lody.virtual.helper.PersistenceLayer;\nimport com.lody.virtual.os.VEnvironment;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\nclass VSPersistenceLayer extends PersistenceLayer {\n\n    private static final char[] MAGIC = {'v', 's', 'a'};\n    private static final int CURRENT_VERSION = 1;\n    private final VirtualStorageService mService;\n\n    VSPersistenceLayer(VirtualStorageService service) {\n        super(VEnvironment.getVSConfigFile());\n        this.mService = service;\n    }\n\n    @Override\n    public int getCurrentVersion() {\n        return CURRENT_VERSION;\n    }\n\n    @Override\n    public void writeMagic(Parcel p) {\n        p.writeCharArray(MAGIC);\n    }\n\n    @Override\n    public boolean verifyMagic(Parcel p) {\n        char[] magic = p.createCharArray();\n        return Arrays.equals(magic, MAGIC);\n    }\n\n\n    @Override\n    public void writePersistenceData(Parcel p) {\n        final SparseArray<HashMap<String, VSConfig>> configs = mService.getConfigs();\n        int N = configs.size();\n        p.writeInt(N);\n        while (N-- > 0) {\n            int userId = configs.keyAt(N);\n            Map<String, VSConfig> userMap = configs.valueAt(N);\n            p.writeInt(userId);\n            p.writeMap(userMap);\n        }\n\n    }\n\n    @Override\n    public void readPersistenceData(Parcel p) {\n        final SparseArray<HashMap<String, VSConfig>> configs = mService.getConfigs();\n        int N = p.readInt();\n        while (N-- > 0) {\n            int userId = p.readInt();\n            //noinspection unchecked\n            HashMap<String, VSConfig> userMap = p.readHashMap(VSConfig.class.getClassLoader());\n            configs.put(userId, userMap);\n        }\n    }\n\n    @Override\n    public boolean onVersionConflict(int fileVersion, int currentVersion) {\n        return false;\n    }\n\n\n    @Override\n    public void onPersistenceFileDamage() {\n\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/com/lody/virtual/server/vs/VirtualStorageService.java",
    "content": "package com.lody.virtual.server.vs;\n\nimport android.os.RemoteException;\nimport android.util.SparseArray;\n\nimport com.lody.virtual.server.IVirtualStorageService;\nimport com.lody.virtual.server.pm.VUserManagerService;\n\nimport java.util.HashMap;\n\n/**\n * @author Lody\n */\n\npublic class VirtualStorageService extends IVirtualStorageService.Stub {\n\n    private static final VirtualStorageService sService = new VirtualStorageService();\n    private final VSPersistenceLayer mLayer = new VSPersistenceLayer(this);\n    private final SparseArray<HashMap<String, VSConfig>> mConfigs = new SparseArray<>();\n\n    public static VirtualStorageService get() {\n        return sService;\n    }\n\n    private VirtualStorageService() {\n        mLayer.read();\n    }\n\n    SparseArray<HashMap<String, VSConfig>> getConfigs() {\n        return mConfigs;\n    }\n\n    @Override\n    public void setVirtualStorage(String packageName, int userId, String vsPath) throws RemoteException {\n        checkUserId(userId);\n        synchronized (mConfigs) {\n            VSConfig config = getOrCreateVSConfigLocked(packageName, userId);\n            config.vsPath = vsPath;\n            mLayer.save();\n        }\n    }\n\n    private VSConfig getOrCreateVSConfigLocked(String packageName, int userId) {\n        HashMap<String, VSConfig> userMap = mConfigs.get(userId);\n        if (userMap == null) {\n            userMap = new HashMap<>();\n            mConfigs.put(userId, userMap);\n        }\n        VSConfig config = userMap.get(packageName);\n        if (config == null) {\n            config = new VSConfig();\n            config.enable = true;\n            userMap.put(packageName, config);\n        }\n        return config;\n    }\n\n\n    @Override\n    public String getVirtualStorage(String packageName, int userId) throws RemoteException {\n        checkUserId(userId);\n        synchronized (mConfigs) {\n            VSConfig config = getOrCreateVSConfigLocked(packageName, userId);\n            return config.vsPath;\n        }\n    }\n\n    @Override\n    public void setVirtualStorageState(String packageName, int userId, boolean enable) throws RemoteException {\n        checkUserId(userId);\n        synchronized (mConfigs) {\n            VSConfig config = getOrCreateVSConfigLocked(packageName, userId);\n            config.enable = enable;\n            mLayer.save();\n        }\n\n    }\n\n    @Override\n    public boolean isVirtualStorageEnable(String packageName, int userId) throws RemoteException {\n        checkUserId(userId);\n        synchronized (mConfigs) {\n            VSConfig config = getOrCreateVSConfigLocked(packageName, userId);\n            return config.enable;\n        }\n\n    }\n\n    private void checkUserId(int userId) {\n        if (!VUserManagerService.get().exists(userId)) {\n            throw new IllegalStateException(\"Invalid userId \" + userId);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/MethodParams.java",
    "content": "package mirror;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface MethodParams {\n    Class<?>[] value();\n}"
  },
  {
    "path": "lib/src/main/java/mirror/MethodReflectParams.java",
    "content": "package mirror;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface MethodReflectParams {\n    String[] value();\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefBoolean.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\npublic class RefBoolean {\n    private Field field;\n\n    public RefBoolean(Class<?> cls, Field field) throws NoSuchFieldException {\n            this.field = cls.getDeclaredField(field.getName());\n            this.field.setAccessible(true);\n    }\n\n    public boolean get(Object object) {\n        try {\n            return this.field.getBoolean(object);\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    public void set(Object obj, boolean value) {\n        try {\n            this.field.setBoolean(obj, value);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefClass.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.HashMap;\n\npublic final class RefClass {\n\n    private static HashMap<Class<?>,Constructor<?>> REF_TYPES = new HashMap<Class<?>, Constructor<?>>();\n    static {\n        try {\n            REF_TYPES.put(RefObject.class, RefObject.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefMethod.class, RefMethod.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefInt.class, RefInt.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefLong.class, RefLong.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefFloat.class, RefFloat.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefDouble.class, RefDouble.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefBoolean.class, RefBoolean.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefStaticObject.class, RefStaticObject.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefStaticInt.class, RefStaticInt.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefStaticMethod.class, RefStaticMethod.class.getConstructor(Class.class, Field.class));\n            REF_TYPES.put(RefConstructor.class, RefConstructor.class.getConstructor(Class.class, Field.class));\n        }\n        catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static Class<?> load(Class<?> mappingClass, String className) {\n        try {\n            return load(mappingClass, Class.forName(className));\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n\n    public static Class load(Class mappingClass, Class<?> realClass) {\n        // 获取 Mirror 类的所有字段\n        Field[] fields = mappingClass.getDeclaredFields();\n        for (Field field : fields) {\n            try {\n                // 必须是 static 变量\n                if (Modifier.isStatic(field.getModifiers())) {\n                    // 从预设的 Map 中找到 RefXXXX 的构造器\n                    Constructor<?> constructor = REF_TYPES.get(field.getType());\n                    if (constructor != null) {\n                        // 赋值\n                        field.set(null, constructor.newInstance(realClass, field));\n                    }\n                }\n            }\n            catch (Exception e) {\n                // Ignore\n            }\n        }\n        return realClass;\n    }\n\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefConstructor.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\n\npublic class RefConstructor<T> {\n    private Constructor<?> ctor;\n\n    public RefConstructor(Class<?> cls, Field field) throws NoSuchMethodException {\n        if (field.isAnnotationPresent(MethodParams.class)) {\n            Class<?>[] types = field.getAnnotation(MethodParams.class).value();\n            ctor = cls.getDeclaredConstructor(types);\n        } else if (field.isAnnotationPresent(MethodReflectParams.class)) {\n            String[] values = field.getAnnotation(MethodReflectParams.class).value();\n            Class[] parameterTypes = new Class[values.length];\n            int N = 0;\n            while (N < values.length) {\n                try {\n                    parameterTypes[N] = Class.forName(values[N]);\n                    N++;\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n            ctor = cls.getDeclaredConstructor(parameterTypes);\n        } else {\n            ctor = cls.getDeclaredConstructor();\n        }\n        if (ctor != null && !ctor.isAccessible()) {\n            ctor.setAccessible(true);\n        }\n    }\n\n    public T newInstance() {\n        try {\n            return (T) ctor.newInstance();\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    public T newInstance(Object... params) {\n        try {\n            return (T) ctor.newInstance(params);\n        } catch (Exception e) {\n            return null;\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefDouble.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\npublic class RefDouble {\n    private Field field;\n\n    public RefDouble(Class cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    public double get(Object object) {\n        try {\n            return this.field.getDouble(object);\n        } catch (Exception e) {\n            return 0;\n        }\n    }\n\n    public void set(Object obj, double value) {\n        try {\n            this.field.setDouble(obj, value);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefFloat.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\npublic class RefFloat {\n    private Field field;\n\n    public RefFloat(Class cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    public float get(Object object) {\n        try {\n            return this.field.getFloat(object);\n        } catch (Exception e) {\n            return 0;\n        }\n    }\n\n    public void set(Object obj, float value) {\n        try {\n            this.field.setFloat(obj, value);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefInt.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\npublic class RefInt {\n    private Field field;\n\n    public RefInt(Class cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    public int get(Object object) {\n        try {\n            return this.field.getInt(object);\n        } catch (Exception e) {\n            return 0;\n        }\n    }\n\n    public void set(Object obj, int intValue) {\n        try {\n            this.field.setInt(obj, intValue);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefLong.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\npublic class RefLong {\n    private Field field;\n\n    public RefLong(Class cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    public long get(Object object) {\n        try {\n            return this.field.getLong(object);\n        } catch (Exception e) {\n            return 0;\n        }\n    }\n\n    public void set(Object obj, long value) {\n        try {\n            this.field.setLong(obj, value);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefMethod.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport static mirror.RefStaticMethod.getProtoType;\n\n// 方法映射\n@SuppressWarnings(\"unchecked\")\npublic class RefMethod<T> {\n    private Method method;\n\n    public RefMethod(Class<?> cls, Field field) throws NoSuchMethodException {\n        // 是否是隐藏类，隐藏类使用 String 描述类型\n        if (field.isAnnotationPresent(MethodParams.class)) {\n            Class<?>[] types = field.getAnnotation(MethodParams.class).value();\n            for (int i = 0; i < types.length; i++) {\n                Class<?> clazz = types[i];\n                if (clazz.getClassLoader() == getClass().getClassLoader()) {\n                    try {\n                        Class.forName(clazz.getName());\n                        Class<?> realClass = (Class<?>) clazz.getField(\"TYPE\").get(null);\n                        types[i] = realClass;\n                    } catch (Throwable e) {\n                        throw new RuntimeException(e);\n                    }\n                }\n            }\n            this.method = cls.getDeclaredMethod(field.getName(), types);\n            this.method.setAccessible(true);\n        } else if (field.isAnnotationPresent(MethodReflectParams.class)) {\n            String[] typeNames = field.getAnnotation(MethodReflectParams.class).value();\n            Class<?>[] types = new Class<?>[typeNames.length];\n            for (int i = 0; i < typeNames.length; i++) {\n                Class<?> type = getProtoType(typeNames[i]);\n                if (type == null) {\n                    try {\n                        type = Class.forName(typeNames[i]);\n                    } catch (ClassNotFoundException e) {\n                        e.printStackTrace();\n                    }\n                }\n                types[i] = type;\n            }\n            this.method = cls.getDeclaredMethod(field.getName(), types);\n            this.method.setAccessible(true);\n        }\n        else {\n            for (Method method : cls.getDeclaredMethods()) {\n                if (method.getName().equals(field.getName())) {\n                    this.method = method;\n                    this.method.setAccessible(true);\n                    break;\n                }\n            }\n        }\n        if (this.method == null) {\n            throw new NoSuchMethodException(field.getName());\n        }\n    }\n\n    public T call(Object receiver, Object... args) {\n        try {\n            return (T) this.method.invoke(receiver, args);\n        } catch (InvocationTargetException e) {\n            if (e.getCause() != null) {\n                e.getCause().printStackTrace();\n            } else {\n                e.printStackTrace();\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    public T callWithException(Object receiver, Object... args) throws Throwable {\n        try {\n            return (T) this.method.invoke(receiver, args);\n        } catch (InvocationTargetException e) {\n            if (e.getCause() != null) {\n                throw e.getCause();\n            }\n            throw e;\n        }\n    }\n\n    public Class<?>[] paramList() {\n        return method.getParameterTypes();\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefObject.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\n// Field 映射\n@SuppressWarnings(\"unchecked\")\npublic class RefObject<T> {\n\n    // framework 层对应的 Field\n    private Field field;\n\n    public RefObject(Class<?> cls, Field field) throws NoSuchFieldException {\n        // 获取 framework 中同名字段的 field\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    // 获取变量值\n    public T get(Object object) {\n        try {\n            return (T) this.field.get(object);\n        } catch (Exception e) {\n            return null;\n        }\n    }\n    // 赋值\n    public void set(Object obj, T value) {\n        try {\n            this.field.set(obj, value);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefStaticInt.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\npublic class RefStaticInt {\n    private Field field;\n\n    public RefStaticInt(Class<?> cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    public int get() {\n        try {\n            return this.field.getInt(null);\n        } catch (Exception e) {\n            return 0;\n        }\n    }\n\n    public void set(int value) {\n        try {\n            this.field.setInt(null, value);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/RefStaticMethod.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\n@SuppressWarnings(\"unchecked\")\npublic class RefStaticMethod<T> {\n    private Method method;\n\n    public RefStaticMethod(Class<?> cls, Field field) throws NoSuchMethodException {\n        if (field.isAnnotationPresent(MethodParams.class)) {\n            Class<?>[] types = field.getAnnotation(MethodParams.class).value();\n            for (int i = 0; i < types.length; i++) {\n                Class<?> clazz = types[i];\n                if (clazz.getClassLoader() == getClass().getClassLoader()) {\n                    try {\n                        Class.forName(clazz.getName());\n                        Class<?> realClass = (Class<?>) clazz.getField(\"TYPE\").get(null);\n                        types[i] = realClass;\n                    } catch (Throwable e) {\n                        throw new RuntimeException(e);\n                    }\n                }\n            }\n            this.method = cls.getDeclaredMethod(field.getName(), types);\n            this.method.setAccessible(true);\n        } else if (field.isAnnotationPresent(MethodReflectParams.class)) {\n            boolean arrayset=false;\n            String[] typeNames = field.getAnnotation(MethodReflectParams.class).value();\n            Class<?>[] types = new Class<?>[typeNames.length];\n            Class<?>[] types2 = new Class<?>[typeNames.length];\n            for (int i = 0; i < typeNames.length; i++) {\n                Class<?> type = getProtoType(typeNames[i]);\n                if (type == null) {\n                    try {\n                        type = Class.forName(typeNames[i]);\n                    } catch (ClassNotFoundException e) {\n                        e.printStackTrace();\n                    }\n                }\n                types[i] = type;\n                if(\"java.util.HashSet\".equals(typeNames[i])){\n                    arrayset=true;\n                    Class<?> type2 =type;\n                    try {\n                        type2 = Class.forName(\"android.util.ArraySet\");\n                    } catch (ClassNotFoundException e) {\n                        e.printStackTrace();\n                    }\n                    if(type2 != null) {\n                        types2[i] = type2;\n                    }else{\n                        types2[i] = type;\n                    }\n                }else{\n                    types2[i] = type;\n                }\n            }\n            try {\n                this.method = cls.getDeclaredMethod(field.getName(), types);\n            }catch (Exception e){\n                e.printStackTrace();\n                if(arrayset){\n                    this.method = cls.getDeclaredMethod(field.getName(), types2);\n                }\n            }\n            this.method.setAccessible(true);\n        } else {\n            for (Method method : cls.getDeclaredMethods()) {\n                if (method.getName().equals(field.getName())) {\n                    this.method = method;\n                    this.method.setAccessible(true);\n                    break;\n                }\n            }\n        }\n\n        if (this.method == null) {\n            throw new NoSuchMethodException(field.getName());\n        }\n    }\n\n    static Class<?> getProtoType(String typeName) {\n        if (typeName.equals(\"int\")) {\n            return Integer.TYPE;\n        }\n        if (typeName.equals(\"long\")) {\n            return Long.TYPE;\n        }\n        if (typeName.equals(\"boolean\")) {\n            return Boolean.TYPE;\n        }\n        if (typeName.equals(\"byte\")) {\n            return Byte.TYPE;\n        }\n        if (typeName.equals(\"short\")) {\n            return Short.TYPE;\n        }\n        if (typeName.equals(\"char\")) {\n            return Character.TYPE;\n        }\n        if (typeName.equals(\"float\")) {\n            return Float.TYPE;\n        }\n        if (typeName.equals(\"double\")) {\n            return Double.TYPE;\n        }\n        if (typeName.equals(\"void\")) {\n            return Void.TYPE;\n        }\n        return null;\n    }\n\n\n    public T call(Object... params) {\n        T obj = null;\n        try {\n            obj = (T) method.invoke(null, params);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return obj;\n    }\n\n    public T callWithException(Object... params) throws Throwable {\n        try {\n            return (T) this.method.invoke(null, params);\n        } catch (InvocationTargetException e) {\n            if (e.getCause() != null) {\n                throw e.getCause();\n            }\n            throw e;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/RefStaticObject.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\n@SuppressWarnings(\"unchecked\")\npublic class RefStaticObject<T> {\n    private Field field;\n\n    public RefStaticObject(Class<?> cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\n    }\n\n    public Class<?> type() {\n        return field.getType();\n    }\n\n    public T get() {\n        T obj = null;\n        try {\n            obj = (T) this.field.get(null);\n        } catch (Exception e) {\n            //Ignore\n        }\n        return obj;\n    }\n\n    public void set(T obj) {\n        try {\n            this.field.set(null, obj);\n        } catch (Exception e) {\n            //Ignore\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/accounts/IAccountManager.java",
    "content": "package mirror.android.accounts;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IAccountManager {\n    public static Class<?> TYPE = RefClass.load(IAccountManager.class, \"android.accounts.IAccountManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.accounts.IAccountManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/Activity.java",
    "content": "package mirror.android.app;\n\n\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.os.IBinder;\n\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.RefInt;\n\npublic class Activity {\n    public static Class<?> TYPE = RefClass.load(Activity.class, \"android.app.Activity\");\n    public static RefObject<ActivityInfo> mActivityInfo;\n    public static RefBoolean mFinished;\n    public static RefObject<android.app.Activity> mParent;\n    public static RefInt mResultCode;\n    public static RefObject<Intent> mResultData;\n    public static RefObject<IBinder> mToken;\n    public static RefObject<String> mEmbeddedID;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ActivityManagerNative.java",
    "content": "package mirror.android.app;\n\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\nimport mirror.RefStaticMethod;\n\npublic class ActivityManagerNative {\n    public static Class<?> TYPE = RefClass.load(ActivityManagerNative.class, \"android.app.ActivityManagerNative\");\n    public static RefStaticObject<Object> gDefault;\n    public static RefStaticMethod<IInterface> getDefault;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ActivityManagerOreo.java",
    "content": "package mirror.android.app;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\n\npublic class ActivityManagerOreo {\n\n    public static Class<?> TYPE = RefClass.load(ActivityManagerOreo.class, \"android.app.ActivityManager\");\n\n    public static RefStaticMethod<IInterface> getService;\n    public static RefStaticObject<Object> IActivityManagerSingleton;\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ActivityThread.java",
    "content": "package mirror.android.app;\n\n\nimport android.app.*;\nimport android.app.Activity;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.ProviderInfo;\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport java.lang.ref.WeakReference;\nimport java.util.List;\nimport java.util.Map;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefObject;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticObject;\nimport mirror.RefStaticInt;\nimport mirror.RefStaticMethod;\n\npublic class ActivityThread {\n    public static Class<?> TYPE = RefClass.load(ActivityThread.class, \"android.app.ActivityThread\");\n    public static RefStaticMethod currentActivityThread;\n    public static RefMethod<String> getProcessName;\n    public static RefMethod<Handler> getHandler;\n    public static RefMethod<Object> installProvider;\n    public static RefObject<Object> mBoundApplication;\n    public static RefObject<Handler> mH;\n    public static RefObject<Application> mInitialApplication;\n    public static RefObject<Instrumentation> mInstrumentation;\n    public static RefObject<Map<String, WeakReference<?>>> mPackages;\n    public static RefObject<Map> mProviderMap;\n    @MethodParams({IBinder.class, List.class})\n    public static RefMethod<Void> performNewIntents;\n    public static RefStaticObject<IInterface> sPackageManager;\n    @MethodParams({IBinder.class, String.class, int.class, int.class, Intent.class})\n    public static RefMethod<Void> sendActivityResult;\n    public static RefMethod<Binder> getApplicationThread;\n\n    public static Object installProvider(Object mainThread, Context context, ProviderInfo providerInfo, Object holder) {\n        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n            return installProvider.call(mainThread, context, holder, providerInfo, false, true);\n        }\n        return installProvider.call(mainThread, context, holder, providerInfo, false, true, true);\n    }\n\n    public static class ActivityClientRecord {\n        public static Class<?> TYPE = RefClass.load(ActivityClientRecord.class, \"android.app.ActivityThread$ActivityClientRecord\");\n        public static RefObject<Activity> activity;\n        public static RefObject<ActivityInfo> activityInfo;\n        public static RefObject<Intent> intent;\n        public static RefObject<IBinder> token;\n    }\n\n    public static class ProviderClientRecord {\n        public static Class<?> TYPE = RefClass.load(ProviderClientRecord.class, \"android.app.ActivityThread$ProviderClientRecord\");\n        @MethodReflectParams({\"android.app.ActivityThread\", \"java.lang.String\", \"android.content.IContentProvider\", \"android.content.ContentProvider\"})\n        public static RefConstructor<?> ctor;\n        public static RefObject<String> mName;\n        public static RefObject<IInterface> mProvider;\n    }\n\n\n    public static class ProviderClientRecordJB {\n        public static Class<?> TYPE = RefClass.load(ProviderClientRecordJB.class, \"android.app.ActivityThread$ProviderClientRecord\");\n        public static RefObject<Object> mHolder;\n        public static RefObject<IInterface> mProvider;\n    }\n\n    public static class ProviderKeyJBMR1 {\n        public static Class<?> TYPE = RefClass.load(ProviderKeyJBMR1.class, \"android.app.ActivityThread$ProviderKey\");\n        @MethodParams({String.class, int.class})\n        public static RefConstructor<?> ctor;\n    }\n\n    public static class AppBindData {\n        public static Class<?> TYPE = RefClass.load(AppBindData.class, \"android.app.ActivityThread$AppBindData\");\n        public static RefObject<ApplicationInfo> appInfo;\n        public static RefObject<Object> info;\n        public static RefObject<String> processName;\n        public static RefObject<ComponentName> instrumentationName;\n        public static RefObject<List<ProviderInfo>> providers;\n    }\n\n    public static class H {\n        public static Class<?> TYPE = RefClass.load(H.class, \"android.app.ActivityThread$H\");\n        public static RefStaticInt LAUNCH_ACTIVITY;\n        public static RefStaticInt CREATE_SERVICE;\n        public static RefStaticInt SCHEDULE_CRASH;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ActivityThreadNMR1.java",
    "content": "package mirror.android.app;\n\nimport android.os.IBinder;\n\nimport java.util.List;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\n/**\n * @author Lody\n */\n\npublic class ActivityThreadNMR1 {\n    public static Class<?> Class = RefClass.load(ActivityThreadNMR1.class, \"android.app.ActivityThread\");\n    @MethodParams({IBinder.class, List.class, boolean.class})\n    public static RefMethod<Void> performNewIntents;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ApplicationThreadNative.java",
    "content": "package mirror.android.app;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\npublic class ApplicationThreadNative {\n    public static Class<?> TYPE = RefClass.load(ApplicationThreadNative.class, \"android.app.ApplicationThreadNative\");\n\n    @MethodParams({IBinder.class})\n    public static RefStaticMethod<IInterface> asInterface;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ContextImpl.java",
    "content": "package mirror.android.app;\n\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefObject;\nimport mirror.MethodParams;\n\npublic class ContextImpl {\n    public static Class<?> TYPE = RefClass.load(ContextImpl.class, \"android.app.ContextImpl\");\n    @MethodParams({Context.class})\n    public static RefObject<String> mBasePackageName;\n    public static RefObject<Object> mPackageInfo;\n    public static RefObject<PackageManager> mPackageManager;\n\n    public static RefMethod<Context> getReceiverRestrictedContext;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ContextImplICS.java",
    "content": "package mirror.android.app;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ContextImplICS {\n    public static Class<?> TYPE = RefClass.load(ContextImplICS.class, \"android.app.ContextImpl\");\n    public static RefObject<File> mExternalCacheDir;\n    public static RefObject<File> mExternalFilesDir;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ContextImplKitkat.java",
    "content": "package mirror.android.app;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ContextImplKitkat {\n    public static Class<?> TYPE = RefClass.load(ContextImplKitkat.class, \"android.app.ContextImpl\");\n    public static RefObject<File[]> mExternalCacheDirs;\n    public static RefObject<File[]> mExternalFilesDirs;\n    public static RefObject<String> mOpPackageName;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IActivityManager.java",
    "content": "package mirror.android.app;\n\nimport android.content.pm.ProviderInfo;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefObject;\n\npublic class IActivityManager {\n    public static Class<?> TYPE = RefClass.load(IActivityManager.class, \"android.app.IActivityManager\");\n    @MethodParams({IBinder.class, boolean.class})\n    public static RefMethod<Integer> getTaskForActivity;\n    @MethodParams({IBinder.class, int.class})\n    public static RefMethod<Void> setRequestedOrientation;\n    @MethodParams({IBinder.class, String.class, int.class, int.class})\n    public static RefMethod<Void> overridePendingTransition;\n    public static RefMethod<Integer> startActivity;\n    public static RefMethod<Integer> startActivities;\n\n    public static class ContentProviderHolder {\n        public static Class<?> TYPE = RefClass.load(ContentProviderHolder.class, \"android.app.IActivityManager$ContentProviderHolder\");\n        public static RefObject<ProviderInfo> info;\n        public static RefObject<IInterface> provider;\n        public static RefBoolean noReleaseNeeded;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IActivityManagerICS.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\nimport android.os.IBinder;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\npublic class IActivityManagerICS {\n    public static Class<?> TYPE = RefClass.load(IActivityManagerICS.class, \"android.app.IActivityManager\");\n    @MethodParams({IBinder.class, int.class, Intent.class})\n    public static RefMethod<Boolean> finishActivity;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IActivityManagerL.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\nimport android.os.IBinder;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class IActivityManagerL {\n    public static Class<?> TYPE = RefClass.load(IActivityManagerL.class, \"android.app.IActivityManager\");\n    @MethodParams({IBinder.class, int.class, Intent.class, boolean.class})\n    public static RefMethod<Boolean> finishActivity;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IActivityManagerN.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\nimport android.os.IBinder;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\n/**\n * @author Lody\n */\n\npublic class IActivityManagerN {\n    public static Class<?> TYPE = RefClass.load(IActivityManagerN.class, \"android.app.IActivityManager\");\n    @MethodParams({IBinder.class, int.class, Intent.class, int.class})\n    public static RefMethod<Boolean> finishActivity;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IAlarmManager.java",
    "content": "package mirror.android.app;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class IAlarmManager {\n    public static Class<?> TYPE = RefClass.load(IAlarmManager.class, \"android.app.IAlarmManager\");\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.app.IAlarmManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IApplicationThread.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\nimport android.content.pm.ServiceInfo;\nimport android.os.IBinder;\n\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThread {\n    public static Class<?> TYPE = RefClass.load(IApplicationThread.class, \"android.app.IApplicationThread\");\n\n    @MethodParams({List.class, IBinder.class})\n    public static RefMethod<Void> scheduleNewIntent;\n\n    @MethodParams({IBinder.class, ServiceInfo.class})\n    public static RefMethod<Void> scheduleCreateService;\n\n    @MethodParams({IBinder.class, Intent.class, boolean.class})\n    public static RefMethod<Void> scheduleBindService;\n\n    @MethodParams({IBinder.class, Intent.class})\n    public static RefMethod<Void> scheduleUnbindService;\n\n    @MethodParams({IBinder.class, int.class, int.class, Intent.class})\n    public static RefMethod<Void> scheduleServiceArgs;\n\n    @MethodParams({IBinder.class})\n    public static RefMethod<Void> scheduleStopService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IApplicationThreadICSMR1.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\nimport android.content.pm.ServiceInfo;\nimport android.os.IBinder;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.android.content.res.CompatibilityInfo;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadICSMR1 {\n    public static Class<?> TYPE = RefClass.load(IApplicationThreadICSMR1.class, \"android.app.IApplicationThread\");\n\n    @MethodReflectParams({\"android.content.Intent\", \"android.content.pm.ActivityInfo\", \"android.content.res.CompatibilityInfo\", \"int\", \"java.lang.String\", \"android.os.Bundle\", \"boolean\"})\n    public static RefMethod<Void> scheduleReceiver;\n\n    @MethodParams({IBinder.class, ServiceInfo.class, CompatibilityInfo.class})\n    public static RefMethod<Void> scheduleCreateService;\n\n    @MethodParams({IBinder.class, boolean.class, int.class, int.class, Intent.class})\n    public static RefMethod<Void> scheduleServiceArgs;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IApplicationThreadJBMR1.java",
    "content": "package mirror.android.app;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodReflectParams;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadJBMR1 {\n    public static Class<?> TYPE = RefClass.load(IApplicationThreadJBMR1.class, \"android.app.IApplicationThread\");\n\n    @MethodReflectParams({\"android.content.Intent\", \"android.content.pm.ActivityInfo\", \"android.content.res.CompatibilityInfo\", \"int\", \"java.lang.String\", \"android.os.Bundle\", \"boolean\", \"int\"})\n    public static RefMethod<Void> scheduleReceiver;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IApplicationThreadKitkat.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\nimport android.content.pm.ServiceInfo;\nimport android.os.IBinder;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.android.content.res.CompatibilityInfo;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadKitkat {\n    public static Class<?> TYPE = RefClass.load(IApplicationThreadKitkat.class, \"android.app.IApplicationThread\");\n\n    @MethodReflectParams({\"android.content.Intent\", \"android.content.pm.ActivityInfo\", \"android.content.res.CompatibilityInfo\", \"int\", \"java.lang.String\", \"android.os.Bundle\", \"boolean\", \"int\", \"int\"})\n    public static RefMethod<Void> scheduleReceiver;\n\n    @MethodParams({IBinder.class, ServiceInfo.class, CompatibilityInfo.class, int.class})\n    public static RefMethod<Void> scheduleCreateService;\n\n    @MethodParams({IBinder.class, Intent.class, boolean.class, int.class})\n    public static RefMethod<Void> scheduleBindService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/IApplicationThreadOreo.java",
    "content": "package mirror.android.app;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadOreo {\n\n    public static final class Stub {\n        public static Class<?> TYPE = RefClass.load(IApplicationThreadOreo.Stub.class, \"android.app.IApplicationThread$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/ISearchManager.java",
    "content": "package mirror.android.app;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class ISearchManager {\n    public static Class<?> TYPE = RefClass.load(ISearchManager.class, \"android.app.ISearchManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.app.ISearchManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/LoadedApk.java",
    "content": "package mirror.android.app;\n\nimport java.lang.ref.WeakReference;\n\nimport android.app.Application;\nimport android.app.Instrumentation;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.IIntentReceiver;\nimport android.content.ServiceConnection;\nimport android.content.pm.ApplicationInfo;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\npublic class LoadedApk {\n    public static Class Class = RefClass.load(LoadedApk.class, \"android.app.LoadedApk\");\n    public static RefObject<ApplicationInfo> mApplicationInfo;\n    @MethodParams({boolean.class, Instrumentation.class})\n    public static RefMethod<Application> makeApplication;\n\n    public static class ReceiverDispatcher {\n        public static Class Class = RefClass.load(ReceiverDispatcher.class, \"android.app.LoadedApk$ReceiverDispatcher\");\n        public static RefMethod<IInterface> getIIntentReceiver;\n        public static RefObject<BroadcastReceiver> mReceiver;\n        public static RefObject<IIntentReceiver> mIIntentReceiver;\n\n        public static class InnerReceiver {\n            public static Class Class = RefClass.load(InnerReceiver.class, \"android.app.LoadedApk$ReceiverDispatcher$InnerReceiver\");\n            public static RefObject<WeakReference> mDispatcher;\n        }\n    }\n\n    public static class ServiceDispatcher {\n        public static Class Class = RefClass.load(ServiceDispatcher.class, \"android.app.LoadedApk$ServiceDispatcher\");\n        public static RefObject<ServiceConnection> mConnection;\n        public static RefObject<Context> mContext;\n\n        public static class InnerConnection {\n            public static Class Class = RefClass.load(InnerConnection.class, \"android.app.LoadedApk$ServiceDispatcher$InnerConnection\");\n            public static RefObject<WeakReference> mDispatcher;\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/LoadedApkHuaWei.java",
    "content": "package mirror.android.app;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\npublic class LoadedApkHuaWei {\n    public static Class<?> TYPE = RefClass.load(LoadedApkHuaWei.class, \"android.app.LoadedApk\");\n    public static RefObject<Object> mReceiverResource;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/Notification.java",
    "content": "package mirror.android.app;\n\nimport android.app.PendingIntent;\nimport android.content.Context;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\npublic class Notification {\n    public static Class<?> TYPE = RefClass.load(Notification.class, android.app.Notification.class);\n    @MethodParams({Context.class, CharSequence.class, CharSequence.class, PendingIntent.class})\n    public static RefMethod<Void> setLatestEventInfo;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/NotificationL.java",
    "content": "package mirror.android.app;\n\nimport android.app.Notification;\nimport android.content.Context;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class NotificationL {\n    public static Class<?> TYPE = RefClass.load(NotificationL.class, Notification.class);\n\n    public static class Builder {\n        public static Class<?> TYPE = RefClass.load(Builder.class, android.app.Notification.Builder.class);\n\n        @MethodParams({Context.class, Notification.class})\n        public static RefStaticMethod<Notification> rebuild;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/NotificationM.java",
    "content": "package mirror.android.app;\n\n\nimport android.app.Notification;\nimport android.graphics.drawable.Icon;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class NotificationM {\n    public static Class<?> TYPE = RefClass.load(NotificationM.class, Notification.class);\n    public static RefObject<Icon> mLargeIcon;\n    public static RefObject<Icon> mSmallIcon;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/NotificationManager.java",
    "content": "package mirror.android.app;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\nimport mirror.RefStaticMethod;\n\npublic class NotificationManager {\n    public static Class<?> TYPE = RefClass.load(NotificationManager.class, \"android.app.NotificationManager\");\n\n    public static RefStaticMethod<IInterface> getService;\n    public static RefStaticObject<IInterface> sService;\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/PendingIntentJBMR2.java",
    "content": "package mirror.android.app;\n\nimport android.app.PendingIntent;\nimport android.content.Intent;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\npublic class PendingIntentJBMR2 {\n    public static Class Class = RefClass.load(PendingIntentJBMR2.class, PendingIntent.class);\n    public static RefMethod<Intent> getIntent;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/backup/IBackupManager.java",
    "content": "package mirror.android.app.backup;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IBackupManager {\n    public static Class<?> TYPE = RefClass.load(IBackupManager.class, \"android.app.backup.IBackupManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.app.backup.IBackupManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/job/IJobScheduler.java",
    "content": "package mirror.android.app.job;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IJobScheduler {\n    public static Class<?> TYPE = RefClass.load(IJobScheduler.class, \"android.app.job.IJobScheduler\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.app.job.IJobScheduler$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/job/JobInfo.java",
    "content": "package mirror.android.app.job;\n\nimport android.annotation.TargetApi;\nimport android.content.ComponentName;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefInt;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class JobInfo {\n    public static Class<?> TYPE = RefClass.load(JobInfo.class, android.app.job.JobInfo.class);\n\n    public static RefInt jobId;\n    public static RefObject<ComponentName> service;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/app/job/JobParameters.java",
    "content": "package mirror.android.app.job;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.PersistableBundle;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class JobParameters {\n    public static Class<?> TYPE = RefClass.load(JobParameters.class, android.app.job.JobParameters.class);\n\n    public static RefObject<IBinder> callback;\n    public static RefObject<PersistableBundle> extras;\n    public static RefInt jobId;\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/bluetooth/IBluetooth.java",
    "content": "package mirror.android.bluetooth;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class IBluetooth {\n    /**\n     * @see android.bluetooth.IBluetooth\n     * */\n    public static Class<?> TYPE = RefClass.load(IBluetooth.class, \"android.bluetooth.IBluetooth\");\n    /**\n     * @see android.bluetooth.IBluetooth.Stub\n     * */\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.bluetooth.IBluetooth$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/BroadcastReceiver.java",
    "content": "package mirror.android.content;\n\n\nimport android.os.Bundle;\nimport android.os.IBinder;\n\nimport mirror.MethodParams;\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\nimport mirror.RefMethod;\nimport mirror.RefObject;\n\npublic class BroadcastReceiver {\n    public static Class<?> Class = RefClass.load(BroadcastReceiver.class, android.content.BroadcastReceiver.class);\n    public static RefMethod<android.content.BroadcastReceiver.PendingResult> getPendingResult;\n    @MethodParams({android.content.BroadcastReceiver.PendingResult.class})\n    public static RefMethod<Void> setPendingResult;\n\n    public static class PendingResult {\n        public static Class<?> Class = RefClass.load(PendingResult.class, android.content.BroadcastReceiver.PendingResult.class);\n        @MethodParams({int.class, String.class, Bundle.class, int.class, boolean.class, boolean.class, IBinder.class})\n        public static RefConstructor<android.content.BroadcastReceiver.PendingResult> ctor;\n        public static RefBoolean mAbortBroadcast;\n        public static RefBoolean mFinished;\n        public static RefBoolean mInitialStickyHint;\n        public static RefBoolean mOrderedHint;\n        public static RefInt mResultCode;\n        public static RefObject<String> mResultData;\n        public static RefObject<Bundle> mResultExtras;\n        public static RefObject<IBinder> mToken;\n        public static RefInt mType;\n    }\n\n    public static class PendingResultJBMR1 {\n        public static Class<?> Class = RefClass.load(PendingResultJBMR1.class, android.content.BroadcastReceiver.PendingResult.class);\n        @MethodParams({int.class, String.class, Bundle.class, int.class, boolean.class, boolean.class, IBinder.class, int.class})\n        public static RefConstructor<android.content.BroadcastReceiver.PendingResult> ctor;\n        public static RefBoolean mAbortBroadcast;\n        public static RefBoolean mFinished;\n        public static RefBoolean mInitialStickyHint;\n        public static RefBoolean mOrderedHint;\n        public static RefInt mResultCode;\n        public static RefObject<String> mResultData;\n        public static RefObject<Bundle> mResultExtras;\n        public static RefInt mSendingUser;\n        public static RefObject<IBinder> mToken;\n        public static RefInt mType;\n    }\n\n    public static class PendingResultMNC {\n        public static Class<?> Class = RefClass.load(PendingResultMNC.class, android.content.BroadcastReceiver.PendingResult.class);\n        @MethodParams({int.class, String.class, Bundle.class, int.class, boolean.class, boolean.class, IBinder.class, int.class, int.class})\n        public static RefConstructor<android.content.BroadcastReceiver.PendingResult> ctor;\n        public static RefBoolean mAbortBroadcast;\n        public static RefBoolean mFinished;\n        public static RefInt mFlags;\n        public static RefBoolean mInitialStickyHint;\n        public static RefBoolean mOrderedHint;\n        public static RefInt mResultCode;\n        public static RefObject<String> mResultData;\n        public static RefObject<Bundle> mResultExtras;\n        public static RefInt mSendingUser;\n        public static RefObject<IBinder> mToken;\n        public static RefInt mType;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ClipboardManager.java",
    "content": "package mirror.android.content;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\nimport mirror.RefStaticMethod;\n\npublic class ClipboardManager {\n    public static Class<?> TYPE = RefClass.load(ClipboardManager.class, android.content.ClipboardManager.class);\n    public static RefStaticMethod<IInterface> getService;\n    public static RefStaticObject<IInterface> sService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ClipboardManagerOreo.java",
    "content": "package mirror.android.content;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ClipboardManagerOreo {\n    public static Class<?> TYPE = RefClass.load(ClipboardManagerOreo.class, android.content.ClipboardManager.class);\n    public static RefObject<IInterface> mService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ContentProviderClient.java",
    "content": "package mirror.android.content;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ContentProviderClient {\n    public static Class Class = RefClass.load(ContentProviderClient.class, android.content.ContentProviderClient.class);\n    public static RefObject<IInterface> mContentProvider;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ContentProviderHolderOreo.java",
    "content": "package mirror.android.content;\n\nimport android.content.pm.ProviderInfo;\nimport android.os.IInterface;\n\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\npublic class ContentProviderHolderOreo {\n    public static Class<?> TYPE = RefClass.load(ContentProviderHolderOreo.class, \"android.app.ContentProviderHolder\");\n    public static RefObject<ProviderInfo> info;\n    public static RefObject<IInterface> provider;\n    public static RefBoolean noReleaseNeeded;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ContentProviderNative.java",
    "content": "package mirror.android.content;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class ContentProviderNative {\n    public static Class<?> TYPE = RefClass.load(ContentProviderNative.class, \"android.content.ContentProviderNative\");\n    @MethodParams({IBinder.class})\n    public static RefStaticMethod<IInterface> asInterface;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ContentResolver.java",
    "content": "package mirror.android.content;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\n\npublic class ContentResolver {\n    public static Class<?> TYPE = RefClass.load(ContentResolver.class, android.content.ContentResolver.class);\n    public static RefStaticObject<IInterface> sContentService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/ContentResolverJBMR2.java",
    "content": "package mirror.android.content;\n\nimport android.content.ContentResolver;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ContentResolverJBMR2 {\n    public static Class Class = RefClass.load(ContentResolverJBMR2.class, ContentResolver.class);;\n    public static RefObject<String> mPackageName;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IClipboard.java",
    "content": "package mirror.android.content;\n\nimport mirror.RefClass;\n\npublic class IClipboard {\n    public static Class<?> TYPE = RefClass.load(IClipboard.class, \"android.content.IClipboard\");\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IContentProvider.java",
    "content": "package mirror.android.content;\n\nimport mirror.RefClass;\n\n/**\n * @author Lody\n */\n\npublic class IContentProvider {\n    public static Class<?> TYPE = RefClass.load(IContentProvider.class, \"android.content.IContentProvider\");\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IContentService.java",
    "content": "package mirror.android.content;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class IContentService {\n    public static Class<?> TYPE = RefClass.load(IContentService.class, \"android.content.IContentService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.content.IContentService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IIntentReceiver.java",
    "content": "package mirror.android.content;\n\nimport android.content.Intent;\nimport android.os.Bundle;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class IIntentReceiver {\n    public static Class<?> TYPE = RefClass.load(IIntentReceiver.class, \"android.content.IIntentReceiver\");\n\n    @MethodParams({Intent.class, int.class,\n            String.class, Bundle.class, boolean.class, boolean.class})\n    public static RefMethod<Void> performReceive;\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IIntentReceiverJB.java",
    "content": "package mirror.android.content;\n\nimport android.content.Intent;\nimport android.os.Bundle;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class IIntentReceiverJB {\n    public static Class<?> TYPE = RefClass.load(IIntentReceiverJB.class, \"android.content.IIntentReceiver\");\n\n    @MethodParams({Intent.class, int.class,\n            String.class, Bundle.class, boolean.class, boolean.class, int.class})\n    public static RefMethod<Void> performReceive;\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IRestrictionsManager.java",
    "content": "package mirror.android.content;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IRestrictionsManager {\n    public static Class<?> TYPE = RefClass.load(IRestrictionsManager.class, \"android.content.IRestrictionsManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.content.IRestrictionsManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/IntentFilter.java",
    "content": "package mirror.android.content;\n\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\npublic class IntentFilter {\n    public static Class Class = RefClass.load(IntentFilter.class, android.content.IntentFilter.class);\n    public static RefObject<List<String>> mActions;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/SyncAdapterType.java",
    "content": "package mirror.android.content;\n\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\n\npublic class SyncAdapterType {\n    public static Class<?> TYPE = RefClass.load(SyncAdapterType.class, android.content.SyncAdapterType.class);\n    @MethodParams({String.class, String.class, boolean.class, boolean.class, boolean.class, boolean.class, String.class})\n    public static RefConstructor<android.content.SyncAdapterType> ctor;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/SyncAdapterTypeN.java",
    "content": "package mirror.android.content;\n\nimport android.content.SyncAdapterType;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\n\npublic class SyncAdapterTypeN {\n    public static Class<?> Class = RefClass.load(SyncAdapterTypeN.class, SyncAdapterType.class);\n    @MethodParams({String.class, String.class, boolean.class, boolean.class, boolean.class, boolean.class, String.class, String.class})\n    public static RefConstructor<SyncAdapterType> ctor;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/SyncInfo.java",
    "content": "package mirror.android.content;\n\nimport android.accounts.Account;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\n\npublic class SyncInfo {\n    public static Class<?> TYPE = RefClass.load(SyncInfo.class, android.content.SyncInfo.class);\n    @MethodParams({int.class, Account.class, String.class, long.class})\n    public static RefConstructor<android.content.SyncInfo> ctor;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/SyncRequest.java",
    "content": "package mirror.android.content;\n\n\nimport android.accounts.Account;\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.Bundle;\n\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefLong;\nimport mirror.RefObject;\n\n@TargetApi(Build.VERSION_CODES.KITKAT)\npublic class SyncRequest {\n    public static Class<?> TYPE = RefClass.load(SyncRequest.class, android.content.SyncRequest.class);\n    public static RefObject<Account> mAccountToSync;\n    public static RefObject<String> mAuthority;\n    public static RefObject<Bundle> mExtras;\n    public static RefBoolean mIsPeriodic;\n    public static RefLong mSyncRunTimeSecs;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/ApplicationInfoL.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.ApplicationInfo;\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ApplicationInfoL {\n    public static Class<?> TYPE = RefClass.load(ApplicationInfoL.class, ApplicationInfo.class);\n    public static RefObject<String> primaryCpuAbi;\n    public static RefObject<String> scanPublicSourceDir;\n    public static RefObject<String> scanSourceDir;\n    public static RefObject<String[]> splitPublicSourceDirs;\n    public static RefObject<String[]> splitSourceDirs;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/ApplicationInfoN.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.ApplicationInfo;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ApplicationInfoN {\n    public static Class<?> TYPE = RefClass.load(ApplicationInfoN.class, ApplicationInfo.class);\n    public static RefObject<String> deviceProtectedDataDir;\n    public static RefObject<String> deviceEncryptedDataDir;\n    public static RefObject<String> credentialProtectedDataDir;\n    public static RefObject<String> credentialEncryptedDataDir;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/ILauncherApps.java",
    "content": "package mirror.android.content.pm;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\npublic class ILauncherApps {\n\n    public static final class Stub {\n        public static Class<?> TYPE = RefClass.load(ILauncherApps.class, \"android.content.pm.ILauncherApps$Stub\");\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/IShortcutService.java",
    "content": "package mirror.android.content.pm;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class IShortcutService {\n\n    public static final class Stub {\n        public static Class<?> TYPE = RefClass.load(IShortcutService.class, \"android.content.pm.IShortcutService$Stub\");\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/LauncherApps.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.PackageManager;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.com.android.internal.os.UserManager;\n\n/**\n * @author Lody\n */\n\npublic class LauncherApps {\n\n    public static Class<?> TYPE = RefClass.load(LauncherApps.class, \"android.content.pm.LauncherApps\");\n\n    public static RefObject<PackageManager> mPm;\n    public static RefObject<IInterface> mService;\n    public static RefObject<UserManager> mUserManager;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageInstaller.java",
    "content": "package mirror.android.content.pm;\n\n\nimport android.graphics.Bitmap;\nimport android.net.Uri;\n\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefFloat;\nimport mirror.RefInt;\nimport mirror.RefLong;\nimport mirror.RefObject;\n\npublic class PackageInstaller {\n\n    public static class SessionInfo {\n        public static Class<?> TYPE = RefClass.load(SessionInfo.class, \"android.content.pm.PackageInstaller$SessionInfo\");\n        public static RefBoolean active;\n        public static RefObject<Bitmap> appIcon;\n        public static RefObject<CharSequence> appLabel;\n        public static RefObject<String> appPackageName;\n        public static RefConstructor<android.content.pm.PackageInstaller.SessionInfo> ctor;\n        public static RefObject<String> installerPackageName;\n        public static RefInt mode;\n        public static RefFloat progress;\n        public static RefObject<String> resolvedBaseCodePath;\n        public static RefBoolean sealed;\n        public static RefInt sessionId;\n        public static RefLong sizeBytes;\n    }\n\n    public static class SessionParamsLOLLIPOP {\n        public static Class<?> TYPE = RefClass.load(SessionParamsLOLLIPOP.class, \"android.content.pm.PackageInstaller$SessionParams\");\n        public static RefObject<String> abiOverride;\n        public static RefObject<Bitmap> appIcon;\n        public static RefLong appIconLastModified;\n        public static RefObject<String> appLabel;\n        public static RefObject<String> appPackageName;\n        public static RefInt installFlags;\n        public static RefInt installLocation;\n        public static RefInt mode;\n        public static RefObject<Uri> originatingUri;\n        public static RefObject<Uri> referrerUri;\n        public static RefLong sizeBytes;\n    }\n\n    public static class SessionParamsMarshmallow {\n        public static Class<?> TYPE = RefClass.load(SessionParamsMarshmallow.class, \"android.content.pm.PackageInstaller$SessionParams\");\n        public static RefObject<String> abiOverride;\n        public static RefObject<Bitmap> appIcon;\n        public static RefLong appIconLastModified;\n        public static RefObject<String> appLabel;\n        public static RefObject<String> appPackageName;\n        public static RefObject<String[]> grantedRuntimePermissions;\n        public static RefInt installFlags;\n        public static RefInt installLocation;\n        public static RefInt mode;\n        public static RefObject<Uri> originatingUri;\n        public static RefObject<Uri> referrerUri;\n        public static RefLong sizeBytes;\n        public static RefObject<String> volumeUuid;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParser.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.ComponentName;\nimport android.content.IntentFilter;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PermissionGroupInfo;\nimport android.content.pm.PermissionInfo;\nimport android.content.pm.ProviderInfo;\nimport android.content.pm.ServiceInfo;\nimport android.content.pm.Signature;\nimport android.os.Bundle;\nimport android.util.DisplayMetrics;\n\nimport java.io.File;\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefObject;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParser {\n\n    public static Class<?> TYPE = RefClass.load(PackageParser.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefMethod<Void> collectCertificates;\n    @MethodParams({String.class})\n    public static RefConstructor<android.content.pm.PackageParser> ctor;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Activity\", \"int\"})\n    public static RefStaticMethod<ActivityInfo> generateActivityInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefStaticMethod<ApplicationInfo> generateApplicationInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"[I\", \"int\", \"long\", \"long\"})\n    public static RefStaticMethod<PackageInfo> generatePackageInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Provider\", \"int\"})\n    public static RefStaticMethod<ProviderInfo> generateProviderInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Service\", \"int\"})\n    public static RefStaticMethod<ServiceInfo> generateServiceInfo;\n    @MethodParams({File.class, String.class, DisplayMetrics.class, int.class})\n    public static RefMethod<android.content.pm.PackageParser.Package> parsePackage;\n\n    // 中间包组件结构\n    public static class Package {\n        public static Class<?> TYPE = RefClass.load(Package.class, \"android.content.pm.PackageParser$Package\");\n        public static RefObject<List> activities;\n        public static RefObject<Bundle> mAppMetaData;\n        public static RefObject<String> mSharedUserId;\n        public static RefObject<Signature[]> mSignatures;\n        public static RefObject<Integer> mVersionCode;\n        public static RefObject<String> packageName;\n        public static RefObject<List> permissionGroups;\n        public static RefObject<List> permissions;\n        public static RefObject<List<String>> protectedBroadcasts;\n        public static RefObject<List> providers;\n        public static RefObject<List> receivers;\n        public static RefObject<List<String>> requestedPermissions;\n        public static RefObject<List> services;\n    }\n\n    public static class Activity {\n        public static Class<?> TYPE = RefClass.load(Activity.class, \"android.content.pm.PackageParser$Activity\");\n        public static RefObject<ActivityInfo> info;\n    }\n\n    public static class Provider {\n        public static Class<?> TYPE = RefClass.load(Provider.class, \"android.content.pm.PackageParser$Provider\");\n        public static RefObject<ProviderInfo> info;\n    }\n\n    public static class Service {\n        public static Class<?> TYPE = RefClass.load(Provider.class, \"android.content.pm.PackageParser$Service\");\n        public static RefObject<ServiceInfo> info;\n    }\n\n\n\n\n    public static class Permission {\n        public static Class<?> TYPE = RefClass.load(Permission.class, \"android.content.pm.PackageParser$Permission\");\n        public static RefObject<PermissionInfo> info;\n    }\n\n    public static class PermissionGroup {\n        public static Class<?> TYPE = RefClass.load(PermissionGroup.class, \"android.content.pm.PackageParser$PermissionGroup\");\n        public static RefObject<PermissionGroupInfo> info;\n    }\n\n    public static class Component {\n        public static Class<?> TYPE = RefClass.load(Component.class, \"android.content.pm.PackageParser$Component\");\n        public static RefObject<String> className;\n        public static RefObject<ComponentName> componentName;\n        public static RefObject<List<IntentFilter>> intents;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParserJellyBean.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.*;\nimport android.content.pm.PackageParser;\nimport android.util.DisplayMetrics;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserJellyBean {\n    public static Class<?> TYPE = RefClass.load(PackageParserJellyBean.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefMethod<Void> collectCertificates;\n    @MethodParams({String.class})\n    public static RefConstructor<PackageParser> ctor;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Activity\", \"int\", \"boolean\", \"int\", \"int\"})\n    public static RefStaticMethod<ActivityInfo> generateActivityInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\", \"boolean\", \"int\"})\n    public static RefStaticMethod<ApplicationInfo> generateApplicationInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"[I\", \"int\", \"long\", \"long\", \"java.util.HashSet\"})\n    public static RefStaticMethod<PackageInfo> generatePackageInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Provider\", \"int\", \"boolean\", \"int\", \"int\"})\n    public static RefStaticMethod<ProviderInfo> generateProviderInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Service\", \"int\", \"boolean\", \"int\", \"int\"})\n    public static RefStaticMethod<ServiceInfo> generateServiceInfo;\n    @MethodParams({File.class, String.class, DisplayMetrics.class, int.class})\n    public static RefMethod<PackageParser.Package> parsePackage;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParserJellyBean17.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.*;\nimport android.content.pm.PackageParser;\nimport android.util.DisplayMetrics;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserJellyBean17 {\n    public static Class<?> TYPE = RefClass.load(PackageParserJellyBean17.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefMethod<Void> collectCertificates;\n    @MethodParams({String.class})\n    public static RefConstructor<PackageParser> ctor;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Activity\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ActivityInfo> generateActivityInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<ApplicationInfo> generateApplicationInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"[I\", \"int\", \"long\", \"long\", \"java.util.HashSet\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<PackageInfo> generatePackageInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Provider\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ProviderInfo> generateProviderInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Service\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ServiceInfo> generateServiceInfo;\n    @MethodParams({File.class, String.class, DisplayMetrics.class, int.class})\n    public static RefMethod<PackageParser.Package> parsePackage;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParserLollipop.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.*;\nimport android.content.pm.PackageParser;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserLollipop {\n    public static Class<?> TYPE = RefClass.load(PackageParserLollipop.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefMethod<Void> collectCertificates;\n    public static RefConstructor<PackageParser> ctor;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Activity\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ActivityInfo> generateActivityInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<ApplicationInfo> generateApplicationInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"[I\", \"int\", \"long\", \"long\", \"java.util.HashSet\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<PackageInfo> generatePackageInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Provider\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ProviderInfo> generateProviderInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Service\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ServiceInfo> generateServiceInfo;\n    @MethodParams({File.class, int.class})\n    public static RefMethod<PackageParser.Package> parsePackage;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParserLollipop22.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.*;\nimport android.content.pm.PackageParser;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserLollipop22 {\n    public static Class<?> TYPE = RefClass.load(PackageParserLollipop22.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefMethod<Void> collectCertificates;\n    public static RefConstructor<PackageParser> ctor;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Activity\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ActivityInfo> generateActivityInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<ApplicationInfo> generateApplicationInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"[I\", \"int\", \"long\", \"long\", \"android.util.ArraySet\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<PackageInfo> generatePackageInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Provider\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ProviderInfo> generateProviderInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Service\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ServiceInfo> generateServiceInfo;\n    @MethodParams({File.class, int.class})\n    public static RefMethod<PackageParser.Package> parsePackage;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParserMarshmallow.java",
    "content": "package mirror.android.content.pm;\n\nimport android.content.pm.*;\nimport android.content.pm.PackageParser;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.MethodReflectParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserMarshmallow {\n    public static Class<?> TYPE = RefClass.load(PackageParserMarshmallow.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefMethod<Void> collectCertificates;\n    public static RefConstructor<PackageParser> ctor;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Activity\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ActivityInfo> generateActivityInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<ApplicationInfo> generateApplicationInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"[I\", \"int\", \"long\", \"long\", \"java.util.Set\", \"android.content.pm.PackageUserState\"})\n    public static RefStaticMethod<PackageInfo> generatePackageInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Provider\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ProviderInfo> generateProviderInfo;\n    @MethodReflectParams({\"android.content.pm.PackageParser$Service\", \"int\", \"android.content.pm.PackageUserState\", \"int\"})\n    public static RefStaticMethod<ServiceInfo> generateServiceInfo;\n    @MethodParams({File.class, int.class})\n    public static RefMethod<PackageParser.Package> parsePackage;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageParserNougat.java",
    "content": "package mirror.android.content.pm;\n\nimport mirror.MethodReflectParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class PackageParserNougat {\n    public static Class<?> TYPE = RefClass.load(PackageParserNougat.class, \"android.content.pm.PackageParser\");\n    @MethodReflectParams({\"android.content.pm.PackageParser$Package\", \"int\"})\n    public static RefStaticMethod<Void> collectCertificates;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/PackageUserState.java",
    "content": "package mirror.android.content.pm;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\n\npublic class PackageUserState {\n    public static Class<?> TYPE = RefClass.load(PackageUserState.class, \"android.content.pm.PackageUserState\");\n    public static RefConstructor<Object> ctor;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/ParceledListSlice.java",
    "content": "package mirror.android.content.pm;\n\nimport android.os.Parcelable;\n\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\n\npublic class ParceledListSlice {\n    public static RefStaticObject<Parcelable.Creator> CREATOR;\n    public static Class<?> TYPE = RefClass.load(ParceledListSlice.class, \"android.content.pm.ParceledListSlice\");\n    public static RefMethod<Boolean> append;\n    public static RefConstructor<Parcelable> ctor;\n    public static RefMethod<Boolean> isLastSlice;\n    public static RefMethod<Parcelable> populateList;\n    public static RefMethod<Void> setLastSlice;\n    public static RefMethod<List<?>> getList;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/ParceledListSliceJBMR2.java",
    "content": "package mirror.android.content.pm;\n\nimport android.os.Parcelable;\n\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\n\npublic class ParceledListSliceJBMR2 {\n    public static RefStaticObject<Parcelable.Creator> CREATOR;\n    public static Class<?> TYPE = RefClass.load(ParceledListSliceJBMR2.class, \"android.content.pm.ParceledListSlice\");\n    @MethodParams({List.class})\n    public static RefConstructor<Parcelable> ctor;\n    public static RefMethod<List> getList;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/pm/UserInfo.java",
    "content": "package mirror.android.content.pm;\n\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefStaticInt;\n\npublic class UserInfo {\n    public static Class<?> TYPE = RefClass.load(UserInfo.class, \"android.content.pm.UserInfo\");\n    public static RefStaticInt FLAG_PRIMARY;\n    @MethodParams({int.class, String.class, int.class})\n    public static RefConstructor<Object> ctor;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/res/AssetManager.java",
    "content": "package mirror.android.content.res;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class AssetManager {\n    public static Class<?> TYPE = RefClass.load(AssetManager.class, android.content.res.AssetManager.class);\n    public static RefConstructor<android.content.res.AssetManager> ctor;\n    @MethodParams(String.class)\n    public static RefMethod<Integer> addAssetPath;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/content/res/CompatibilityInfo.java",
    "content": "package mirror.android.content.res;\n\nimport android.content.pm.ApplicationInfo;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.MethodParams;\nimport mirror.RefStaticObject;\n\npublic class CompatibilityInfo {\n    public static Class<?> TYPE = RefClass.load(CompatibilityInfo.class, \"android.content.res.CompatibilityInfo\");\n    @MethodParams({ApplicationInfo.class, int.class, int.class, boolean.class})\n    public static RefConstructor ctor;\n    public static RefStaticObject<Object> DEFAULT_COMPATIBILITY_INFO;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/ddm/DdmHandleAppName.java",
    "content": "package mirror.android.ddm;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class DdmHandleAppName {\n    public static Class Class = RefClass.load(DdmHandleAppName.class, \"android.ddm.DdmHandleAppName\");\n    @MethodParams({String.class})\n    public static RefStaticMethod<Void> setAppName;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/ddm/DdmHandleAppNameJBMR1.java",
    "content": "package mirror.android.ddm;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class DdmHandleAppNameJBMR1 {\n    public static Class Class = RefClass.load(DdmHandleAppNameJBMR1.class, \"android.ddm.DdmHandleAppName\");\n    @MethodParams({String.class, int.class})\n    public static RefStaticMethod<Void> setAppName;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/graphics/drawable/Icon.java",
    "content": "package mirror.android.graphics.drawable;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n@TargetApi(Build.VERSION_CODES.M)\npublic class Icon {\n    public static final int TYPE_BITMAP   = 1;\n    public static final int TYPE_RESOURCE = 2;\n    public static final int TYPE_DATA     = 3;\n    public static final int TYPE_URI      = 4;\n\n    public static Class<?> TYPE = RefClass.load(Icon.class, android.graphics.drawable.Icon.class);\n    public static RefObject<Object> mObj1;\n    public static RefObject<String> mString1;\n    public static RefObject<Integer> mType;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/hardware/display/DisplayManagerGlobal.java",
    "content": "package mirror.android.hardware.display;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.RefStaticMethod;\n\npublic class DisplayManagerGlobal {\n    public static Class<?> TYPE = RefClass.load(DisplayManagerGlobal.class, \"android.hardware.display.DisplayManagerGlobal\");\n    public static RefStaticMethod<Object> getInstance;\n    public static RefObject<IInterface> mDm;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/hardware/display/IDisplayManager.java",
    "content": "package mirror.android.hardware.display;\n\nimport mirror.RefClass;\n\npublic class IDisplayManager {\n    public static Class<?> TYPE = RefClass.load(IDisplayManager.class, \"android.hardware.display.IDisplayManager\");\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/hardware/location/IContextHubService.java",
    "content": "package mirror.android.hardware.location;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\npublic class IContextHubService {\n    public static Class<?> TYPE = RefClass.load(IContextHubService.class, \"android.hardware.location.IContextHubService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.hardware.location.IContextHubService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/location/ILocationListener.java",
    "content": "package mirror.android.location;\n\nimport android.location.Location;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefStaticMethod;\n\n\n/**\n * @author legency\n */\npublic class ILocationListener {\n    public static Class<?> TYPE = RefClass.load(ILocationListener.class, \"android.location.ILocationListener\");\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.location.ILocationListener$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n    @MethodParams(Location.class)\n    public static RefMethod<Void> onLocationChanged;\n\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/location/ILocationManager.java",
    "content": "package mirror.android.location;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class ILocationManager {\n    public static Class<?> TYPE = RefClass.load(ILocationManager.class, \"android.location.ILocationManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.location.ILocationManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/location/LocationRequestL.java",
    "content": "package mirror.android.location;\n\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefObject;\n\npublic class LocationRequestL {\n    public static Class<?> TYPE = RefClass.load(LocationRequestL.class, \"android.location.LocationRequest\");\n    public static RefBoolean mHideFromAppOps;\n    public static RefObject<Object> mWorkSource;\n    public static RefObject<String> mProvider;\n    public static RefMethod<String> getProvider;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/media/AudioManager.java",
    "content": "package mirror.android.media;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\nimport mirror.RefStaticMethod;\n\npublic class AudioManager {\n    public static Class<?> TYPE = RefClass.load(AudioManager.class, android.media.AudioManager.class);\n    public static RefStaticMethod getService;\n    public static RefStaticObject<IInterface> sService;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/media/IAudioService.java",
    "content": "package mirror.android.media;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IAudioService {\n    public static Class<?> TYPE = RefClass.load(IAudioService.class, \"android.media.IAudioService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.media.IAudioService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/media/IMediaRouterService.java",
    "content": "package mirror.android.media;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IMediaRouterService {\n    public static Class<?> TYPE = RefClass.load(IMediaRouterService.class, \"android.media.IMediaRouterService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.media.IMediaRouterService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/media/MediaRouter.java",
    "content": "package mirror.android.media;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.RefStaticObject;\n\npublic class MediaRouter {\n    public static Class<?> TYPE = RefClass.load(MediaRouter.class, android.media.MediaRouter.class);\n    public static RefStaticObject sStatic;\n\n    public static class Static {\n        public static Class<?> TYPE = RefClass.load(Static.class, \"android.media.MediaRouter$Static\");\n        public static RefObject<IInterface> mAudioService;\n    }\n\n    public static class StaticKitkat {\n        public static Class<?> TYPE = RefClass.load(StaticKitkat.class, \"android.media.MediaRouter$Static\");\n        public static RefObject<IInterface> mMediaRouterService;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/media/session/ISessionManager.java",
    "content": "package mirror.android.media.session;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class ISessionManager {\n    public static Class<?> TYPE = RefClass.load(ISessionManager.class, \"android.media.session.ISessionManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.media.session.ISessionManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/net/IConnectivityManager.java",
    "content": "package mirror.android.net;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n\n/**\n * @author Junelegency\n *\n */\npublic class IConnectivityManager {\n    public static Class<?> TYPE = RefClass.load(IConnectivityManager.class, \"android.net.IConnectivityManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.net.IConnectivityManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/net/NetworkInfo.java",
    "content": "package mirror.android.net;\n\nimport mirror.MethodParams;\nimport mirror.RefBoolean;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\npublic class NetworkInfo {\n    public static Class<?> TYPE = RefClass.load(NetworkInfo.class, android.net.NetworkInfo.class);\n    @MethodParams({int.class, int.class, String.class, String.class})\n    public static RefConstructor<android.net.NetworkInfo> ctor;\n    @MethodParams({int.class})\n    public static RefConstructor<android.net.NetworkInfo> ctorOld;\n    public static RefInt mNetworkType;\n    public static RefObject<String> mTypeName;\n    public static RefObject<android.net.NetworkInfo.State> mState;\n    public static RefObject<android.net.NetworkInfo.DetailedState> mDetailedState;\n    public static RefBoolean mIsAvailable;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/net/wifi/IWifiManager.java",
    "content": "package mirror.android.net.wifi;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IWifiManager {\n    public static Class<?> TYPE = RefClass.load(IWifiManager.class, \"android.net.wifi.IWifiManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.net.wifi.IWifiManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/net/wifi/WifiInfo.java",
    "content": "package mirror.android.net.wifi;\n\nimport android.net.wifi.SupplicantState;\n\nimport java.net.InetAddress;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\nimport mirror.RefObject;\n\npublic class WifiInfo {\n    public static Class<?> TYPE = RefClass.load(WifiInfo.class, android.net.wifi.WifiInfo.class);\n    public static RefConstructor<android.net.wifi.WifiInfo> ctor;\n    public static RefObject<String> mMacAddress;\n    public static RefInt mNetworkId;\n    public static RefInt mLinkSpeed;\n    public static RefInt mFrequency;\n    public static RefInt mRssi;\n    public static RefObject<SupplicantState> mSupplicantState;\n    public static RefObject<InetAddress> mIpAddress;\n    public static RefObject<Object> mWifiSsid;\n    public static RefObject<String> mBSSID;\n    public static RefObject<String> mSSID;\n\n\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/net/wifi/WifiScanner.java",
    "content": "package mirror.android.net.wifi;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\npublic final class WifiScanner {\n    public static Class<?> Class;\n    public static RefStaticObject<String> GET_AVAILABLE_CHANNELS_EXTRA;\n\n    static {\n        RefClass.load(WifiScanner.class, \"android.net.wifi.WifiScanner\");\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/net/wifi/WifiSsid.java",
    "content": "package mirror.android.net.wifi;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\n\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.KITKAT)\npublic class WifiSsid {\n    public static final Class<?> TYPE = RefClass.load(WifiSsid.class, \"android.net.wifi.WifiSsid\");\n    public static RefStaticMethod<Object> createFromAsciiEncoded;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/Build.java",
    "content": "package mirror.android.os;\n\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\npublic class Build {\n    public static Class<?> TYPE = RefClass.load(Build.class, android.os.Build.class);\n    public static RefStaticObject<String> DEVICE;\n    public static RefStaticObject<String> SERIAL;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/Bundle.java",
    "content": "package mirror.android.os;\n\nimport android.os.IBinder;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class Bundle {\n    public static Class<?> TYPE = RefClass.load(Bundle.class, android.os.Bundle.class);\n\n    @MethodParams({String.class, IBinder.class})\n    public static RefMethod<Void> putIBinder;\n\n    @MethodParams({String.class})\n    public static RefMethod<IBinder> getIBinder;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/Handler.java",
    "content": "package mirror.android.os;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class Handler {\n    public static Class<?> TYPE = RefClass.load(Handler.class, \"android.os.Handler\");\n    public static RefObject<android.os.Handler.Callback> mCallback;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/INetworkManagementService.java",
    "content": "package mirror.android.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class INetworkManagementService {\n    public static Class<?> TYPE = RefClass.load(INetworkManagementService.class, \"android.os.INetworkManagementService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.os.INetworkManagementService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/IPowerManager.java",
    "content": "package mirror.android.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class IPowerManager {\n    public static Class<?> TYPE = RefClass.load(IPowerManager.class, \"android.os.IPowerManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.os.IPowerManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/IUserManager.java",
    "content": "package mirror.android.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IUserManager {\n    public static Class<?> TYPE = RefClass.load(IUserManager.class, \"android.os.IUserManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.os.IUserManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/Message.java",
    "content": "package mirror.android.os;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefStaticMethod;\n\npublic class Message {\n    public static Class<?> TYPE = RefClass.load(Message.class, android.os.Message.class);\n    @MethodParams({int.class})\n    public static RefStaticMethod<Void> updateCheckRecycle;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/Process.java",
    "content": "package mirror.android.os;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class Process {\n    public static Class<?> TYPE = RefClass.load(Process.class, android.os.Process.class);\n    @MethodParams({String.class})\n    public static RefStaticMethod<Void> setArgV0;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/ServiceManager.java",
    "content": "package mirror.android.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport java.util.Map;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticObject;\nimport mirror.RefStaticMethod;\n\npublic class ServiceManager {\n    public static Class<?> TYPE = RefClass.load(ServiceManager.class, \"android.os.ServiceManager\");\n    @MethodParams({String.class, IBinder.class})\n    public static RefStaticMethod<Void> addService;\n    public static RefStaticMethod<IBinder> checkService;\n    public static RefStaticMethod<IInterface> getIServiceManager;\n    public static RefStaticMethod<IBinder> getService;\n    public static RefStaticMethod<String[]> listServices;\n    public static RefStaticObject<Map<String, IBinder>> sCache;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/StrictMode.java",
    "content": "package mirror.android.os;\n\n\nimport mirror.RefClass;\nimport mirror.RefStaticInt;\n\npublic class StrictMode {\n    public static Class<?> TYPE = RefClass.load(StrictMode.class, \"android.os.StrictMode\");\n    public static RefStaticInt sVmPolicyMask;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/os/mount/IMountService.java",
    "content": "package mirror.android.os.mount;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IMountService {\n    public static Class<?> TYPE = RefClass.load(IMountService.class, \"android.os.storage.IMountService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.os.storage.IMountService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/providers/Settings.java",
    "content": "package mirror.android.providers;\n\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\npublic class Settings {\n    public static Class<?> TYPE = RefClass.load(Settings.class, android.provider.Settings.class);\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    public static class Global {\n        public static Class<?> TYPE = RefClass.load(Global.class, android.provider.Settings.Global.class);\n        public static RefStaticObject<Object> sNameValueCache;\n    }\n\n    public static class NameValueCache {\n        public static Class<?> TYPE = RefClass.load(NameValueCache.class, \"android.provider.Settings$NameValueCache\");\n        public static RefObject<Object> mContentProvider;\n    }\n\n    public static class NameValueCacheOreo {\n        public static Class<?> TYPE = RefClass.load(NameValueCacheOreo.class, \"android.provider.Settings$NameValueCache\");\n        public static RefObject<Object> mProviderHolder;\n    }\n\n    public static class ContentProviderHolder {\n        public static Class<?> TYPE = RefClass.load(ContentProviderHolder.class, \"android.provider.Settings$ContentProviderHolder\");\n        public static RefObject<IInterface> mContentProvider;\n    }\n\n    public static class Secure {\n        public static Class<?> TYPE = RefClass.load(Secure.class, android.provider.Settings.Secure.class);\n        public static RefStaticObject<Object> sNameValueCache;\n    }\n\n    public static class System {\n        public static Class<?> TYPE = RefClass.load(System.class, android.provider.Settings.System.class);\n        public static RefStaticObject<Object> sNameValueCache;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/renderscript/RenderScriptCacheDir.java",
    "content": "package mirror.android.renderscript;\n\n\nimport java.io.File;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class RenderScriptCacheDir {\n    public static Class<?> TYPE = RefClass.load(RenderScriptCacheDir.class, \"android.renderscript.RenderScriptCacheDir\");\n    @MethodParams({File.class})\n    public static RefStaticMethod<Void> setupDiskCache;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/rms/resource/ReceiverResourceLP.java",
    "content": "package mirror.android.rms.resource;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ReceiverResourceLP {\n    public static Class<?> TYPE = RefClass.load(ReceiverResourceLP.class, \"android.rms.resource.ReceiverResource\");\n    public static RefObject<Object> mResourceConfig;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/rms/resource/ReceiverResourceM.java",
    "content": "package mirror.android.rms.resource;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ReceiverResourceM {\n    public static Class<?> TYPE = RefClass.load(ReceiverResourceM.class, \"android.rms.resource.ReceiverResource\");\n    public static RefObject<String[]> mWhiteList;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/rms/resource/ReceiverResourceN.java",
    "content": "package mirror.android.rms.resource;\n\n\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class ReceiverResourceN {\n    public static Class<?> TYPE = RefClass.load(ReceiverResourceN.class, \"android.rms.resource.ReceiverResource\");\n    public static RefObject<List<String>> mWhiteList;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/service/persistentdata/IPersistentDataBlockService.java",
    "content": "package mirror.android.service.persistentdata;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IPersistentDataBlockService {\n    public static Class<?> TYPE = RefClass.load(IPersistentDataBlockService.class, \"android.service.persistentdata.IPersistentDataBlockService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.service.persistentdata.IPersistentDataBlockService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/util/Singleton.java",
    "content": "package mirror.android.util;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\nimport mirror.RefMethod;\n\npublic class Singleton {\n    public static Class<?> TYPE = RefClass.load(Singleton.class, \"android.util.Singleton\");\n    public static RefMethod<Object> get;\n    public static RefObject<Object> mInstance;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/Display.java",
    "content": "package mirror.android.view;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\npublic class Display {\n    public static Class<?> TYPE = RefClass.load(Display.class, android.view.Display.class);\n    public static RefStaticObject<IInterface> sWindowManager;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/HardwareRenderer.java",
    "content": "package mirror.android.view;\n\n\nimport java.io.File;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class HardwareRenderer {\n    public static Class<?> TYPE = RefClass.load(HardwareRenderer.class, \"android.view.HardwareRenderer\");\n    @MethodParams({File.class})\n    public static RefStaticMethod<Void> setupDiskCache;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/IGraphicsStats.java",
    "content": "package mirror.android.view;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IGraphicsStats {\n    public static Class<?> TYPE = RefClass.load(IGraphicsStats.class, \"android.view.IGraphicsStats\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.view.IGraphicsStats$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/IWindowManager.java",
    "content": "package mirror.android.view;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IWindowManager {\n    public static Class<?> TYPE = RefClass.load(IWindowManager.class, \"android.view.IWindowManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.view.IWindowManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/RenderScript.java",
    "content": "package mirror.android.view;\n\nimport java.io.File;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class RenderScript {\n    public static Class<?> TYPE = RefClass.load(RenderScript.class, android.renderscript.RenderScript.class);\n    @MethodParams({File.class})\n    public static RefStaticMethod<Void> setupDiskCache;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/SurfaceControl.java",
    "content": "package mirror.android.view;\n\nimport android.graphics.Bitmap;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class SurfaceControl {\n\n    public static Class<?> TYPE = RefClass.load(SurfaceControl.class, \"android.view.SurfaceControl\");\n\n    @MethodParams({int.class/*width*/, int.class/*height*/})\n    public static RefStaticMethod<Bitmap> screnshot;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/ThreadedRenderer.java",
    "content": "package mirror.android.view;\n\n\nimport java.io.File;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class ThreadedRenderer {\n    public static Class<?> TYPE = RefClass.load(ThreadedRenderer.class, \"android.view.ThreadedRenderer\");\n    @MethodParams({File.class})\n    public static RefStaticMethod<Void> setupDiskCache;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/android/view/WindowManagerGlobal.java",
    "content": "package mirror.android.view;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\npublic class WindowManagerGlobal {\n    public static Class<?> TYPE = RefClass.load(WindowManagerGlobal.class, \"android.view.WindowManagerGlobal\");\n    public static RefStaticObject<IInterface> sWindowManagerService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/webkit/IWebViewUpdateService.java",
    "content": "package mirror.android.webkit;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\n/**\n * @author CodeHz\n */\n\npublic class IWebViewUpdateService {\n    public static Class<?> TYPE = RefClass.load(IWebViewUpdateService.class, \"android.webkit.IWebViewUpdateService$Stub$Proxy\");\n\n    public static RefMethod<String> getCurrentWebViewPackageName;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/webkit/WebViewFactory.java",
    "content": "package mirror.android.webkit;\n\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author CodeHz\n */\n\npublic class WebViewFactory {\n\tpublic static Class<?> TYPE = RefClass.load(WebViewFactory.class, \"android.webkit.WebViewFactory\");\n\tpublic static RefStaticMethod<Object> getUpdateService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/widget/RemoteViews.java",
    "content": "package mirror.android.widget;\n\nimport android.content.pm.ApplicationInfo;\n\nimport java.util.ArrayList;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\npublic class RemoteViews {\n    public static Class<?> TYPE = RefClass.load(RemoteViews.class, android.widget.RemoteViews.class);\n    public static RefObject<ApplicationInfo> mApplication;\n    public static RefObject<ArrayList<Object>> mActions;\n    public static RefObject<String> mPackage;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/android/widget/Toast.java",
    "content": "package mirror.android.widget;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\npublic class Toast {\n    public static Class<?> TYPE = RefClass.load(Toast.class, android.widget.Toast.class);\n\n    public static RefStaticObject<IInterface> sService;\n\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/R_Hide.java",
    "content": "package mirror.com.android.internal;\n\nimport mirror.RefClass;\nimport mirror.RefStaticInt;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\n\npublic final class R_Hide {\n\n    public static Class<?> TYPE = RefClass.load(R_Hide.class, \"com.android.internal.R\");\n\n    public static class id {\n        public static Class<?> TYPE = RefClass.load(id.class, \"com.android.internal.R$id\");\n        public static RefStaticInt icon;\n        public static RefStaticInt contentPanel;\n        public static RefStaticInt topPanel;\n        public static RefStaticInt buttonPanel;\n        public static RefStaticInt customPanel;\n        public static RefStaticInt custom;\n        public static RefStaticInt titleDivider;\n        public static RefStaticInt titleDividerTop;\n        public static RefStaticInt title_template;\n        public static RefStaticInt scrollView;\n        public static RefStaticInt alertTitle;\n        public static RefStaticInt message;\n        public static RefStaticInt button1;\n        public static RefStaticInt button2;\n        public static RefStaticInt button3;\n        public static RefStaticInt text1;\n        public static RefStaticInt text2;\n        public static RefStaticInt leftSpacer;\n        public static RefStaticInt rightSpacer;\n        public static RefStaticInt resolver_list;\n    }\n\n    public static class layout {\n        public static Class<?> TYPE = RefClass.load(id.class, \"com.android.internal.R$layout\");\n        public static RefStaticInt resolver_list;\n    }\n\n    public static class drawable {\n        public static Class<?> TYPE = RefClass.load(id.class, \"com.android.internal.R$drawable\");\n        public static RefStaticInt popup_full_dark;\n        public static RefStaticInt popup_top_dark;\n        public static RefStaticInt popup_bottom_dark;\n        public static RefStaticInt popup_full_bright;\n        public static RefStaticInt popup_top_bright;\n        public static RefStaticInt popup_center_bright;\n        public static RefStaticInt popup_bottom_bright;\n        public static RefStaticInt popup_bottom_medium;\n        public static RefStaticInt popup_center_dark;\n    }\n\n    public static class styleable {\n\n        public static Class<?> TYPE = RefClass.load(styleable.class, \"com.android.internal.R$styleable\");\n\n        public static RefStaticObject<int[]> AccountAuthenticator;\n        public static RefStaticInt AccountAuthenticator_accountPreferences;\n        public static RefStaticInt AccountAuthenticator_accountType;\n        public static RefStaticInt AccountAuthenticator_customTokens;\n        public static RefStaticInt AccountAuthenticator_icon;\n        public static RefStaticInt AccountAuthenticator_label;\n        public static RefStaticInt AccountAuthenticator_smallIcon;\n        public static RefStaticObject<int[]> SyncAdapter;\n        public static RefStaticInt SyncAdapter_accountType;\n        public static RefStaticInt SyncAdapter_allowParallelSyncs;\n        public static RefStaticInt SyncAdapter_contentAuthority;\n        public static RefStaticInt SyncAdapter_isAlwaysSyncable;\n        public static RefStaticInt SyncAdapter_settingsActivity;\n        public static RefStaticInt SyncAdapter_supportsUploading;\n        public static RefStaticInt SyncAdapter_userVisible;\n        public static RefStaticObject<int[]> Window;\n        public static RefStaticInt Window_windowBackground;\n        public static RefStaticInt Window_windowFullscreen;\n        public static RefStaticInt Window_windowIsFloating;\n        public static RefStaticInt Window_windowIsTranslucent;\n        public static RefStaticInt Window_windowShowWallpaper;\n        public static RefStaticInt AlertDialog_fullDark;\n        public static RefStaticInt AlertDialog_topDark;\n        public static RefStaticInt AlertDialog_centerDark;\n        public static RefStaticInt AlertDialog_bottomDark;\n        public static RefStaticInt AlertDialog_fullBright;\n        public static RefStaticInt AlertDialog_topBright;\n        public static RefStaticInt AlertDialog_centerBright;\n        public static RefStaticInt AlertDialog_bottomBright;\n        public static RefStaticInt AlertDialog_bottomMedium;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/app/IAppOpsService.java",
    "content": "package mirror.com.android.internal.app;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IAppOpsService {\n    public static Class<?> TYPE = RefClass.load(IAppOpsService.class, \"com.android.internal.app.IAppOpsService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.app.IAppOpsService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/appwidget/IAppWidgetService.java",
    "content": "package mirror.com.android.internal.appwidget;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IAppWidgetService {\n    public static Class<?> TYPE = RefClass.load(IAppWidgetService.class, \"com.android.internal.appwidget.IAppWidgetService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.appwidget.IAppWidgetService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/content/NativeLibraryHelper.java",
    "content": "package mirror.com.android.internal.content;\n\nimport java.io.File;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class NativeLibraryHelper {\n    public static Class<?> TYPE = RefClass.load(NativeLibraryHelper.class, \"com.android.internal.content.NativeLibraryHelper\");\n\n    @MethodParams({Handle.class, File.class, String.class})\n    public static RefStaticMethod<Integer> copyNativeBinaries;\n\n    @MethodParams({Handle.class, String[].class})\n    public static RefStaticMethod<Integer> findSupportedAbi;\n\n    public static class Handle {\n        public static Class<?> TYPE = RefClass.load(Handle.class, \"com.android.internal.content.NativeLibraryHelper$Handle\");\n\n        @MethodParams({File.class})\n        public static RefStaticMethod<Object> create;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/content/ReferrerIntent.java",
    "content": "package mirror.com.android.internal.content;\n\nimport android.content.Intent;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.MethodParams;\n\n/**\n * @author Lody\n */\n\npublic class ReferrerIntent {\n    public static Class<?> TYPE = RefClass.load(ReferrerIntent.class, \"com.android.internal.content.ReferrerIntent\");\n    @MethodParams({Intent.class, String.class})\n    public static RefConstructor<Intent> ctor;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/os/IDropBoxManagerService.java",
    "content": "package mirror.com.android.internal.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IDropBoxManagerService {\n    public static Class<?> TYPE = RefClass.load(IDropBoxManagerService.class, \"com.android.internal.os.IDropBoxManagerService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.os.IDropBoxManagerService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/os/IVibratorService.java",
    "content": "package mirror.com.android.internal.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IVibratorService {\n    public static Class<?> TYPE = RefClass.load(IVibratorService.class, \"android.os.IVibratorService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.os.IVibratorService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/os/UserManager.java",
    "content": "package mirror.com.android.internal.os;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class UserManager {\n    public static Class<?> TYPE = RefClass.load(UserManager.class, \"android.os.UserManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.os.IUserManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/policy/PhoneWindow.java",
    "content": "package mirror.com.android.internal.policy;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\npublic class PhoneWindow {\n    public static Class<?> TYPE;\n    public static RefStaticObject<IInterface> sWindowManager;\n\n    static {\n        TYPE = RefClass.load(PhoneWindow.class, \"com.android.internal.policy.impl.PhoneWindow$WindowManagerHolder\");\n        if (TYPE == null) {\n            TYPE = RefClass.load(PhoneWindow.class, \"com.android.internal.policy.PhoneWindow$WindowManagerHolder\");\n        }\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/IMms.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IMms {\n    public static Class<?> TYPE = RefClass.load(IMms.class, \"com.android.internal.telephony.IMms\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.telephony.IMms$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/IPhoneSubInfo.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class IPhoneSubInfo {\n    public static Class<?> TYPE = RefClass.load(IPhoneSubInfo.class, \"com.android.internal.telephony.IPhoneSubInfo\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.telephony.IPhoneSubInfo$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/ISms.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class ISms {\n    public static Class<?> TYPE = RefClass.load(ISms.class, \"com.android.internal.telephony.ISms\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.telephony.ISms$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/ISub.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class ISub {\n    public static Class<?> TYPE = RefClass.load(ISub.class, \"com.android.internal.telephony.ISub\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.telephony.ISub$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/ITelephony.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\npublic class ITelephony {\n    public static Class<?> TYPE = RefClass.load(ITelephony.class, \"com.android.internal.telephony.ITelephony\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"com.android.internal.telephony.ITelephony$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/ITelephonyRegistry.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class ITelephonyRegistry {\n\tpublic static Class<?> TYPE = RefClass.load(ITelephonyRegistry.class, \"com.android.internal.telephony.ITelephonyRegistry\");\n\n\tpublic static class Stub {\n\t\tpublic static Class<?> TYPE = RefClass.load(ITelephonyRegistry.Stub.class, \"com.android.internal.telephony.ITelephonyRegistry$Stub\");\n\t\t@MethodParams({IBinder.class})\n\t\tpublic static RefStaticMethod<IInterface> asInterface;\n\t}\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/telephony/PhoneConstantsMtk.java",
    "content": "package mirror.com.android.internal.telephony;\n\nimport mirror.RefClass;\nimport mirror.RefStaticInt;\n\npublic class PhoneConstantsMtk {\n    public static Class<?> TYPE = RefClass.load(PhoneConstantsMtk.class, \"com.android.internal.telephony.PhoneConstants\");\n    public static RefStaticInt GEMINI_SIM_NUM;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/view/IInputMethodManager.java",
    "content": "package mirror.com.android.internal.view;\n\nimport mirror.RefClass;\n\npublic class IInputMethodManager {\n    public static Class<?> TYPE = RefClass.load(IInputMethodManager.class, \"com.android.internal.view.IInputMethodManager\");\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/com/android/internal/view/inputmethod/InputMethodManager.java",
    "content": "package mirror.com.android.internal.view.inputmethod;\n\nimport android.os.IInterface;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class InputMethodManager {\n    public static Class<?> TYPE = RefClass.load(InputMethodManager.class, android.view.inputmethod.InputMethodManager.class);\n    public static RefObject<IInterface> mService;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/dalvik/system/VMRuntime.java",
    "content": "package mirror.dalvik.system;\n\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.MethodParams;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class VMRuntime {\n        public static Class<?> TYPE = RefClass.load(VMRuntime.class, \"dalvik.system.VMRuntime\");\n        public static RefStaticMethod<Object> getRuntime;\n        @MethodParams({int.class})\n        public static RefMethod<Void> setTargetSdkVersion;\n        public static RefMethod<Boolean> is64Bit;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/java/lang/ThreadGroup.java",
    "content": "package mirror.java.lang;\n\n\nimport java.util.List;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\npublic class ThreadGroup {\n    public static Class<?> TYPE = RefClass.load(ThreadGroup.class, java.lang.ThreadGroup.class);\n    public static RefObject<List<java.lang.ThreadGroup>> groups;\n    public static RefObject<java.lang.ThreadGroup> parent;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/java/lang/ThreadGroupN.java",
    "content": "package mirror.java.lang;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\npublic class ThreadGroupN {\n    public static Class<?> Class = RefClass.load(ThreadGroupN.class, java.lang.ThreadGroup.class);\n    public static RefObject<Integer> ngroups;\n    public static RefObject<java.lang.ThreadGroup[]> groups;\n    public static RefObject<java.lang.ThreadGroup> parent;\n}"
  },
  {
    "path": "lib/src/main/java/mirror/libcore/io/ForwardingOs.java",
    "content": "package mirror.libcore.io;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\npublic class ForwardingOs {\n    public static Class<?> TYPE = RefClass.load(ForwardingOs.class, \"libcore.io.ForwardingOs\");\n    public static RefObject<Object> os;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/libcore/io/Libcore.java",
    "content": "package mirror.libcore.io;\n\nimport mirror.RefClass;\nimport mirror.RefStaticObject;\n\n/**\n * @author Lody\n */\n\npublic class Libcore {\n    public static Class<?> TYPE = RefClass.load(Libcore.class, \"libcore.io.Libcore\");\n\n    public static RefStaticObject<Object> os;\n}\n"
  },
  {
    "path": "lib/src/main/java/mirror/libcore/io/Os.java",
    "content": "package mirror.libcore.io;\n\nimport mirror.RefClass;\n\n/**\n * @author Lody\n */\n\npublic class Os {\n    public static Class<?> TYPE = RefClass.load(Os.class, \"libcore.io.Os\");\n}\n"
  },
  {
    "path": "lib/src/main/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := va-native\n\nLOCAL_CFLAGS := -Wno-error=format-security -fpermissive\nLOCAL_CFLAGS += -fno-rtti -fno-exceptions\n\nLOCAL_C_INCLUDES += $(LOCAL_PATH)\nLOCAL_C_INCLUDES += $(LOCAL_PATH)/Foundation\nLOCAL_C_INCLUDES += $(LOCAL_PATH)/MSHook\nLOCAL_C_INCLUDES += $(LOCAL_PATH)/GodinHook\n\nifeq ($(TARGET_ARCH_ABI),x86)\n    ARCH_FILES := \\\n        MSHook/MSHook.cpp \\\n        MSHook/x86_64.cpp \\\n        MSHook/ARM.cpp \\\n        MSHook/Debug.cpp \\\n        MSHook/Hooker.cpp \\\n        MSHook/PosixMemory.cpp \\\n        MSHook/Thumb.cpp \\\n        MSHook/util.cpp \\\n        MSHook/x86.cpp \\\n\nelse ifeq ($(TARGET_ARCH_ABI),x86_64)\n    ARCH_FILES := \\\n        MSHook/MSHook.cpp \\\n        MSHook/x86_64.cpp \\\n        MSHook/ARM.cpp \\\n        MSHook/Debug.cpp \\\n        MSHook/Hooker.cpp \\\n        MSHook/PosixMemory.cpp \\\n        MSHook/Thumb.cpp \\\n        MSHook/util.cpp \\\n        MSHook/x86.cpp \\\n\nelse\n    ARCH_FILES := \\\n        GodinHook/mem_helper.cpp \\\n        GodinHook/instruction/instruction_helper.cpp \\\n        GodinHook/instruction/arm_instruction.cpp \\\n        GodinHook/instruction/thumb_instruction.cpp \\\n        GodinHook/native_hook.cpp \\\n        GodinHook/thread_helper.cpp \\\n        MSHook/MSHook.cpp \\\n        MSHook/x86_64.cpp \\\n        MSHook/ARM.cpp \\\n        MSHook/Debug.cpp \\\n        MSHook/Hooker.cpp \\\n        MSHook/PosixMemory.cpp \\\n        MSHook/Thumb.cpp \\\n        MSHook/util.cpp \\\n        MSHook/x86.cpp \\\n\nendif\n\n\nLOCAL_SRC_FILES := Core.cpp \\\n\t\t\t\t   Foundation/IOUniformer.cpp \\\n\t\t\t\t   Foundation/VMPatch.cpp \\\n\t\t\t\t   $(ARCH_FILES) \\\n\n\nLOCAL_LDLIBS := -llog\n\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "lib/src/main/jni/Application.mk",
    "content": "APP_ABI := armeabi armeabi-v7a x86\nAPP_PLATFORM := android-14\nAPP_STL := stlport_static\nAPP_OPTIM := release"
  },
  {
    "path": "lib/src/main/jni/Core.cpp",
    "content": "//\n// VirtualApp Native Project\n//\n#include \"Core.h\"\n\n\nJavaVM *gVm;\njclass gClass;\n\n\nvoid Java_nativeHookNative(JNIEnv *env, jclass jclazz, jobjectArray javaMethods,\n                           jstring packageName,\n                           jboolean isArt, jint apiLevel, jint cameraMethodType) {\n    patchAndroidVM(javaMethods, packageName, isArt, apiLevel, cameraMethodType);\n}\n\n\nvoid Java_nativeStartUniformer(JNIEnv *env, jclass jclazz, jstring selfSoPath, jint apiLevel, jint previewApiLevel) {\n    const char *soPath = env->GetStringUTFChars(selfSoPath, NULL);\n    IOUniformer::saveEnvironment(soPath, apiLevel, previewApiLevel);\n    IOUniformer::startUniformer(apiLevel, previewApiLevel);\n}\n\nvoid Java_nativeReadOnly(JNIEnv *env, jclass jclazz, jstring _path) {\n    const char *path = env->GetStringUTFChars(_path, NULL);\n    IOUniformer::readOnly(path);\n}\n\nvoid Java_nativeRedirect(JNIEnv *env, jclass jclazz, jstring orgPath, jstring newPath) {\n    const char *org_path = env->GetStringUTFChars(orgPath, NULL);\n    const char *new_path = env->GetStringUTFChars(newPath, NULL);\n    IOUniformer::redirect(org_path, new_path);\n}\n\njstring Java_nativeQuery(JNIEnv *env, jclass jclazz, jstring orgPath) {\n    const char *org_path = env->GetStringUTFChars(orgPath, NULL);\n    const char *redirected_path = IOUniformer::query(org_path);\n    return env->NewStringUTF(redirected_path);\n}\n\njstring Java_nativeRestore(JNIEnv *env, jclass jclazz, jstring redirectedPath) {\n    const char *redirected_path = env->GetStringUTFChars(redirectedPath, NULL);\n    const char *org_path = IOUniformer::restore(redirected_path);\n    return env->NewStringUTF(org_path);\n}\n\n\nstatic JNINativeMethod gMethods[] = {\n        NATIVE_METHOD((void *) Java_nativeStartUniformer, \"nativeStartUniformer\", \"(Ljava/lang/String;II)V\"),\n        NATIVE_METHOD((void *) Java_nativeReadOnly, \"nativeReadOnly\", \"(Ljava/lang/String;)V\"),\n        NATIVE_METHOD((void *) Java_nativeRedirect, \"nativeRedirect\",\n                      \"(Ljava/lang/String;Ljava/lang/String;)V\"),\n        NATIVE_METHOD((void *) Java_nativeQuery, \"nativeGetRedirectedPath\",\n                      \"(Ljava/lang/String;)Ljava/lang/String;\"),\n        NATIVE_METHOD((void *) Java_nativeRestore, \"nativeRestoreRedirectedPath\",\n                      \"(Ljava/lang/String;)Ljava/lang/String;\"),\n\n        NATIVE_METHOD((void *) Java_nativeHookNative, \"nativeHookNative\",\n                      \"(Ljava/lang/Object;Ljava/lang/String;ZII)V\"),\n};\n\n\nJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {\n    JNIEnv *env;\n    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {\n        return JNI_ERR;\n    }\n    jclass javaClass = env->FindClass(JAVA_CLASS);\n    if (javaClass == NULL) {\n        LOGE(\"Error: Unable to find the IOHook class.\");\n        return JNI_ERR;\n    }\n    if (env->RegisterNatives(javaClass, gMethods, NELEM(gMethods)) < 0) {\n        LOGE(\"Error: Unable to register the native methods.\");\n        return JNI_ERR;\n    }\n    gVm = vm;\n    gClass = (jclass) env->NewGlobalRef(javaClass);\n    env->DeleteLocalRef(javaClass);\n    return JNI_VERSION_1_6;\n}\n\nextern \"C\" void _init(void) {\n    IOUniformer::init_array();\n}\n\nJNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {\n    JNIEnv *env;\n    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {\n        return;\n    }\n    env->DeleteGlobalRef((jobject) gVm);\n    env->DeleteGlobalRef((jobject) gClass);\n}\n\n"
  },
  {
    "path": "lib/src/main/jni/Core.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef NDK_CORE_H\n#define NDK_CORE_H\n\n#include <jni.h>\n#include <stdlib.h>\n\n\n#include \"Helper.h\"\n#include \"Foundation/VMPatch.h\"\n#include \"Foundation/IOUniformer.h\"\n\n__BEGIN_DECLS\nJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);\nJNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);\n__END_DECLS\n\n\n#endif //NDK_CORE_H\n"
  },
  {
    "path": "lib/src/main/jni/Foundation/IOUniformer.cpp",
    "content": "//\n// VirtualApp Native Project\n//\n#include <util.h>\n#include \"IOUniformer.h\"\n#include \"native_hook.h\"\n\nstatic list<std::string> ReadOnlyPathMap;\nstatic std::map<std::string/*orig_path*/, std::string/*new_path*/> IORedirectMap;\nstatic std::map<std::string/*orig_path*/, std::string/*new_path*/> RootIORedirectMap;\n\nstatic struct {\n    const char *selfSoPath;\n    int api_level;\n    int preview_api_level;\n    bool hooked_process;\n} gVars;\n\n\nvoid IOUniformer::init_array() {\n    // TODO: init hook for child process\n//    if (!gVars.hooked_process) {\n//        gVars.selfSoPath = getenv(\"V_SELF_SO\");;\n//        if (gVars.selfSoPath != NULL) {\n//            LOGE(\"start init child process, io.size = %i\", IORedirectMap.size());\n//            gVars.api_level = atoi(getenv(\"V_API_LEVEL\"));\n//            gVars.preview_api_level = atoi(getenv(\"V_PREVIEW_API_LEVEL\"));\n//            startUniformer(gVars.api_level, gVars.preview_api_level);\n//        }\n//    }\n}\n\nvoid IOUniformer::saveEnvironment(const char *selfSoPath, int api_level, int preview_api_level) {\n    LOGE(\"Saving environment, so : %s, api: %i, io.size : %i.\", selfSoPath, api_level,\n         IORedirectMap.size());\n    gVars.selfSoPath = selfSoPath;\n    gVars.api_level = api_level;\n    gVars.preview_api_level = preview_api_level;\n    char chars[5];\n    char envName[30];\n    char buffer[200];\n    setenv(\"V_SELF_SO\", gVars.selfSoPath, 1);\n    sprintf(chars, \"%i\", api_level);\n    setenv(\"V_API_LEVEL\", chars, 1);\n    memset(chars, sizeof(chars), 0);\n    sprintf(chars, \"%i\", preview_api_level);\n    setenv(\"V_PREVIEW_API_LEVEL\", chars, 1);\n    std::map<std::string, std::string>::iterator iterator;\n    int i = 0;\n    for (iterator = IORedirectMap.begin(); iterator != IORedirectMap.end(); iterator++, i++) {\n        const std::string &prefix = iterator->first;\n        const std::string &new_prefix = iterator->second;\n        memset(envName, sizeof(envName), 0);\n        memset(buffer, sizeof(buffer), 0);\n        sprintf(envName, \"V_IO_REDIRECT_%i\", i);\n        sprintf(buffer, \"%s&%s\", prefix.c_str(), new_prefix.c_str());\n        setenv(envName, buffer, 1);\n    }\n    i = 0;\n    list<std::string>::iterator it;\n    for (it = ReadOnlyPathMap.begin(); it != ReadOnlyPathMap.end(); it++, i++) {\n        memset(envName, sizeof(envName), 0);\n        memset(buffer, sizeof(buffer), 0);\n        sprintf(envName, \"V_IO_RO_%i\", i);\n        setenv(envName, it->c_str(), 1);\n    }\n}\n\n/**\n *\n * NOTICE:\n *   We use MSHook to hook symbol on x86 & X64.\n *   But on ARM, we use GodinHook to instead of it.\n */\nstatic inline void\nhook_template(void *handle, const char *symbol, void *new_func, void **old_func) {\n    void *addr = dlsym(handle, symbol);\n    if (addr == NULL) {\n        LOGW(\"Error: unable to find the Symbol : %s.\", symbol);\n        return;\n    }\n#if defined(__i386__) || defined(__x86_64__)\n    inlineHookDirect((unsigned int) (addr), new_func, old_func);\n#else\n    GodinHook::NativeHook::registeredHook((size_t) addr, (size_t) new_func, (size_t **) old_func);\n#endif\n}\n\n\nvoid onSoLoaded(const char *name, void *handle);\n\n\nstatic inline bool startWith(const std::string &str, const std::string &prefix) {\n    return str.compare(0, prefix.length(), prefix) == 0;\n}\n\n\nstatic inline bool endWith(const std::string &str, const char &suffix) {\n    return *(str.end() - 1) == suffix;\n}\n\nstatic void add_pair(const char *_orig_path, const char *_new_path) {\n    std::string origPath = std::string(_orig_path);\n    std::string newPath = std::string(_new_path);\n    IORedirectMap.insert(std::pair<std::string, std::string>(origPath, newPath));\n    if (endWith(origPath, '/')) {\n        RootIORedirectMap.insert(\n                std::pair<std::string, std::string>(\n                        origPath.substr(0, origPath.length() - 1),\n                        newPath.substr(0, newPath.length() - 1))\n        );\n    }\n}\n\n\nconst char *match_redirected_path(const char *_path) {\n    std::string path(_path);\n    if (path.length() <= 1) {\n        return _path;\n    }\n    std::map<std::string, std::string>::iterator iterator;\n    iterator = RootIORedirectMap.find(path);\n    if (iterator != RootIORedirectMap.end()) {\n        return strdup(iterator->second.c_str());\n    }\n\n    for (iterator = IORedirectMap.begin(); iterator != IORedirectMap.end(); iterator++) {\n        const std::string &prefix = iterator->first;\n        const std::string &new_prefix = iterator->second;\n        if (startWith(path, prefix)) {\n            std::string new_path = new_prefix + path.substr(prefix.length(), path.length());\n            return strdup(new_path.c_str());\n        }\n    }\n    return _path;\n}\n\n\nvoid IOUniformer::redirect(const char *orig_path, const char *new_path) {\n    LOGI(\"Start Java_nativeRedirect : from %s to %s\", orig_path, new_path);\n    add_pair(orig_path, new_path);\n}\n\nconst char *IOUniformer::query(const char *orig_path) {\n    return match_redirected_path(orig_path);\n}\n\nvoid IOUniformer::readOnly(const char *_path) {\n    std::string path(_path);\n    ReadOnlyPathMap.push_back(path);\n}\n\nbool isReadOnlyPath(const char *_path) {\n    std::string path(_path);\n    list<std::string>::iterator it;\n    for (it = ReadOnlyPathMap.begin(); it != ReadOnlyPathMap.end(); ++it) {\n        if (startWith(path, *it)) {\n            return true;\n        }\n    }\n    return false;\n}\n\n\nconst char *IOUniformer::restore(const char *_path) {\n    if (_path == NULL) {\n        return NULL;\n    }\n    std::string path(_path);\n    if (path.length() <= 1) {\n        return _path;\n    }\n    std::map<std::string, std::string>::iterator iterator;\n    iterator = RootIORedirectMap.find(path);\n    if (iterator != RootIORedirectMap.end()) {\n        return strdup(iterator->second.c_str());\n    }\n    for (iterator = RootIORedirectMap.begin(); iterator != RootIORedirectMap.end(); iterator++) {\n        const std::string &origin = iterator->first;\n        const std::string &redirected = iterator->second;\n        if (path == redirected) {\n            return strdup(origin.c_str());\n        }\n    }\n\n    for (iterator = IORedirectMap.begin(); iterator != IORedirectMap.end(); iterator++) {\n        const std::string &prefix = iterator->first;\n        const std::string &new_prefix = iterator->second;\n        if (startWith(path, new_prefix)) {\n            std::string origin_path = prefix + path.substr(new_prefix.length(), path.length());\n            return strdup(origin_path.c_str());\n        }\n    }\n    return _path;\n}\n\n\n__BEGIN_DECLS\n\n\n\n// int faccessat(int dirfd, const char *pathname, int mode, int flags);\nHOOK_DEF(int, faccessat, int dirfd, const char *pathname, int mode, int flags) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_faccessat, dirfd, redirect_path, mode, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);\nHOOK_DEF(int, fchmodat, int dirfd, const char *pathname, mode_t mode, int flags) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_fchmodat, dirfd, redirect_path, mode, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// int fchmod(const char *pathname, mode_t mode);\nHOOK_DEF(int, fchmod, const char *pathname, mode_t mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_chmod, redirect_path, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags);\nHOOK_DEF(int, fstatat, int dirfd, const char *pathname, struct stat *buf, int flags) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_fstatat64, dirfd, redirect_path, buf, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// int fstat(const char *pathname, struct stat *buf, int flags);\nHOOK_DEF(int, fstat, const char *pathname, struct stat *buf) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_fstat64, redirect_path, buf);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);\nHOOK_DEF(int, mknodat, int dirfd, const char *pathname, mode_t mode, dev_t dev) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_mknodat, dirfd, redirect_path, mode, dev);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// int mknod(const char *pathname, mode_t mode, dev_t dev);\nHOOK_DEF(int, mknod, const char *pathname, mode_t mode, dev_t dev) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_mknod, redirect_path, mode, dev);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);\nHOOK_DEF(int, utimensat, int dirfd, const char *pathname, const struct timespec times[2],\n         int flags) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_utimensat, dirfd, redirect_path, times, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags);\nHOOK_DEF(int, fchownat, int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_fchownat, dirfd, redirect_path, owner, group, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n// int chroot(const char *pathname);\nHOOK_DEF(int, chroot, const char *pathname) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_chroot, redirect_path);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);\nHOOK_DEF(int, renameat, int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {\n    const char *redirect_path_old = match_redirected_path(oldpath);\n    const char *redirect_path_new = match_redirected_path(newpath);\n    if (isReadOnlyPath(redirect_path_old) || isReadOnlyPath(redirect_path_new)) {\n        return -1;\n    }\n    int ret = syscall(__NR_renameat, olddirfd, redirect_path_old, newdirfd, redirect_path_new);\n    FREE(redirect_path_old, oldpath);\n    FREE(redirect_path_new, newpath);\n    return ret;\n}\n// int rename(const char *oldpath, const char *newpath);\nHOOK_DEF(int, rename, const char *oldpath, const char *newpath) {\n    const char *redirect_path_old = match_redirected_path(oldpath);\n    const char *redirect_path_new = match_redirected_path(newpath);\n    if (isReadOnlyPath(redirect_path_old) || isReadOnlyPath(redirect_path_new)) {\n        return -1;\n    }\n    int ret = syscall(__NR_rename, redirect_path_old, redirect_path_new);\n    FREE(redirect_path_old, oldpath);\n    FREE(redirect_path_new, newpath);\n    return ret;\n}\n\n\n// int unlinkat(int dirfd, const char *pathname, int flags);\nHOOK_DEF(int, unlinkat, int dirfd, const char *pathname, int flags) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_unlinkat, dirfd, redirect_path, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// int unlink(const char *pathname);\nHOOK_DEF(int, unlink, const char *pathname) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_unlink, redirect_path);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int symlinkat(const char *oldpath, int newdirfd, const char *newpath);\nHOOK_DEF(int, symlinkat, const char *oldpath, int newdirfd, const char *newpath) {\n    const char *redirect_path_old = match_redirected_path(oldpath);\n    const char *redirect_path_new = match_redirected_path(newpath);\n    int ret = syscall(__NR_symlinkat, redirect_path_old, newdirfd, redirect_path_new);\n    FREE(redirect_path_old, oldpath);\n    FREE(redirect_path_new, newpath);\n    return ret;\n}\n// int symlink(const char *oldpath, const char *newpath);\nHOOK_DEF(int, symlink, const char *oldpath, const char *newpath) {\n    const char *redirect_path_old = match_redirected_path(oldpath);\n    const char *redirect_path_new = match_redirected_path(newpath);\n    if (isReadOnlyPath(redirect_path_old) || isReadOnlyPath(newpath)) {\n        return -1;\n    }\n    int ret = syscall(__NR_symlink, redirect_path_old, redirect_path_new);\n    FREE(redirect_path_old, oldpath);\n    FREE(redirect_path_new, newpath);\n    return ret;\n}\n\n\n// int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags);\nHOOK_DEF(int, linkat, int olddirfd, const char *oldpath, int newdirfd, const char *newpath,\n         int flags) {\n    const char *redirect_path_old = match_redirected_path(oldpath);\n    const char *redirect_path_new = match_redirected_path(newpath);\n    if (isReadOnlyPath(redirect_path_old) || isReadOnlyPath(newpath)) {\n        return -1;\n    }\n    int ret = syscall(__NR_linkat, olddirfd, redirect_path_old, newdirfd, redirect_path_new, flags);\n    FREE(redirect_path_old, oldpath);\n    FREE(redirect_path_new, newpath);\n    return ret;\n}\n// int link(const char *oldpath, const char *newpath);\nHOOK_DEF(int, link, const char *oldpath, const char *newpath) {\n    const char *redirect_path_old = match_redirected_path(oldpath);\n    const char *redirect_path_new = match_redirected_path(newpath);\n    int ret = syscall(__NR_link, redirect_path_old, redirect_path_new);\n    FREE(redirect_path_old, oldpath);\n    FREE(redirect_path_new, newpath);\n    return ret;\n}\n\n\n// int utimes(const char *filename, const struct timeval *tvp);\nHOOK_DEF(int, utimes, const char *pathname, const struct timeval *tvp) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_utimes, redirect_path, tvp);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int access(const char *pathname, int mode);\nHOOK_DEF(int, access, const char *pathname, int mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (mode & W_OK && isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_access, redirect_path, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int chmod(const char *path, mode_t mode);\nHOOK_DEF(int, chmod, const char *pathname, mode_t mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_chmod, redirect_path, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int chown(const char *path, uid_t owner, gid_t group);\nHOOK_DEF(int, chown, const char *pathname, uid_t owner, gid_t group) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_chown, redirect_path, owner, group);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int lstat(const char *path, struct stat *buf);\nHOOK_DEF(int, lstat, const char *pathname, struct stat *buf) {\n    char *redirect_path = const_cast<char *>(match_redirected_path(pathname));\n    int ret = syscall(__NR_lstat64, redirect_path, buf);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int stat(const char *path, struct stat *buf);\nHOOK_DEF(int, stat, const char *pathname, struct stat *buf) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_stat64, redirect_path, buf);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int mkdirat(int dirfd, const char *pathname, mode_t mode);\nHOOK_DEF(int, mkdirat, int dirfd, const char *pathname, mode_t mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_mkdirat, dirfd, redirect_path, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// int mkdir(const char *pathname, mode_t mode);\nHOOK_DEF(int, mkdir, const char *pathname, mode_t mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_mkdir, redirect_path, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int rmdir(const char *pathname);\nHOOK_DEF(int, rmdir, const char *pathname) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_rmdir, redirect_path);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);\nHOOK_DEF(int, readlinkat, int dirfd, const char *pathname, char *buf, size_t bufsiz) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_readlinkat, dirfd, redirect_path, buf, bufsiz);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// ssize_t readlink(const char *path, char *buf, size_t bufsiz);\nHOOK_DEF(ssize_t, readlink, const char *pathname, char *buf, size_t bufsiz) {\n    const char *redirect_path = match_redirected_path(pathname);\n    ssize_t ret = static_cast<ssize_t>(syscall(__NR_readlink, redirect_path, buf, bufsiz));\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int __statfs64(const char *path, size_t size, struct statfs *stat);\nHOOK_DEF(int, __statfs64, const char *pathname, size_t size, struct statfs *stat) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_statfs64, redirect_path, size, stat);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int truncate(const char *path, off_t length);\nHOOK_DEF(int, truncate, const char *pathname, off_t length) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_truncate, redirect_path, length);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n// int truncate64(const char *pathname, off_t length);\nHOOK_DEF(int, truncate64, const char *pathname, off_t length) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_truncate64, redirect_path, length);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int chdir(const char *path);\nHOOK_DEF(int, chdir, const char *pathname) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_chdir, redirect_path);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int __getcwd(char *buf, size_t size);\nHOOK_DEF(int, __getcwd, char *buf, size_t size) {\n    int ret = syscall(__NR_getcwd, buf, size);\n    return ret;\n}\n\n\n// int __openat(int fd, const char *pathname, int flags, int mode);\nHOOK_DEF(int, __openat, int fd, const char *pathname, int flags, int mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_openat, fd, redirect_path, flags, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n// int __open(const char *pathname, int flags, int mode);\nHOOK_DEF(int, __open, const char *pathname, int flags, int mode) {\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_open, redirect_path, flags, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n// int lchown(const char *pathname, uid_t owner, gid_t group);\nHOOK_DEF(int, lchown, const char *pathname, uid_t owner, gid_t group) {\n    const char *redirect_path = match_redirected_path(pathname);\n    if (isReadOnlyPath(redirect_path)) {\n        return -1;\n    }\n    int ret = syscall(__NR_lchown, redirect_path, owner, group);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n// int (*origin_execve)(const char *pathname, char *const argv[], char *const envp[]);\nHOOK_DEF(int, execve, const char *pathname, char *const argv[], char *const envp[]) {\n\n    for (int i = 0; argv[i] != NULL; ++i) {\n        LOGE(\"argv[%i] : %s\", i, argv[i]);\n    }\n    for (int i = 0; envp[i] != NULL; ++i) {\n        if (!strncmp(envp[i], \"LD_PRELOAD=\", 11)) {\n            char preload_path[200];\n            sprintf(preload_path, \"LD_PRELOAD=%s:%s\", gVars.selfSoPath, envp[i] + 11);\n            const_cast<char **>(envp)[i] = preload_path;\n            break;\n        }\n    }\n    for (int i = 0; envp[i] != NULL; ++i) {\n        LOGE(\"envp[%i] : %s\", i, envp[i]);\n    }\n    const char *redirect_path = match_redirected_path(pathname);\n    int ret = syscall(__NR_execve, redirect_path, argv, envp);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\nHOOK_DEF(void*, dlopen, const char *filename, int flag) {\n    const char *redirect_path = match_redirected_path(filename);\n    void *ret = orig_dlopen(redirect_path, flag);\n    onSoLoaded(filename, ret);\n    LOGD(\"dlopen : %s, return : %p.\", redirect_path, ret);\n    FREE(redirect_path, filename);\n    return ret;\n}\n\nHOOK_DEF(void*, do_dlopen_V19, const char *filename, int flag, const void *extinfo) {\n    const char *redirect_path = match_redirected_path(filename);\n    void *ret = orig_do_dlopen_V19(redirect_path, flag, extinfo);\n    onSoLoaded(filename, ret);\n    LOGD(\"do_dlopen : %s, return : %p.\", redirect_path, ret);\n    FREE(redirect_path, filename);\n    return ret;\n}\n\nHOOK_DEF(void*, do_dlopen_V24, const char *name, int flags, const void *extinfo,\n         void *caller_addr) {\n    const char *redirect_path = match_redirected_path(name);\n    void *ret = orig_do_dlopen_V24(redirect_path, flags, extinfo, caller_addr);\n    onSoLoaded(name, ret);\n    LOGD(\"do_dlopen : %s, return : %p.\", redirect_path, ret);\n    FREE(redirect_path, name);\n    return ret;\n}\n\n\n\n//void *dlsym(void *handle,const char *symbol)\nHOOK_DEF(void*, dlsym, void *handle, char *symbol) {\n    LOGD(\"dlsym : %p %s.\", handle, symbol);\n    return orig_dlsym(handle, symbol);\n}\n\n// int kill(pid_t pid, int sig);\nHOOK_DEF(int, kill, pid_t pid, int sig) {\n    LOGD(\">>>>> kill >>> pid: %d, sig: %d.\", pid, sig);\n    extern JavaVM *gVm;\n    extern jclass gClass;\n    JNIEnv *env = NULL;\n    gVm->GetEnv((void **) &env, JNI_VERSION_1_4);\n    gVm->AttachCurrentThread(&env, NULL);\n    jmethodID method = env->GetStaticMethodID(gClass, \"onKillProcess\", \"(II)V\");\n    env->CallStaticVoidMethod(gClass, method, pid, sig);\n    int ret = syscall(__NR_kill, pid, sig);\n    return ret;\n}\n\nHOOK_DEF(pid_t, vfork) {\n    return fork();\n}\n\n__END_DECLS\n// end IO DEF\n\n\nvoid onSoLoaded(const char *name, void *handle) {\n}\n\n\nvoid hook_dlopen(int api_level) {\n    void *symbol = NULL;\n    if (api_level > 23) {\n        if (findSymbol(\"__dl__Z9do_dlopenPKciPK17android_dlextinfoPv\", \"linker\",\n                       (unsigned long *) &symbol) == 0) {\n            inlineHookDirect((unsigned int) symbol, (void *) new_do_dlopen_V24,\n                             (void **) &orig_do_dlopen_V24);\n        }\n    } else if (api_level >= 19) {\n        if (findSymbol(\"__dl__Z9do_dlopenPKciPK17android_dlextinfo\", \"linker\",\n                       (unsigned long *) &symbol) == 0) {\n            inlineHookDirect((unsigned int) symbol, (void *) new_do_dlopen_V19,\n                             (void **) &orig_do_dlopen_V19);\n        }\n    } else {\n        if (findSymbol(\"__dl_dlopen\", \"linker\",\n                       (unsigned long *) &symbol) == 0) {\n            inlineHookDirect((unsigned int) symbol, (void *) new_dlopen, (void **) &orig_dlopen);\n        }\n    }\n    if (!symbol) {\n        HOOK_SYMBOL(RTLD_DEFAULT, dlopen);\n    }\n}\n\n\nvoid IOUniformer::startUniformer(int api_level, int preview_api_level) {\n    gVars.hooked_process = true;\n    HOOK_SYMBOL(RTLD_DEFAULT, vfork);\n    HOOK_SYMBOL(RTLD_DEFAULT, kill);\n    HOOK_SYMBOL(RTLD_DEFAULT, __getcwd);\n    HOOK_SYMBOL(RTLD_DEFAULT, truncate);\n    HOOK_SYMBOL(RTLD_DEFAULT, __statfs64);\n    HOOK_SYMBOL(RTLD_DEFAULT, execve);\n    HOOK_SYMBOL(RTLD_DEFAULT, __open);\n    if ((api_level < 25) || (api_level == 25 && preview_api_level == 0)) {\n        HOOK_SYMBOL(RTLD_DEFAULT, utimes);\n        HOOK_SYMBOL(RTLD_DEFAULT, mkdir);\n        HOOK_SYMBOL(RTLD_DEFAULT, chmod);\n        HOOK_SYMBOL(RTLD_DEFAULT, lstat);\n        HOOK_SYMBOL(RTLD_DEFAULT, link);\n        HOOK_SYMBOL(RTLD_DEFAULT, symlink);\n        HOOK_SYMBOL(RTLD_DEFAULT, mknod);\n        HOOK_SYMBOL(RTLD_DEFAULT, rmdir);\n        HOOK_SYMBOL(RTLD_DEFAULT, chown);\n        HOOK_SYMBOL(RTLD_DEFAULT, rename);\n        HOOK_SYMBOL(RTLD_DEFAULT, stat);\n        HOOK_SYMBOL(RTLD_DEFAULT, chdir);\n        HOOK_SYMBOL(RTLD_DEFAULT, access);\n        HOOK_SYMBOL(RTLD_DEFAULT, readlink);\n        HOOK_SYMBOL(RTLD_DEFAULT, unlink);\n    }\n    HOOK_SYMBOL(RTLD_DEFAULT, fstatat);\n    HOOK_SYMBOL(RTLD_DEFAULT, fchmodat);\n    HOOK_SYMBOL(RTLD_DEFAULT, symlinkat);\n    HOOK_SYMBOL(RTLD_DEFAULT, readlinkat);\n    HOOK_SYMBOL(RTLD_DEFAULT, unlinkat);\n    HOOK_SYMBOL(RTLD_DEFAULT, linkat);\n    HOOK_SYMBOL(RTLD_DEFAULT, utimensat);\n    HOOK_SYMBOL(RTLD_DEFAULT, __openat);\n    HOOK_SYMBOL(RTLD_DEFAULT, faccessat);\n    HOOK_SYMBOL(RTLD_DEFAULT, mkdirat);\n    HOOK_SYMBOL(RTLD_DEFAULT, renameat);\n    HOOK_SYMBOL(RTLD_DEFAULT, fchownat);\n    HOOK_SYMBOL(RTLD_DEFAULT, mknodat);\n//    hook_dlopen(api_level);\n\n#if defined(__i386__) || defined(__x86_64__)\n    // Do nothing\n#else\n    GodinHook::NativeHook::hookAllRegistered();\n#endif\n}\n"
  },
  {
    "path": "lib/src/main/jni/Foundation/IOUniformer.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef NDK_HOOK_H\n#define NDK_HOOK_H\n\n\n#include <string>\n#include <map>\n#include <list>\n#include <jni.h>\n#include <dlfcn.h>\n#include <stddef.h>\n#include <fcntl.h>\n#include<dirent.h>\n#include <sys/syscall.h>\n\n#include <MSHook.h>\n#include \"Helper.h\"\n\n\n#define HOOK_SYMBOL(handle, func) hook_template(handle, #func, (void*) new_##func, (void**) &orig_##func)\n#define HOOK_DEF(ret, func, ...) \\\n  ret (*orig_##func)(__VA_ARGS__); \\\n  ret new_##func(__VA_ARGS__)\n\n\nnamespace IOUniformer {\n\n    void init_array();\n\n    void saveEnvironment(const char *selfSoPath, int api_level, int preview_api_level);\n\n    void startUniformer(int api_level, int preview_api_level);\n\n    void redirect(const char*orig_path, const char*new_path);\n\n    void readOnly(const char*path);\n\n    const char *query(const char *orig_path);\n\n    const char *restore(const char *redirected_path);\n}\n\n#endif //NDK_HOOK_H\n"
  },
  {
    "path": "lib/src/main/jni/Foundation/VMPatch.cpp",
    "content": "//\n// VirtualApp Native Project\n//\n#include \"VMPatch.h\"\n\ntypedef void (*Bridge_DalvikBridgeFunc)(const void **, void *, const void *, void *);\n\ntypedef jobject (*Native_openDexNativeFunc)(JNIEnv *, jclass, jstring, jstring, jint);\n\ntypedef jobject (*Native_openDexNativeFunc_N)(JNIEnv *, jclass, jstring, jstring, jint, jobject,\n                                              jobject);\n\n\ntypedef jint (*Native_cameraNativeSetupFunc_T1)(JNIEnv *, jobject, jobject, jint, jstring);\n\ntypedef jint (*Native_cameraNativeSetupFunc_T2)(JNIEnv *, jobject, jobject, jint, jint, jstring);\n\ntypedef jint (*Native_cameraNativeSetupFunc_T3)(JNIEnv *, jobject, jobject, jint, jint, jstring,\n                                                jboolean);\n\ntypedef jint (*Native_cameraNativeSetupFunc_T4)(JNIEnv *, jobject, jobject, jint, jstring,\n                                                jboolean);\n\ntypedef jint (*Native_getCallingUid)(JNIEnv *, jclass);\n\ntypedef jint (*Native_audioRecordNativeCheckPermission)(JNIEnv *, jobject, jstring);\n\n\nstatic struct {\n\n    bool isArt;\n    int nativeOffset;\n    char *hostPackageName;\n    jint apiLevel;\n    jclass binder_class;\n    jmethodID method_onGetCallingUid;\n    jmethodID method_onOpenDexFileNative;\n\n    void *art_work_around_app_jni_bugs;\n\n    char *(*GetCstrFromString)(void *);\n\n    void *(*GetStringFromCstr)(const char *);\n\n\n    void *sym_IPCThreadState_self;\n    void *sym_IPCThreadState_getCallingUid;\n\n    Native_getCallingUid orig_getCallingUid;\n\n    int cameraMethodType;\n    Bridge_DalvikBridgeFunc orig_cameraNativeSetup_dvm;\n    union {\n        Native_cameraNativeSetupFunc_T1 t1;\n        Native_cameraNativeSetupFunc_T2 t2;\n        Native_cameraNativeSetupFunc_T3 t3;\n        Native_cameraNativeSetupFunc_T4 t4;\n    } orig_native_cameraNativeSetupFunc;\n\n    Bridge_DalvikBridgeFunc orig_openDexFile_dvm;\n    union {\n        Native_openDexNativeFunc beforeN;\n        Native_openDexNativeFunc_N afterN;\n    } orig_native_openDexNativeFunc;\n\n    Native_audioRecordNativeCheckPermission orig_native_audioRecordNativeCheckPermission;\n\n} gOffset;\n\n\nextern JavaVM *gVm;\nextern jclass gClass;\n\n\nvoid mark() {\n    // Do nothing\n};\n\njint getCallingUid(JNIEnv *env, jclass jclazz) {\n    jint uid;\n    if (gOffset.isArt) {\n        uid = gOffset.orig_getCallingUid(env, jclazz);\n    } else {\n        int (*org_getCallingUid)(int) = (int (*)(int)) gOffset.sym_IPCThreadState_getCallingUid;\n        int (*func_self)(void) = (int (*)(void)) gOffset.sym_IPCThreadState_self;\n        uid = org_getCallingUid(func_self());\n    }\n    uid = env->CallStaticIntMethod(gClass, gOffset.method_onGetCallingUid, uid);\n    return uid;\n}\n\n\nstatic JNINativeMethod gMarkMethods[] = {\n        NATIVE_METHOD((void *) mark, \"nativeMark\", \"()V\"),\n};\n\nJNINativeMethod gUidMethods[] = {\n        NATIVE_METHOD((void *) getCallingUid, \"getCallingUid\", \"()I\"),\n};\n\n\nstatic jobject new_native_openDexNativeFunc(JNIEnv *env, jclass jclazz, jstring javaSourceName,\n                                            jstring javaOutputName, jint options) {\n    jclass stringClass = env->FindClass(\"java/lang/String\");\n    jobjectArray array = env->NewObjectArray(2, stringClass, NULL);\n\n    if (javaSourceName) {\n        env->SetObjectArrayElement(array, 0, javaSourceName);\n    }\n    if (javaOutputName) {\n        env->SetObjectArrayElement(array, 1, javaOutputName);\n    }\n    env->CallStaticVoidMethod(gClass, gOffset.method_onOpenDexFileNative, array);\n\n    jstring newSource = (jstring) env->GetObjectArrayElement(array, 0);\n    jstring newOutput = (jstring) env->GetObjectArrayElement(array, 1);\n\n    return gOffset.orig_native_openDexNativeFunc.beforeN(env, jclazz, newSource, newOutput,\n                                                         options);\n}\n\nstatic jobject new_native_openDexNativeFunc_N(JNIEnv *env, jclass jclazz, jstring javaSourceName,\n                                              jstring javaOutputName, jint options, jobject loader,\n                                              jobject elements) {\n    jclass stringClass = env->FindClass(\"java/lang/String\");\n    jobjectArray array = env->NewObjectArray(2, stringClass, NULL);\n\n    if (javaSourceName) {\n        env->SetObjectArrayElement(array, 0, javaSourceName);\n    }\n    if (javaOutputName) {\n        env->SetObjectArrayElement(array, 1, javaOutputName);\n    }\n    env->CallStaticVoidMethod(gClass, gOffset.method_onOpenDexFileNative, array);\n\n    jstring newSource = (jstring) env->GetObjectArrayElement(array, 0);\n    jstring newOutput = (jstring) env->GetObjectArrayElement(array, 1);\n\n    return gOffset.orig_native_openDexNativeFunc.afterN(env, jclazz, newSource, newOutput, options,\n                                                        loader, elements);\n}\n\n\nstatic void\nnew_bridge_openDexNativeFunc(const void **args, void *pResult, const void *method, void *self) {\n    JNIEnv *env = NULL;\n    gVm->GetEnv((void **) &env, JNI_VERSION_1_6);\n    gVm->AttachCurrentThread(&env, NULL);\n\n    typedef char *(*GetCstrFromString)(void *);\n    typedef void *(*GetStringFromCstr)(const char *);\n\n    const char *source = args[0] == NULL ? NULL : gOffset.GetCstrFromString((void *) args[0]);\n    const char *output = args[1] == NULL ? NULL : gOffset.GetCstrFromString((void *) args[1]);\n\n    jstring orgSource = source == NULL ? NULL : env->NewStringUTF(source);\n    jstring orgOutput = output == NULL ? NULL : env->NewStringUTF(output);\n\n    jclass stringClass = env->FindClass(\"java/lang/String\");\n    jobjectArray array = env->NewObjectArray(2, stringClass, NULL);\n    if (orgSource) {\n        env->SetObjectArrayElement(array, 0, orgSource);\n    }\n    if (orgOutput) {\n        env->SetObjectArrayElement(array, 1, orgOutput);\n    }\n\n    env->CallStaticVoidMethod(gClass, gOffset.method_onOpenDexFileNative, array);\n\n    jstring newSource = (jstring) env->GetObjectArrayElement(array, 0);\n    jstring newOutput = (jstring) env->GetObjectArrayElement(array, 1);\n\n    const char *_newSource = newSource == NULL ? NULL : env->GetStringUTFChars(newSource, NULL);\n    const char *_newOutput = newOutput == NULL ? NULL : env->GetStringUTFChars(newOutput, NULL);\n\n    args[0] = _newSource == NULL ? NULL : gOffset.GetStringFromCstr(_newSource);\n    args[1] = _newOutput == NULL ? NULL : gOffset.GetStringFromCstr(_newOutput);\n\n    if (source && orgSource) {\n        env->ReleaseStringUTFChars(orgSource, source);\n    }\n    if (output && orgOutput) {\n        env->ReleaseStringUTFChars(orgOutput, output);\n    }\n\n    gOffset.orig_openDexFile_dvm(args, pResult, method, self);\n}\n\nstatic jint new_native_cameraNativeSetupFunc_T1(JNIEnv *env, jobject thiz, jobject camera_this,\n                                                jint cameraId, jstring packageName) {\n\n    jstring host = env->NewStringUTF(gOffset.hostPackageName);\n\n    return gOffset.orig_native_cameraNativeSetupFunc.t1(env, thiz, camera_this,\n                                                        cameraId,\n                                                        host);\n}\n\nstatic jint new_native_cameraNativeSetupFunc_T2(JNIEnv *env, jobject thiz, jobject camera_this,\n                                                jint cameraId, jint halVersion,\n                                                jstring packageName) {\n\n    jstring host = env->NewStringUTF(gOffset.hostPackageName);\n\n    return gOffset.orig_native_cameraNativeSetupFunc.t2(env, thiz, camera_this, cameraId,\n                                                        halVersion, host);\n}\n\nstatic jint new_native_cameraNativeSetupFunc_T3(JNIEnv *env, jobject thiz, jobject camera_this,\n                                                jint cameraId, jint halVersion,\n                                                jstring packageName, jboolean option) {\n\n    jstring host = env->NewStringUTF(gOffset.hostPackageName);\n\n    return gOffset.orig_native_cameraNativeSetupFunc.t3(env, thiz, camera_this, cameraId,\n                                                        halVersion, host, option);\n}\n\nstatic jint new_native_cameraNativeSetupFunc_T4(JNIEnv *env, jobject thiz, jobject camera_this,\n                                                jint cameraId,\n                                                jstring packageName, jboolean option) {\n\n    jstring host = env->NewStringUTF(gOffset.hostPackageName);\n\n    return gOffset.orig_native_cameraNativeSetupFunc.t4(env, thiz, camera_this, cameraId, host,\n                                                        option);\n}\n\n\nstatic jint\nnew_native_audioRecordNativeCheckPermission(JNIEnv *env, jobject thiz, jstring _packagename) {\n    jstring host = env->NewStringUTF(gOffset.hostPackageName);\n    return gOffset.orig_native_audioRecordNativeCheckPermission(env, thiz, host);\n}\n\n\nstatic void\nnew_bridge_cameraNativeSetupFunc(const void **args, void *pResult, const void *method, void *self) {\n    JNIEnv *env = NULL;\n    gVm->GetEnv((void **) &env, JNI_VERSION_1_6);\n    gVm->AttachCurrentThread(&env, NULL);\n    // args[0] = this\n    switch (gOffset.cameraMethodType) {\n        case 1:\n            args[4] = gOffset.GetStringFromCstr(gOffset.hostPackageName);\n            break;\n        case 2:\n            args[5] = gOffset.GetStringFromCstr(gOffset.hostPackageName);\n            break;\n        case 3:\n            args[5] = gOffset.GetStringFromCstr(gOffset.hostPackageName);\n            break;\n        case 4:\n            args[4] = gOffset.GetStringFromCstr(gOffset.hostPackageName);\n            break;\n    }\n    gOffset.orig_cameraNativeSetup_dvm(args, pResult, method, self);\n}\n\n\nvoid measureNativeOffset(JNIEnv *env, bool isArt) {\n\n    jmethodID mtd_nativeHook = env->GetStaticMethodID(gClass, gMarkMethods[0].name,\n                                                      gMarkMethods[0].signature);\n\n    size_t startAddress = (size_t) mtd_nativeHook;\n    size_t targetAddress = (size_t) mark;\n    if (isArt && gOffset.art_work_around_app_jni_bugs) {\n        targetAddress = (size_t) gOffset.art_work_around_app_jni_bugs;\n    }\n\n    int offset = 0;\n    bool found = false;\n    while (true) {\n        if (*((size_t *) (startAddress + offset)) == targetAddress) {\n            found = true;\n            break;\n        }\n        offset += 4;\n        if (offset >= 100) {\n            LOGE(\"Error: Unable to find the jni function.\");\n            break;\n        }\n    }\n    if (found) {\n        gOffset.nativeOffset = offset;\n        if (!isArt) {\n            gOffset.nativeOffset += (sizeof(int) + sizeof(void *));\n        }\n    }\n}\n\n\ninline void replaceGetCallingUid(JNIEnv *env, jboolean isArt) {\n\n\n    if (isArt) {\n        size_t mtd_getCallingUid = (size_t) env->GetStaticMethodID(gOffset.binder_class,\n                                                                   \"getCallingUid\", \"()I\");\n        int nativeFuncOffset = gOffset.nativeOffset;\n        void **jniFuncPtr = (void **) (mtd_getCallingUid + nativeFuncOffset);\n        gOffset.orig_getCallingUid = (Native_getCallingUid) (*jniFuncPtr);\n        *jniFuncPtr = (void *) getCallingUid;\n    } else {\n        env->RegisterNatives(gOffset.binder_class, gUidMethods, NELEM(gUidMethods));\n    }\n\n}\n\ninline void\nreplaceOpenDexFileMethod(JNIEnv *env, jobject javaMethod, jboolean isArt, int apiLevel) {\n\n    size_t mtd_openDexNative = (size_t) env->FromReflectedMethod(javaMethod);\n    int nativeFuncOffset = gOffset.nativeOffset;\n    void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset);\n\n    if (!isArt) {\n        gOffset.orig_openDexFile_dvm = (Bridge_DalvikBridgeFunc) (*jniFuncPtr);\n        *jniFuncPtr = (void *) new_bridge_openDexNativeFunc;\n    } else {\n        if (apiLevel < ANDROID_N) {\n            gOffset.orig_native_openDexNativeFunc.beforeN = (Native_openDexNativeFunc) (*jniFuncPtr);\n            *jniFuncPtr = (void *) new_native_openDexNativeFunc;\n        } else {\n            gOffset.orig_native_openDexNativeFunc.afterN = (Native_openDexNativeFunc_N) (*jniFuncPtr);\n            *jniFuncPtr = (void *) new_native_openDexNativeFunc_N;\n        }\n    }\n\n}\n\n\ninline void\nreplaceCameraNativeSetupMethod(JNIEnv *env, jobject javaMethod, jboolean isArt, int apiLevel) {\n\n    if (!javaMethod) {\n        return;\n    }\n    size_t mtd_cameraNativeSetup = (size_t) env->FromReflectedMethod(javaMethod);\n    int nativeFuncOffset = gOffset.nativeOffset;\n    void **jniFuncPtr = (void **) (mtd_cameraNativeSetup + nativeFuncOffset);\n\n    if (!isArt) {\n        gOffset.orig_cameraNativeSetup_dvm = (Bridge_DalvikBridgeFunc) (*jniFuncPtr);\n        *jniFuncPtr = (void *) new_bridge_cameraNativeSetupFunc;\n    } else {\n        switch (gOffset.cameraMethodType) {\n            case 1:\n                gOffset.orig_native_cameraNativeSetupFunc.t1 = (Native_cameraNativeSetupFunc_T1) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T1;\n                break;\n            case 2:\n                gOffset.orig_native_cameraNativeSetupFunc.t2 = (Native_cameraNativeSetupFunc_T2) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T2;\n                break;\n            case 3:\n                gOffset.orig_native_cameraNativeSetupFunc.t3 = (Native_cameraNativeSetupFunc_T3) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T3;\n                break;\n            case 4:\n                gOffset.orig_native_cameraNativeSetupFunc.t4 = (Native_cameraNativeSetupFunc_T4) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T4;\n                break;\n        }\n    }\n\n}\n\n\nvoid\nreplaceAudioRecordNativeCheckPermission(JNIEnv *env, jobject javaMethod, jboolean isArt, int api) {\n    if (!javaMethod || !isArt) {\n        return;\n    }\n    jmethodID methodStruct = env->FromReflectedMethod(javaMethod);\n    void **funPtr = (void **) (reinterpret_cast<size_t>(methodStruct) + gOffset.nativeOffset);\n    gOffset.orig_native_audioRecordNativeCheckPermission = (Native_audioRecordNativeCheckPermission) (*funPtr);\n    *funPtr = (void *) new_native_audioRecordNativeCheckPermission;\n}\n\n\n/**\n * Only called once.\n * @param javaMethod Method from Java\n * @param isArt Dalvik or Art\n * @param apiLevel Api level from Java\n */\nvoid patchAndroidVM(jobjectArray javaMethods, jstring packageName, jboolean isArt, jint apiLevel,\n                    jint cameraMethodType) {\n\n    JNIEnv *env = NULL;\n    gVm->GetEnv((void **) &env, JNI_VERSION_1_6);\n    gVm->AttachCurrentThread(&env, NULL);\n\n    if (env->RegisterNatives(gClass, gMarkMethods, NELEM(gMarkMethods)) < 0) {\n        return;\n    }\n    gOffset.isArt = isArt;\n    gOffset.cameraMethodType = cameraMethodType;\n    gOffset.hostPackageName = (char *) env->GetStringUTFChars(packageName, NULL);\n    gOffset.apiLevel = apiLevel;\n    void *soInfo = getVMHandle();\n    gOffset.binder_class = env->FindClass(\"android/os/Binder\");\n    gOffset.method_onGetCallingUid = env->GetStaticMethodID(gClass, \"onGetCallingUid\", \"(I)I\");\n    gOffset.method_onOpenDexFileNative = env->GetStaticMethodID(gClass, \"onOpenDexFileNative\",\n                                                                \"([Ljava/lang/String;)V\");\n\n    if (isArt) {\n        gOffset.art_work_around_app_jni_bugs = dlsym(soInfo, \"art_work_around_app_jni_bugs\");\n    } else {\n        gOffset.sym_IPCThreadState_self = dlsym(RTLD_DEFAULT, \"_ZN7android14IPCThreadState4selfEv\");\n        gOffset.sym_IPCThreadState_getCallingUid = dlsym(RTLD_DEFAULT,\n                                                         \"_ZNK7android14IPCThreadState13getCallingUidEv\");\n        if (gOffset.sym_IPCThreadState_getCallingUid == NULL) {\n            gOffset.sym_IPCThreadState_getCallingUid = dlsym(RTLD_DEFAULT,\n                                                             \"_ZN7android14IPCThreadState13getCallingUidEv\");\n        }\n\n        gOffset.GetCstrFromString = (char *(*)(void *)) dlsym(soInfo,\n                                                              \"_Z23dvmCreateCstrFromStringPK12StringObject\");\n        if (!gOffset.GetCstrFromString) {\n            gOffset.GetCstrFromString = (char *(*)(void *)) dlsym(soInfo,\n                                                                  \"dvmCreateCstrFromString\");\n        }\n        gOffset.GetStringFromCstr = (void *(*)(const char *)) dlsym(soInfo,\n                                                                    \"_Z23dvmCreateStringFromCstrPKc\");\n        if (!gOffset.GetStringFromCstr) {\n            gOffset.GetStringFromCstr = (void *(*)(const char *)) dlsym(soInfo,\n                                                                        \"dvmCreateStringFromCstr\");\n        }\n    }\n    measureNativeOffset(env, isArt);\n    replaceGetCallingUid(env, isArt);\n    replaceOpenDexFileMethod(env, env->GetObjectArrayElement(javaMethods, OPEN_DEX), isArt,\n                             apiLevel);\n    replaceCameraNativeSetupMethod(env, env->GetObjectArrayElement(javaMethods, CAMERA_SETUP),\n                                   isArt, apiLevel);\n    replaceAudioRecordNativeCheckPermission(env, env->GetObjectArrayElement(javaMethods,\n                                                                            VIVO_AUDIORECORD_NATIVE_CHECK_PERMISSION),\n                                            isArt, apiLevel);\n}\n\nvoid *getVMHandle() {\n    char soName[15] = {0};\n    __system_property_get(\"persist.sys.dalvik.vm.lib.2\", soName);\n    if (soName[0] == '\\x0') {\n        __system_property_get(\"persist.sys.dalvik.vm.lib\", soName);\n    }\n    void *soInfo = dlopen(soName, 0);\n    if (!soInfo) {\n        soInfo = RTLD_DEFAULT;\n    }\n    return soInfo;\n}"
  },
  {
    "path": "lib/src/main/jni/Foundation/VMPatch.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef NDK_HOOK_NATIVE_H\n#define NDK_HOOK_NATIVE_H\n\n\n#include <jni.h>\n#include <dlfcn.h>\n#include <stddef.h>\n#include <fcntl.h>\n#include <sys/system_properties.h>\n\n#include \"Helper.h\"\n\nenum METHODS {\n    OPEN_DEX = 0, CAMERA_SETUP, VIVO_AUDIORECORD_NATIVE_CHECK_PERMISSION\n};\n\nvoid patchAndroidVM(jobjectArray javaMethods, jstring packageName, jboolean isArt, jint apiLevel, jint cameraMethodType);\n\nvoid *getVMHandle();\n\n\n#endif //NDK_HOOK_NATIVE_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/godin_type.h",
    "content": "#ifndef GODINTYPE_H\n#define GODINTYPE_H\n\n\ntypedef signed char             int8_t;\ntypedef short int               int16_t;\ntypedef int                     int32_t;\n# if __WORDSIZE == 64\ntypedef long int                int64_t;\n# else\n__extension__\ntypedef long long int           int64_t;\n# endif\n#endif\n\n/* Unsigned.  */\ntypedef unsigned char           uint8_t;\ntypedef unsigned short int      uint16_t;\n#ifndef __uint32_t_defined\ntypedef unsigned int            uint32_t;\n# define __uint32_t_defined\n#endif\n#if __WORDSIZE == 64\ntypedef unsigned long int       uint64_t;\n#else\n__extension__\ntypedef unsigned long long int  uint64_t;\n\n#endif // GODINTYPE_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/hookinfo.h",
    "content": "#ifndef HOOKINFO_H\n#define HOOKINFO_H\n\n//#include <godin_type.h>\n#include <stdint.h>\n#include <iostream>\nnamespace GodinHook {\n\n\n  enum HookStatus{\n    ERRSTATUS = 0,    /*!< 错误状态 */\n    REGISTERED,\n    HOOKED,           /*!< 已经被hook */\n  };\n\n  enum FunctionType{\n    ERRTYEPE  = 0,    /*!< 错误类型 */\n    ARM,              /*!< ARM指令集 */\n    THUMB ,           /*!< thumb指令集 */\n    ARM64,            /*!< ARMV8指令集*/\n  };\n\nclass HookInfo{\n\n\npublic :\n\n  /**\n   * @brief HookInfo\n   *  唯一的构造函数\n   * @param originalAddr\n   *  原方法地址\n   * @param hookAddr\n   *  新方法地址\n   * @param callOriginalAddr\n   *  存储回调原方法的地址\n   */\n  HookInfo(size_t originalAddr,size_t hookAddr,size_t ** callOriginalAddr)\n    :original_addr_(originalAddr),hook_addr_(hookAddr),call_original_addr_(callOriginalAddr),\n    original_stub_back_(NULL),back_len_(0),call_original_ins_(NULL),hook_status_(ERRSTATUS),\n    original_function_type_(ERRTYEPE),hook_function_type_(ERRTYEPE),count(0){}\n\nprivate:\n  /// 构造函数中负责初始化这三个字段\n  size_t original_addr_;                    /*!< 原方法地址 */\n  size_t hook_addr_;                        /*!< 新方法地址 */\n  size_t **call_original_addr_;             /*!< 回调原方法 */\n\n  /// registerAndHook初始化这三个字段,\n  /// unhook回收资源时要用到\n  uint8_t *original_stub_back_;             /*!< 存储原方法被stub覆盖的机器码 */\n  int    back_len_;                         /*!< 原方法被stub覆盖的机器指令大小，亦即stub大小 */\n  uint8_t *call_original_ins_;              /*!< 存储经过修正过的原方法被stub覆盖的机器指令，以及追加跳转至原方法剩余机器指令的跳转指令*/\n\n  HookStatus hook_status_;                  /*!< 当前hook状态*/\n  FunctionType original_function_type_;     /*!< 原方法指令集类型*/\n  FunctionType hook_function_type_;         /*!< 新方法指令集类型*/\n\npublic:\n  /// 为了保证线程安全\n  int orig_boundaries[8];\n  int trampoline_boundaries[32];\n  int count;\n\npublic:\n\n  void setOriginalAddr(size_t addr){\n    this->original_addr_ = addr;\n  }\n  size_t getOriginalAddr(){\n    return this->original_addr_;\n  }\n\n  void setHookAddr(size_t addr){\n    this->hook_addr_ = addr;\n  }\n  size_t getHookAddr(){\n    return this->hook_addr_;\n  }\n\n  void setCallOriginalAddr(size_t ** addr){\n     this->call_original_addr_ = addr;\n  }\n  size_t ** getCallOriginalAddr(){\n    return this->call_original_addr_;\n  }\n\n\n\n  void setOriginalStubBack(uint8_t* addr){\n    this->original_stub_back_ = addr;\n  }\n uint8_t* getOriginalStubBack(){\n    return this->original_stub_back_;\n  }\n\n  void setBackLen(int len){\n    this->back_len_ = len;\n  }\n  size_t getBackLen(){\n    return this->back_len_;\n  }\n\n  void setCallOriginalIns(uint8_t* addr){\n     this->call_original_ins_ = addr;\n  }\n  uint8_t* getCallOriginalIns(){\n    return this->call_original_ins_;\n  }\n\n\n  void setHookStatus(HookStatus status){\n    this->hook_status_ = status;\n  }\n  HookStatus getHookStatus(){\n    return this->hook_status_;\n  }\n\n  void setOriginalFunctionType(FunctionType type){\n    this->original_function_type_ = type;\n  }\n  FunctionType getOriginalFunctiontype(){\n    return this->original_function_type_;\n  }\n\n  void setHookFunctionType(FunctionType type){\n    this->hook_function_type_ = type;\n  }\n  FunctionType getHookFunctionType(){\n    return this->hook_function_type_;\n  }\n\n};\n\n}\n#endif // HOOKINFO_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/instruction/arm_instruction.cpp",
    "content": "\n#include \"arm_instruction.h\"\n#include <stdlib.h>\n#include <string.h>\n#include \"../mem_helper.h\"\n#include <unistd.h>//android cacheflush\n\n\n#define MAX_REPAIR_INS_LEN 64\n\nuint8_t GodinHook::ArmInstruction::ldr[4]={\n  0x04,0xf0,0x1f,0xe5,\n};\nstatic void clearcache(char* begin, char *end)\n{\n    const int syscall = 0xf0002;\n    __asm __volatile (\n        \"mov     r0, %0\\n\"\n        \"mov     r1, %1\\n\"\n        \"mov     r7, %2\\n\"\n        \"mov     r2, #0x0\\n\"\n        \"svc     0x00000000\\n\"\n        :\n        :\"r\" (begin), \"r\" (end), \"r\" (syscall)\n        :\"r0\",\"r1\",\"r7\"\n        );\n}\nvoid GodinHook::ArmInstruction::createStub(HookInfo * info)\n{\n  size_t originalAddress = info->getOriginalAddr();\n  size_t targetAddress = info->getHookAddr();\n  int len = sizeofStub();\n  /// 去保护，并修改指令\n  if(MemHelper::unProtectMemory(originalAddress,len)){\n     memcpy((void*)originalAddress,ldr,4);\n     memcpy((void*)(originalAddress+4),&targetAddress,4);\n  }else\n    return;\n  //clearcache(originalAddress,originalAddress+len);\n  /// 加保护\n  MemHelper::protectMemory(originalAddress,len);\n  /// 刷新指令缓存\n  cacheflush(originalAddress,originalAddress+len,0);\n\n\n\n}\n\nint GodinHook::ArmInstruction::getRepairInstruction(size_t instruction)\n{\n  if ((instruction & 0xFE000000) == 0xFA000000) {\n          return BLX_ARM;\n  }\n  if ((instruction & 0xF000000) == 0xB000000) {\n          return BL_ARM;\n  }\n  if ((instruction & 0xF000000) == 0xA000000) {\n          return B_ARM;\n  }\n  if ((instruction & 0xFF000FF) == 0x120001F) {\n          return BX_ARM;\n  }\n  if ((instruction & 0xFEF0010) == 0x8F0000) {\n          return ADD_ARM;\n  }\n  if ((instruction & 0xFFF0000) == 0x28F0000) {\n          return ADR1_ARM;\n  }\n  if ((instruction & 0xFFF0000) == 0x24F0000) {\n          return ADR2_ARM;\n  }\n  if ((instruction & 0xE5F0000) == 0x41F0000) {\n          return LDR_ARM;\n  }\n  if ((instruction & 0xFE00FFF) == 0x1A0000F) {\n          return MOV_ARM;\n  }\n  return UNDEFINE;\n}\n\nvoid GodinHook::ArmInstruction::repairBackInstructionsOfStub(HookInfo * info,size_t * calloriginal)\n{\n  size_t originalAddress = info->getOriginalAddr();\n  uint8_t *back = info->getOriginalStubBack();\n  size_t * ins = (size_t *)back;\n  size_t * repair = calloriginal;\n  if(NULL == repair)\n    return;\n  int pos = 0;\n\n  /// 得到原始指令起始处的pc值\n  size_t originalPc = originalAddress + 8;\n  size_t originalLr = originalAddress +sizeofStub();\n\n\n  /**\n   * 需要修正的是那些机器指令内部存储的是要操作数据基于当前PC的偏移值；\n   * 修正思路，计算出绝对地址，构造跳转指令。\n   */\n  for(int i=0;i<sizeofStub()/(sizeof(size_t));i++){\n  /// 为了线程安全\n  info->orig_boundaries[info->count] = i * sizeof(uint32_t);\n  info->trampoline_boundaries[info->count] = pos * sizeof(uint32_t);\n  info->count +=1;\n\n\n  int type = getRepairInstruction(ins[i]);\n  size_t x = 0;\n  int top_bit;\n  size_t imm32;\n  size_t value;\n  switch(type){\n    case BLX_ARM:\n    case BL_ARM:\n      repair[pos++] = 0xE28FE004;\t// ADD LR, PC, #4\n    case B_ARM:\n    case BX_ARM:\n    {\n      repair[pos++] = 0xE51FF004;  \t// LDR PC, [PC, #-4]\n      if(BLX_ARM == type)\n        x = (((ins[i]) & 0xFFFFFF) << 2) | (((ins[i]) & 0x1000000) >> 23);\n      else if (type == BL_ARM || type == B_ARM){\n          x = ((ins[i]) & 0xFFFFFF) << 2;\n        }\n      else {\n          x = 0;\n      }\n      top_bit = x >> 25;\n      imm32 = top_bit ? (x | (0xFFFFFFFF << 26)) : x;\n      if (type == BLX_ARM) {\n          value = originalPc + imm32 + 1;\n        }\n      else {\n          value = originalPc + imm32;\n        }\n      repair[pos++] = value;\n      break;\n    }\n    case ADD_ARM:\n    {\n        int rd;\n        int rm;\n        int r;\n        rd = ((ins[i]) & 0xF000) >> 12;\n        rm = (ins[i]) & 0xF;\n        for (r = 12; ; --r) {\n            if (r != rd && r != rm) {\n                break;\n              }\n          }\n        repair[pos++] = 0xE52D0004 | (r << 12);\t// PUSH {Rr}\n        repair[pos++] = 0xE59F0008 | (r << 12);\t// LDR Rr, [PC, #8]\n        repair[pos++] = ((ins[i]) & 0xFFF0FFFF) | (r << 16);\n        repair[pos++] = 0xE49D0004 | (r << 12);\t// POP {Rr}\n        repair[pos++] = 0xE28FF000;\t// ADD PC, PC\n        repair[pos++] = originalPc;\n        break;\n    }\n    case ADR1_ARM:\n    case ADR2_ARM:\n    case LDR_ARM:\n    case MOV_ARM:\n    {\n        int r;\n        uint32_t value;\n\n        r = ((ins[i]) & 0xF000) >> 12;\n\n        if (type == ADR1_ARM || type == ADR2_ARM || type == LDR_ARM) {\n            uint32_t imm32;\n\n            imm32 = (ins[i]) & 0xFFF;\n            if (type == ADR1_ARM) {\n                value = originalPc + imm32;\n              }\n            else if (type == ADR2_ARM) {\n                value = originalPc - imm32;\n              }\n            else if (type == LDR_ARM) {\n                int is_add;\n\n                is_add = ((ins[i]) & 0x800000) >> 23;\n                if (is_add) {\n                    value = ((size_t *) (originalPc + imm32))[0];\n                  }\n                else {\n                    value = ((size_t *) (originalPc - imm32))[0];\n                  }\n              }\n          }\n        else {\n            value = originalPc;\n          }\n        repair[pos++] = 0xE51F0000 | (r << 12);\t// LDR Rr, [PC]\n        repair[pos++] = 0xE28FF000;\t// ADD PC, PC\n        repair[pos++] = value;\n        break;\n    }\n    default:\n    {\n      ///无需修正\n      repair[pos++] = ins[i];\n    }\n  }\n  originalPc +=sizeof(size_t);\n  //ins++;\n }\n  repair[pos++] = 0xe51ff004;\t// LDR PC, [PC, #-4]\n  repair[pos++] = originalLr;\n}\n\nvoid *GodinHook::ArmInstruction::createCallOriginalIns(HookInfo * info)\n{\n  void * fun = MemHelper::createExecMemory();\n\n\n  //int len = sizeofStub();\n  /**\n   *修正指令，需要对备份的机器指令中与pc相关的进行修正\n   */\n\n  ///创建指令\n  //memcpy(fun,back,len);\n  //memcpy((size_t)fun+len,ldr,4);\n  //memcpy((void*)((size_t)fun+len+4),&originalAddress,4);\n\n  repairBackInstructionsOfStub(info, (size_t *) fun);\n  return fun;\n}\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/instruction/arm_instruction.h",
    "content": "#ifndef ARM_INSTRUCTION_H\n#define ARM_INSTRUCTION_H\n\n#include \"instruction_helper.h\"\n#include <stdint.h>\nnamespace GodinHook {\n\n  class ArmInstruction:public InstructionHelper{\n\n\n\n  public:\n\n    ~ArmInstruction(){}\n\n    /**\n     * @brief sizeofStub\n     *  计算stub指令所需空间大小，单位字节\n     * @return\n     *  返回stub所需字节数\n     */\n    int sizeofStub()\n    {\n      return 8;\n    }\n\n    /**\n     * @brief createStub\n     *  构造stub指令，亦即构造跳转指令,特别注意需要刷新指令缓存\n     * @param originalAddress\n     *  原方法地址\n     * @param targetAddress\n     *  跳转的目标地址\n     * @return\n     *  存储stub指令的空间地址\n     */\n    void  createStub(HookInfo * info);\n\n\n    int getRepairInstruction(size_t ins);\n\n    void  repairBackInstructionsOfStub(HookInfo * info,size_t * calloriginal);\n    void *createCallOriginalIns(HookInfo * info);\n\n   private:\n    static uint8_t ldr[4];\n    enum RepairIns{\n      BLX_ARM,      /*!< BLX <label>*/\n      BL_ARM,       /*!< BL <label>*/\n      B_ARM,        /*!< B <label>*/\n      BX_ARM,       /*!< BX PC*/\n      ADD_ARM,      /*!< ADD Rd, PC, Rm (Rd != PC, Rm != PC)*/\n      ADR1_ARM,     /*!< ADR Rd, <label>*/\n      ADR2_ARM,     /*!< ADR Rd, <label>*/\n      MOV_ARM,      /*!< MOV Rd, PC*/\n      LDR_ARM,      /*!<LDR Rt, <label>*/\n\n      UNDEFINE,\n    };\n\n\n\n\n\n\n\n  };\n\n\n}\n#endif // ARM_INSTRUCTION_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/instruction/instruction_helper.cpp",
    "content": "\n#include \"instruction_helper.h\"\n#include <stdlib.h>\n#include <string.h>\n\n\nuint8_t *GodinHook::InstructionHelper::getBackOfStub(size_t targetAddress)\n{\n  int len = sizeofStub();\n  uint8_t * back = (uint8_t *) calloc(1, (size_t) len);\n  if(NULL == back)\n    return NULL;\n  memcpy(back, (const void *) targetAddress, (size_t) len);\n  return back;\n}\n\n///TODO 添加对ARM 64 指令集支持\nGodinHook::FunctionType GodinHook::InstructionHelper::getFunctionType(size_t functionAddr)\n{\n  if(0 == functionAddr)\n    return ERRTYEPE;\n    if(functionAddr % 4 == 0)\n      return ARM;\n    else if(functionAddr % 4 == 1)\n      return THUMB;\n\n    return THUMB;\n}\n\nsize_t GodinHook::InstructionHelper::valueToMem(size_t addr)\n{\n  return addr &(~0x1L);\n}\n\nsize_t GodinHook::InstructionHelper::valueToPc(size_t addr)\n{\n  return valueToMem(addr)+1;\n}\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/instruction/instruction_helper.h",
    "content": "#ifndef INSTRUCTIONHELPER_H\n#define INSTRUCTIONHELPER_H\n\n//#include <godin_type.h>\n#include <stdint.h>\n#include \"../hookinfo.h\"\n#include <iostream>\n#include \"../hookinfo.h\"\n\n\nnamespace GodinHook {\n\n\n/**\n * @brief 指令集基类\n *\n * 其直接子类负责具体的指令集，比如thumb2,arm,arm64.\n * 该类为抽象类，其子类需负责实现其中的纯虚函数。\n */\nclass InstructionHelper{\npublic:\n  /**\n   * @brief ~InstructionHelper\n   *  虚构造函数，析构时可以调用其子类的析构函数，防止析构不彻底\n   */\n  virtual ~InstructionHelper(){}\n\n  /**\n  * @brief createStub\n  *   在原方法机器码起始处创建跳转至新方法的跳转指令\n  *\n  * @param originalAddress\n  *   原方法地址,需考虑兼容32/64\n  * @param targetAddress\n  *   新方法地址,需考虑兼容32/64\n  */\n  virtual void createStub(HookInfo * info)=0;\n\n\n  virtual void * createCallOriginalIns(HookInfo * info)=0;\n\n  virtual int getRepairInstruction(size_t ins)=0;\n\n  virtual void repairBackInstructionsOfStub(HookInfo * info,size_t * calloriginal)=0;\n\n\n  virtual void isResetStubSize(size_t originalAddress){}\n\n /**\n   * @brief sizeofStub\n   *  计算stub机器指令所占空间\n   *\n   * @return\n   *  stub机器指令所占字节大小\n   */\n  virtual int sizeofStub()=0;\n\n\n  /**\n   * @brief getBackOfStub\n   * 备份被stub覆盖的机器指令，该函数内部申请的堆内存空间，需要调用者手动释放。\n   *\n   * @param targetAddress\n   * 机器指令起始地址\n   * @return\n   * 返回存储备份指令的数组地址\n   */\n  uint8_t * getBackOfStub(size_t targetAddress );\n\n  /**\n   * @brief getFunctionType\n   *   获得该函数的指令集类型\n   *\n   * @param functionAddr\n   *   必须是一个函数的地址\n   *\n   * @return\n   *   该函数指令集类型，ARM,THUMB or ARMV8\n   */\n  static FunctionType getFunctionType(size_t functionAddr);\n  \n  /**\n   * @brief valueToMem\n   *  修正地址，以便内存操作\n   * @param addr\n   *  函数地址info->getOriginalAddr()\n   * @return \n   *  修正后的值\n   */\n  static size_t valueToMem(size_t addr);\n  \n  \n  /**\n   * @brief valueToPc\n   *  修正地址，以便正确运行\n   * @param addr\n   *  机器码地址\n   * @return \n   *  修正后的值\n   */\n  static size_t valueToPc(size_t addr);\n\n\n\n\n\n\nprivate:\n  //static const unsigned char TargetJump[16];\n};\n\n}\n#endif // INSTRUCTIONHELPER_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/instruction/thumb_instruction.cpp",
    "content": "\n#include \"thumb_instruction.h\"\n#include \"../mem_helper.h\"\n#include <unistd.h>//android cacheflush\n\n#define ALIGN_PC(pc)\t(pc & 0xFFFFFFFC)\n\nvoid GodinHook::ThumbInstruction::createStub(HookInfo * info)\n{\n  size_t originalAddress = info->getOriginalAddr();\n  size_t targetAddress = info->getHookAddr();\n  int i = 0;\n  if(NULL == originalAddress || NULL == targetAddress)\n    return ;\n  /// 修正地址\n  size_t original = valueToMem(originalAddress);\n if(MemHelper::unProtectMemory(original,stub_len_)){\n    /// 判断是否需要添加nop指令\n    if(isPcNeedAlgin(original)){\n    ((uint16_t*)original)[i++] = 0xbf00;\n    //printf(\"--need pc align!!!!\\n\");\n    }\n    /// 构造跳转指令\n    ((uint16_t*)original)[i++] = 0xF8DF;\n    ((uint16_t*)original)[i++] = 0xF000;\n    ((uint16_t*)original)[i++] = targetAddress & 0xFFFF;\n    ((uint16_t*)original)[i++] = targetAddress >> 16;\n  }else\n    return ;\n    MemHelper::protectMemory(original,stub_len_);\n    /// 刷新指令缓存\n    cacheflush(original,original+stub_len_,0);\n}\n\nvoid *GodinHook::ThumbInstruction::createCallOriginalIns(HookInfo * info)\n{\n  void * fun = MemHelper::createExecMemory();\n\n\n  //int len = sizeofStub();\n  /**\n   *修正指令，需要对备份的机器指令中与pc相关的进行修正\n   */\n  repairBackInstructionsOfStub(info, (size_t *) fun);\n  return fun;\n}\n\nint GodinHook::ThumbInstruction::getRepairInstruction(size_t instruction)\n{\n  if((instruction >> 16) == 0){\n      if ((instruction & 0xF000) == 0xD000) {\n          return B1_THUMB16;\n        }\n      if ((instruction & 0xF800) == 0xE000) {\n          return B2_THUMB16;\n        }\n      if ((instruction & 0xFFF8) == 0x4778) {\n          return BX_THUMB16;\n        }\n      if ((instruction & 0xFF78) == 0x4478) {\n          return ADD_THUMB16;\n        }\n      if ((instruction & 0xFF78) == 0x4678) {\n          return MOV_THUMB16;\n        }\n      if ((instruction & 0xF800) == 0xA000) {\n          return ADR_THUMB16;\n        }\n      if ((instruction & 0xF800) == 0x4800) {\n          return LDR_THUMB16;\n        }\n   }else{\n      if ((instruction & 0xF800D000) == 0xF000C000) {\n          return BLX_THUMB32;\n        }\n      if ((instruction & 0xF800D000) == 0xF000D000) {\n          return BL_THUMB32;\n        }\n      if ((instruction & 0xF800D000) == 0xF0008000) {\n          return B1_THUMB32;\n        }\n      if ((instruction & 0xF800D000) == 0xF0009000) {\n          return B2_THUMB32;\n        }\n      if ((instruction & 0xFBFF8000) == 0xF2AF0000) {\n          return ADR1_THUMB32;\n        }\n      if ((instruction & 0xFBFF8000) == 0xF20F0000) {\n          return ADR2_THUMB32;\n        }\n      if ((instruction & 0xFF7F0000) == 0xF85F0000) {\n          return LDR_THUMB32;\n        }\n      if ((instruction & 0xFFFF00F0) == 0xE8DF0000) {\n          return TBB_THUMB32;\n        }\n      if ((instruction & 0xFFFF00F0) == 0xE8DF0010) {\n          return TBH_THUMB32;\n        }\n   }\n  return UNDEFINE;\n}\n\nint GodinHook::ThumbInstruction::repairThumb32Instruction(uint32_t pc, uint16_t high_instruction, uint16_t low_instruction, uint16_t *respair)\n{\n\tuint32_t instruction;\n\tint type;\n\tint idx;\n\tint offset;\n\n\tinstruction = (high_instruction << 16) | low_instruction;\n\ttype = getRepairInstruction(instruction);\n\tidx = 0;\n\tif (type == BLX_THUMB32 || type == BL_THUMB32 || type == B1_THUMB32 || type == B2_THUMB32) {\n\t\tuint32_t j1;\n\t\tuint32_t j2;\n\t\tuint32_t s;\n\t\tuint32_t i1;\n\t\tuint32_t i2;\n\t\tuint32_t x;\n\t\tuint32_t imm32;\n\t\tuint32_t value;\n\n\t\tj1 = (low_instruction & 0x2000) >> 13;\n\t\tj2 = (low_instruction & 0x800) >> 11;\n\t\ts = (high_instruction & 0x400) >> 10;\n\t\ti1 = !(j1 ^ s);\n\t\ti2 = !(j2 ^ s);\n\n\t\tif (type == BLX_THUMB32 || type == BL_THUMB32) {\n\t\t\trespair[idx++] = 0xF20F;\n\t\t\trespair[idx++] = 0x0E09;\t// ADD.W LR, PC, #9\n\t\t}\n\t\telse if (type == B1_THUMB32) {\n\t\t\trespair[idx++] = 0xD000 | ((high_instruction & 0x3C0) << 2);\n\t\t\trespair[idx++] = 0xE003;\t// B PC, #6\n\t\t}\n\t\trespair[idx++] = 0xF8DF;\n\t\trespair[idx++] = 0xF000;\t// LDR.W PC, [PC]\n\t\tif (type == BLX_THUMB32) {\n\t\t\tx = (s << 24) | (i1 << 23) | (i2 << 22) | ((high_instruction & 0x3FF) << 12) | ((low_instruction & 0x7FE) << 1);\n\t\t\timm32 = s ? (x | (0xFFFFFFFF << 25)) : x;\n\t\t\tvalue = pc + imm32;\n\t\t}\n\t\telse if (type == BL_THUMB32) {\n\t\t\tx = (s << 24) | (i1 << 23) | (i2 << 22) | ((high_instruction & 0x3FF) << 12) | ((low_instruction & 0x7FF) << 1);\n\t\t\timm32 = s ? (x | (0xFFFFFFFF << 25)) : x;\n\t\t\tvalue = pc + imm32;\n\t\t\tvalue = valueToPc(value);\n\t\t}\n\t\telse if (type == B1_THUMB32) {\n\t\t\tx = (s << 20) | (j2 << 19) | (j1 << 18) | ((high_instruction & 0x3F) << 12) | ((low_instruction & 0x7FF) << 1);\n\t\t\timm32 = s ? (x | (0xFFFFFFFF << 21)) : x;\n\t\t\tvalue = pc + imm32;\n\t\t\tvalue = valueToPc(value);\n\t\t}\n\t\telse if (type == B2_THUMB32) {\n\t\t\tx = (s << 24) | (i1 << 23) | (i2 << 22) | ((high_instruction & 0x3FF) << 12) | ((low_instruction & 0x7FF) << 1);\n\t\t\timm32 = s ? (x | (0xFFFFFFFF << 25)) : x;\n\t\t\tvalue = pc + imm32;\n\t\t\tvalue = valueToPc(value);\n\t\t}\n\t\trespair[idx++] = value & 0xFFFF;\n\t\trespair[idx++] = value >> 16;\n\t\toffset = idx;\n\t}\n\telse if (type == ADR1_THUMB32 || type == ADR2_THUMB32 || type == LDR_THUMB32) {\n\t\tint r;\n\t\tuint32_t imm32;\n\t\tuint32_t value;\n\n\t\tif (type == ADR1_THUMB32 || type == ADR2_THUMB32) {\n\t\t\tuint32_t i;\n\t\t\tuint32_t imm3;\n\t\t\tuint32_t imm8;\n\n\t\t\tr = (low_instruction & 0xF00) >> 8;\n\t\t\ti = (high_instruction & 0x400) >> 10;\n\t\t\timm3 = (low_instruction & 0x7000) >> 12;\n\t\t\timm8 = instruction & 0xFF;\n\n\t\t\timm32 = (i << 31) | (imm3 << 30) | (imm8 << 27);\n\n\t\t\tif (type == ADR1_THUMB32) {\n\t\t\t\tvalue = ALIGN_PC(pc) + imm32;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvalue = ALIGN_PC(pc) - imm32;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tint is_add;\n\t\t\tuint32_t *addr;\n\n\t\t\tis_add = (high_instruction & 0x80) >> 7;\n\t\t\tr = low_instruction >> 12;\n\t\t\timm32 = low_instruction & 0xFFF;\n\n\t\t\tif (is_add) {\n\t\t\t\taddr = (uint32_t *) (ALIGN_PC(pc) + imm32);\n\t\t\t}\n\t\t\telse {\n\t\t\t\taddr = (uint32_t *) (ALIGN_PC(pc) - imm32);\n\t\t\t}\n\n\t\t\tvalue = addr[0];\n\t\t}\n\n\t\trespair[0] = 0x4800 | (r << 8);\t// LDR Rr, [PC]\n\t\trespair[1] = 0xE001;\t// B PC, #2\n\t\trespair[2] = value & 0xFFFF;\n\t\trespair[3] = value >> 16;\n\t\toffset = 4;\n\t}\n\n\telse if (type == TBB_THUMB32 || type == TBH_THUMB32) {\n\t     printf(\"99999999999999999\");\n\t\tint rm;\n\t\tint r;\n\t\tint rx;\n\n\t\trm = low_instruction & 0xF;\n\n\t\tfor (r = 7;; --r) {\n\t\t\tif (r != rm) {\n\t\t\t  break;\n\t\t\t}\n\t\t}\n\n\t\tfor (rx = 7; ; --rx) {\n\t\t\tif (rx != rm && rx != r) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\trespair[0] = 0xB400 | (1 << rx);\t// PUSH {Rx}\n\t\trespair[1] = 0x4805 | (r << 8);\t// LDR Rr, [PC, #20]\n\t\trespair[2] = 0x4600 | (rm << 3) | rx;\t// MOV Rx, Rm\n\t\tif (type == TBB_THUMB32) {\n\t\t\trespair[3] = 0xEB00 | r;\n\t\t\trespair[4] = 0x0000 | (rx << 8) | rx;\t// ADD.W Rx, Rr, Rx\n\t\t\trespair[5] = 0x7800 | (rx << 3) | rx; \t// LDRB Rx, [Rx]\n\t\t}\n\t\telse if (type == TBH_THUMB32) {\n\t\t\trespair[3] = 0xEB00 | r;\n\t\t\trespair[4] = 0x0040 | (rx << 8) | rx;\t// ADD.W Rx, Rr, Rx, LSL #1\n\t\t\trespair[5] = 0x8800 | (rx << 3) | rx; \t// LDRH Rx, [Rx]\n\t\t}\n\t\trespair[6] = 0xEB00 | r;\n\t\trespair[7] = 0x0040 | (r << 8) | rx;\t// ADD Rr, Rr, Rx, LSL #1\n\t\trespair[8] = 0x3001 | (r << 8);\t// ADD Rr, #1\n\t\trespair[9] = 0xBC00 | (1 << rx);\t// POP {Rx}\n\t\trespair[10] = 0x4700 | (r << 3);\t// BX Rr\n\t\trespair[11] = 0xBF00;\n\t\trespair[12] = pc & 0xFFFF;\n\t\trespair[13] = pc >> 16;\n\t\toffset = 14;\n\t}\n\telse {\n\t\trespair[0] = high_instruction;\n\t\trespair[1] = low_instruction;\n\t\toffset = 2;\n\t}\n\n\treturn offset;\n}\n\nint GodinHook::ThumbInstruction::repairThumb16Instruction(uint32_t pc, uint16_t instruction, uint16_t *respair)\n{\n  int type;\n  int offset;\n  type = getRepairInstruction(instruction);\n  if (type == B1_THUMB16 || type == B2_THUMB16 || type == BX_THUMB16) {\n      uint32_t x;\n      int top_bit;\n      uint32_t imm32;\n      uint32_t value;\n      int idx;\n\n      idx = 0;\n      if (type == B1_THUMB16) {\n          x = (instruction & 0xFF) << 1;\n          top_bit = x >> 8;\n          imm32 = top_bit ? (x | (0xFFFFFFFF << 8)) : x;\n          value = pc + imm32;\n          respair[idx++] = instruction & 0xFF00;  // B<cond> 0\n          respair[idx++] = 0xE003;                // B PC, #6\n        }\n      else if (type == B2_THUMB16) {\n          x = (instruction & 0x7FF) << 1;\n          top_bit = x >> 11;\n          imm32 = top_bit ? (x | (0xFFFFFFFF << 11)) : x;\n          value = pc + imm32;\n\n        }\n      else if (type == BX_THUMB16) {\n          value = pc;\n        }\n\n      respair[idx++] = 0xF8DF;\n      respair[idx++] = 0xF000;\t// LDR.W PC, [PC]\n      respair[idx++] = valueToPc(value) & 0xFFFF;\n      respair[idx++] = valueToPc(value) >> 16;\n      offset = idx;\n    }\n  else if (type == ADD_THUMB16) {\n      int rdn;\n      int rm;\n      int r;\n\n      rdn = ((instruction & 0x80) >> 4) | (instruction & 0x7);\n\n      for (r = 7; ; --r) {\n          if (r != rdn) {\n              break;\n            }\n        }\n\n      respair[0] = 0xB400 | (1 << r);\t// PUSH {Rr}\n      respair[1] = 0x4802 | (r << 8);\t// LDR Rr, [PC, #8]\n      respair[2] = (instruction & 0xFF87) | (r << 3);\n      respair[3] = 0xBC00 | (1 << r);\t// POP {Rr}\n      respair[4] = 0xE002;\t// B PC, #4\n      respair[5] = 0xBF00;\n      respair[6] = pc & 0xFFFF;\n      respair[7] = pc >> 16;\n      offset = 8;\n    }\n  else if (type == MOV_THUMB16 || type == ADR_THUMB16 || type == LDR_THUMB16) {\n      int r;\n      uint32_t value;\n\n      if (type == MOV_THUMB16) {\n          r = instruction & 0x7;\n          value = pc;\n        }\n      else if (type == ADR_THUMB16) {\n          r = (instruction & 0x700) >> 8;\n          value = ALIGN_PC(pc) + (instruction & 0xFF) << 2;\n        }\n      else {\n          r = (instruction & 0x700) >> 8;\n          value = ((uint32_t *) (ALIGN_PC(pc) + ((instruction & 0xFF) << 2)))[0];\n        }\n\n      respair[0] = 0x4800 | (r << 8);\t// LDR Rd, [PC]\n      respair[1] = 0xE001;\t// B PC, #2\n      respair[2] = value & 0xFFFF;\n      respair[3] = value >> 16;\n      offset = 4;\n    }\n  else {\n      respair[0] = instruction;\n      respair[1] = 0xBF00;  // NOP 方便接下来构造thumb2指令\n      offset = 2;\n    }\n\n  return offset;\n}\nvoid GodinHook::ThumbInstruction::repairBackInstructionsOfStub(HookInfo * info, size_t *calloriginal)\n{\n  size_t originalAddress = info->getOriginalAddr();\n  uint8_t *back = info->getOriginalStubBack();\n\n  uint16_t * ins = (uint16_t *)back;\n  uint16_t * repair = (uint16_t *)calloriginal;\n  int backlen = sizeofStub();\n  if(NULL == repair)\n    return;\n  int pos = 0;\n  int repair_pos = 0;\n\n  /// 得到原始指令起始处的pc值\n  size_t originalPc = valueToMem(originalAddress) + 4;\n  size_t originalLr = 0;\n\n  /**\n   * 需要修正的是那些机器指令内部存储的是要操作数据基于当前PC的偏移值；\n   * 修正思路，计算出绝对地址，构造跳转指令。\n   * 这里还要判断是常规的16位thumb指令，还是32位的thumb2指令\n   */\n\n  while(true){\n    int offset = 0;\n    /// 为了线程安全\n    info->orig_boundaries[info->count] = pos * sizeof(uint16_t);\n    info->trampoline_boundaries[info->count] = repair_pos * sizeof(uint16_t);\n    info->count +=1;\n\n    if(isThumb2Instruction(ins[pos])){\n      offset = repairThumb32Instruction(originalPc,ins[pos],ins[pos+1],&repair[repair_pos]);\n      originalPc += 4;\n      repair_pos += offset;\n      pos += 2;\n    }else{\n    /// thumb16\n      offset = repairThumb16Instruction(originalPc,ins[pos],&repair[repair_pos]);\n      originalPc += 2;\n      repair_pos += offset;\n      pos +=1;\n    }\n    if(pos >= sizeofStub()/2){\n        break;\n    }\n\n  }\n\n  /// 为了保险起见，再一次做判断\n  if((size_t)(&repair[repair_pos]) % 4 != 0){\n      repair[repair_pos ] = 0xBF00;\n      repair_pos +=1;\n//      printf(\"-------------------->>> 0x%x\\n\",&repair[repair_pos]);\n//      printf(\"-------------------->>> 0x%x\\n\",&repair[0]);\n//      printf(\"-------------------->>> %d\\n\",repair_pos);\n  }\n//  printf(\"-------------------->>> %d\\n\",repair_pos);\n  originalLr = valueToMem(originalAddress) + sizeofStub() + 1;\n  repair[repair_pos ] = 0xF8DF;\n  repair[repair_pos +1] = 0xF000;\t// LDR.W PC, [PC]\n  repair[repair_pos +2] = originalLr & 0xFFFF;\n  repair[repair_pos +3] = originalLr >> 16;\n}\n\nint GodinHook::ThumbInstruction::sizeofStub()\n{\n  return stub_len_;\n}\n\nvoid GodinHook::ThumbInstruction::isResetStubSize(size_t originalAddress)\n{\n  size_t original = valueToMem(originalAddress);\n  uint16_t * ins = (uint16_t *)original;\n  if(!isPcNeedAlgin(original)){\n//      printf(\"ins[3]===0x%x\\n\",ins[3]);\n//      printf(\"ins[4]===0x%x\\n\",ins[4]);\n      if(((ins[3] & 0xf000)==0xf000) && ((ins[4] & 0xc000)==0xc000))\n        setStubSize(10);//调用原函数时，需要特殊处理\n      else\n        setStubSize(8);\n  }\n  else{\n      if(((ins[4] & 0xf000)==0xf000) && ((ins[5] & 0xc000)==0xc000))\n         setStubSize(12);\n      else\n         setStubSize(10);//调用原函数时，需要特殊处理\n  }\n\n}\n\nbool GodinHook::ThumbInstruction::isPcNeedAlgin(size_t address)\n{\n  if(NULL == address)\n    return false;\n  /// 此时表示bit[1]为1\n  if(address % 4 != 0)\n    return true;\n  else\n    return false;\n}\n\nbool GodinHook::ThumbInstruction::isThumb2Instruction(uint16_t ins)\n{\n  if(((ins >> 11)>=0x1d) && ((ins >> 11)<= 0x1f))\n    return true;\n  else\n    return false;\n}\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/instruction/thumb_instruction.h",
    "content": "#ifndef THUMB2_INSTRUCTION_H\n#define THUMB2_INSTRUCTION_H\n\n#include \"instruction_helper.h\"\n\n\n\nnamespace GodinHook {\n  /**\n   * @brief The Thumb2Instruction class\n   * 仅支持ARMv5T之后支持thumb2指令集；\n   *\n   * 另外thumb2指令集中的ldr指令需要4字节对齐；\n   * 传入pc中的指令地址末尾bit要为1；\n   *\n   */\n  class ThumbInstruction :public InstructionHelper{\n\n\n    // InstructionHelper interface\n  public:\n   ThumbInstruction():stub_len_(12){}\n\n   /**\n    * @brief sizeofStub\n    *  计算stub指令所需空间大小，单位字节\n    * @return\n    *  返回stub所需字节数\n    */\n    void createStub(HookInfo * info);\n\n\n    void *createCallOriginalIns(HookInfo * info);\n    int getRepairInstruction(size_t ins);\n    void repairBackInstructionsOfStub(HookInfo * info,size_t * calloriginal);\n    /**\n     * @brief sizeofStub\n     * 计算thumb中stub块大小；\n     *\n     * @return\n     * 返回stub块所需字节数\n     */\n    int sizeofStub();\n    void isResetStubSize(size_t originalAddress);\n  private:\n    int repairThumb32Instruction(uint32_t pc, uint16_t high_instruction, uint16_t low_instruction, uint16_t *respair);\n    int repairThumb16Instruction(uint32_t pc, uint16_t instruction, uint16_t *respair);\n    bool isPcNeedAlgin(size_t address);\n\n    /**\n     * @brief isThumb2Instruction\n     * 判断当前指令是thumb还是thumb2\n     * @param ins\n     * 指令\n     * @return\n     * 返回true是thumb2;\n     * 返回false是thumb;\n     */\n    bool isThumb2Instruction(uint16_t ins);\n\n    void setStubSize(int len)\n    {\n      stub_len_ = len;\n    }\n\n    int stub_len_;\n    enum RepairIns{\n          // B <label>\n          B1_THUMB16 = 0,\n          // B <label>\n          B2_THUMB16,\n          // BX PC\n          BX_THUMB16,\n          // ADD <Rdn>, PC (Rd != PC, Rn != PC) 在对ADD进行修正时，\n          //采用了替换PC为Rr的方法，当Rd也为PC时，由于之前更改了Rr的值，\n          //可能会影响跳转后的正常功能。\n          ADD_THUMB16,\n          // MOV Rd, PC\n          MOV_THUMB16,\n          // ADR Rd, <label>\n          ADR_THUMB16,\n          // LDR Rt, <label>\n          LDR_THUMB16,\n\n          // BLX <label>\n          BLX_THUMB32,\n          // BL <label>\n          BL_THUMB32,\n          // B.W <label>\n          B1_THUMB32,\n          // B.W <label>\n          B2_THUMB32,\n          // ADR.W Rd, <label>\n          ADR1_THUMB32,\n          // ADR.W Rd, <label>\n          ADR2_THUMB32,\n          // LDR.W Rt, <label>\n          LDR_THUMB32,\n          // TBB [PC, Rm]\n          TBB_THUMB32,\n          // TBH [PC, Rm, LSL #1]\n          TBH_THUMB32,\n\n          UNDEFINE,\n    };\n  };\n}\n\n\n\n#endif // THUMB2_INSTRUCTION_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/mem_helper.cpp",
    "content": "#include <mem_helper.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/mman.h>\n#include <stdlib.h>\n#include <asm/unistd.h>\n#include <sys/syscall.h>\n\n\nusing namespace std;\n\n#define maps \"/proc/self/maps\"\n#define MAX_BUF 512\n\nbool GodinHook::MemHelper::isFunctionAddr(size_t addr)\n{\n  char buf[MAX_BUF]={0};\n\n     FILE * fp = fopen(maps,\"r\");\n\n     if(NULL == fp){\n         return false;\n     }\n\n     while(fgets(buf,MAX_BUF,fp)){\n\n         /*\n          *可执行程序和so库对应的属性段是“r-xp”\n          * */\n         if(strstr(buf,\"r-xp\")!=NULL){\n            size_t startAddr = strtoul(strtok(buf,\"-\"),NULL,16);\n            size_t endAddr   = strtoul(strtok(NULL,\" \"),NULL,16);\n             if(addr>=startAddr && addr<=endAddr){\n                 fclose(fp);\n                 //printf(\"startAddr = 0x%x \\n\",startAddr);\n                 //printf(\"endAddr = 0x%x \\n\",endAddr);\n                 return true;\n             }\n\n         }\n     }\n\n     fclose(fp);\n     perror(\"this functionAddr is not a function!\\n\");\n     return false;\n}\n\nbool GodinHook::MemHelper::unProtectMemory(size_t addr, int size)\n{\n  /// 获得当前系统的内存页大小\n  int pageSize = sysconf(_SC_PAGESIZE);\n\n  /// 计算所在内存页中的偏移\n  int align = addr % pageSize;\n\n  int ret  = mprotect((void*)(addr-align),(size_t)(size+align),PROT_READ|PROT_WRITE|PROT_EXEC);\n  if(-1 == ret){\n      perror(\"mprotect\");\n      return false;\n    }\n  return true;\n\n}\n\nbool GodinHook::MemHelper::protectMemory(size_t addr, int size)\n{\n  /// 获得当前系统的内存页大小\n  int pageSize = sysconf(_SC_PAGESIZE);\n\n  /// 计算所在内存页的偏移\n  int align = addr % pageSize;\n\n  int ret  = syscall(__NR_mprotect,(void*)(addr-align),(size_t)(size+align),PROT_READ|PROT_EXEC);\n  if(-1 == ret){\n      perror(\"mprotect\");\n      return false;\n    }\n  return true;\n\n}\n\nvoid *GodinHook::MemHelper::createExecMemory()\n{\n  /// 获得当前系统的内存页大小\n  int pageSize = sysconf(_SC_PAGESIZE);\n\n  return mmap(NULL,pageSize,PROT_READ | PROT_WRITE | PROT_EXEC,MAP_ANONYMOUS | MAP_PRIVATE,0,0);\n}\n\nvoid GodinHook::MemHelper::freeExecMemory(void *address)\n{\n  /// 获得当前系统的内存页大小\n  int pageSize = sysconf(_SC_PAGESIZE);\n  munmap(address,pageSize);\n}\n\n\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/mem_helper.h",
    "content": "#ifndef MEMHELPER_H\n#define MEMHELPER_H\n\n#include <fstream>\n#include <sys/mman.h>\n\nnamespace GodinHook {\n  class MemHelper{\n  public:\n    /**\n     * @brief isFunctionAddr\n     *  判断所给地址是否是一个函数地址\n     * @param addr\n     *\n     * @return\n     *  是函数地址返回true，反之返回false。\n     */\n    static bool isFunctionAddr(size_t addr);\n\n    /**\n     * @brief unProtectMemory\n     *  将起始地址处开始的size大小去保护，即添加写权限。\n     *  此函数可能真正去保护的部分要比size大。\n     *\n     * @param addr\n     *  起始地址\n     * @param size\n     *  要去保护的内存空间大小\n     *\n     * @return\n     *  执行成功返回true，失败返回false\n     */\n    static bool unProtectMemory(size_t addr,int size);\n\n    /**\n     * @brief protectMemory\n     *  将起始地址处开始的size大小添加保护，即取消写权限。\n     *  此函数可能真正添加保护的部分要比size大。\n     *\n     * @param addr\n     *  起始地址\n     * @param size\n     *  要添加保护的内存空间大小\n     *\n     * @return\n     *  执行成功返回true，失败返回false\n     */\n    static bool protectMemory(size_t addr,int size);\n\n    /**\n     * @brief createExecMemory\n     * 创建一个内存页大小的可执行内存区域\n     * @return\n     * 可执行区域的起始地址\n     */\n    static void * createExecMemory();\n\n    /**\n     * @brief freeExecMemory\n     * 释放一个内存页大小的可执行区域\n     * @param address\n     * 要释放的可执行内存起始地址\n     */\n    static void freeExecMemory(void * address);\n\n  };\n}\n#endif // MEMHELPER_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/native_hook.cpp",
    "content": "\n\n#include \"native_hook.h\"\n#include \"mem_helper.h\"\n#include \"instruction/instruction_helper.h\"\n#include \"instruction/arm_instruction.h\"\n#include \"instruction/thumb_instruction.h\"\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include \"thread_helper.h\"\n\n\n\nGodinHook::NativeHook::hook_map GodinHook::NativeHook::hook_map_;\n\nint GodinHook::NativeHook::registeredHook(size_t originalFunAddress, size_t newFunAddress, size_t **callOriginal)\n{\n  bool flag = false;\n\n  /// 首先判断original和new为函数地址\n  if (!MemHelper::isFunctionAddr(originalFunAddress) || !MemHelper::isFunctionAddr(newFunAddress))\n    return GODINHOOK_ERROR_NOT_EXECUTABLE;\n\n  /// 尝试得到HookInfo\n  HookInfo * info = NULL;\n  info = getHookInfo(originalFunAddress);\n\n  if(NULL != info){\n      /// 判断original的hook状态\n      HookStatus hookStatus = info->getHookStatus();\n      if(HOOKED == hookStatus)\n        return GODINHOOK_ERROR_ALREADY_HOOKED;\n      if(REGISTERED == hookStatus)\n        return GODINHOOK_ERROR_ALREADY_REGISTERED;\n  }\n\n  /// info == NULL\n  /// 没有注册过，需要创建和初始化HookInfo对象，并添加到hook_map_中\n  info = new HookInfo(originalFunAddress,newFunAddress,callOriginal);\n  if(NULL == info)\n    return GODINHOOK_ERROR_MEMORY;\n\n  /// 设置original方法指令集\n  FunctionType type = InstructionHelper::getFunctionType(originalFunAddress);\n  if(ERRTYEPE == type)\n    return false;\n  info->setOriginalFunctionType(type);\n\n  /// 设置hook方法指令集\n  info->setHookFunctionType(InstructionHelper::getFunctionType(newFunAddress));\n\n\n  /// 创建指令集处理对象\n  InstructionHelper *insHelper = NULL ;\n  if(ARM == type){\n      insHelper =  new ArmInstruction();\n     printf(\"arm----------------\\n\");\n  }else if(THUMB == type){\n     insHelper = new ThumbInstruction();\n     insHelper->isResetStubSize(originalFunAddress);\n     printf(\"thumb---------len-----%d--\\n\",insHelper->sizeofStub());\n  }else if(ARM64 == type){\n      // TODO\n  }\n  /// 备份stub覆盖的指令\n  uint8_t *back = insHelper->getBackOfStub(InstructionHelper::valueToMem(originalFunAddress));\n  if(NULL == back){\n    free(insHelper);\n    return GODINHOOK_ERROR_MEMORY;\n  }\n  /// 设置hookinfo\n  info->setBackLen(insHelper->sizeofStub());\n  info->setOriginalStubBack(back);\n\n\n  /// 创建call original instruction ins\n\n  void * callOriginalFun = insHelper->createCallOriginalIns(info);\n  if(NULL == callOriginalFun){\n    free(back);\n    free(insHelper);\n    return GODINHOOK_ERROR_MEMORY;\n  }\n  info->setCallOriginalIns((uint8_t *) callOriginalFun);\n\n  /// 修改hookstatus\n  addHookInfo(info);\n  info->setHookStatus(REGISTERED);\n\n  free(insHelper);\n  return GODINHOOK_OK;\n}\n\n\n\nint GodinHook::NativeHook::hook(size_t originalFunAddress)\n{\n\n  HookInfo * info = getHookInfo(originalFunAddress);\n\n  if(NULL == info)\n    return GODINHOOK_ERROR_NOT_REGISTERED;\n  if(info->getHookStatus() == HOOKED)\n    return GODINHOOK_ERROR_ALREADY_HOOKED;\n  else if(info->getHookStatus() == REGISTERED){\n\n    /// 暂停当前其他线程；\n    /// 并且纠正正在运行被hook的函数的线程的PC\n\n    pid_t pid = ThreadHealper::freezzAndRepairThread(info,ACTION_ENABLE);\n\n    /// 进行hook\n    if(Hook(info)){\n      /// 恢复当前其他线程\n      ThreadHealper::unFreeze(pid);\n      return GODINHOOK_OK;\n    }\n    else{\n      /// 恢复当前其他线程\n      ThreadHealper::unFreeze(pid);\n      return GODINHOOK_ERROR_MEMORY;\n    }\n  }else\n    return GODINHOOK_ERROR_UNKNOWN;\n}\n\nvoid *GodinHook::NativeHook::isAlreadyHooked(size_t originalFunAddress)\n{\n  hook_map::iterator it = hook_map_.find(originalFunAddress);\n  if(it == hook_map_.end())\n    return NULL;\n  else {\n      HookInfo * info = it->second;\n      if(NULL != info && (info->getHookAddr() != NULL))\n       return (void *) info->getHookAddr();\n  }\n  return NULL;\n}\n\nint GodinHook::NativeHook::getHookedCount()\n{\n  return hook_map_.size();\n}\n\nbool GodinHook::NativeHook::unHook(size_t originalFunAddress)\n{\n  hook_map::iterator it = hook_map_.find(originalFunAddress);\n  if(it == hook_map_.end())\n    return true;\n  else{\n       HookInfo * info = it->second;\n       if(info != NULL || info->getHookStatus() == HOOKED){\n          size_t addr = InstructionHelper::valueToMem(originalFunAddress);\n\n          pid_t pid;\n          int i;\n          /// 冻结线程\n          pid = ThreadHealper::freezzAndRepairThread(info, ACTION_DISABLE);\n          /// 去保护,还原，加保护\n          if(MemHelper::unProtectMemory(addr,info->getBackLen())){\n              memcpy((void *) addr, info->getOriginalStubBack(), info->getBackLen());\n              MemHelper::protectMemory(addr,info->getBackLen());\n\n           /// 刷新指令缓存\n           cacheflush(addr, addr+info->getBackLen(), 0);\n\n           ///恢复线程\n           ThreadHealper::unFreeze(pid);\n           /// 释放资源\n            if(info->getCallOriginalIns() != NULL)\n              MemHelper::freeExecMemory(info->getCallOriginalIns());\n            if(info->getOriginalStubBack() !=NULL )\n              free(info->getOriginalStubBack());\n            if(info->getCallOriginalAddr() !=NULL )\n              (*(info->getCallOriginalAddr()))=NULL;\n            hook_map_.erase(it);\n            free(info);\n            info = NULL;\n            return true;\n        }else\n            return false;\n\n      }\n  }\n  return false;\n}\n\nGodinHook::HookInfo **GodinHook::NativeHook::getAllHookInfo()\n{\n  int count = getHookedCount();\n\n  HookInfo ** infos = (HookInfo **) calloc(count, sizeof(HookInfo*));\n\n  hook_map::iterator it = hook_map_.begin();\n  for(int i=0;it!=hook_map_.end();++it,++i){\n    infos[i] = it->second;\n  }\n  return infos;\n}\n\nvoid GodinHook::NativeHook::hookAllRegistered()\n{\n  pid_t pid;\n  int i;\n  pid = ThreadHealper::freezzAndRepairThread(NULL, ACTION_ENABLE);\n  HookInfo ** infos = NativeHook::getAllHookInfo();\n  for (i = 0; i < getHookedCount(); ++i) {\n      if (infos[i]->getHookStatus() == REGISTERED) {\n         Hook(infos[i]);\n        }\n    }\n  ThreadHealper::unFreeze(pid);\n}\n\nvoid GodinHook::NativeHook::unHookAll()\n{\n  pid_t pid;\n  int i;\n\n  pid = ThreadHealper::freezzAndRepairThread(NULL, ACTION_DISABLE);\n  HookInfo ** infos = NativeHook::getAllHookInfo();\n  int count = getHookedCount();\n  for (i = 0; i < count; ++i) {\n      if (infos[i]->getHookStatus() == HOOKED) {\n        // printf(\"-------unhookall count %d\\n\",getHookedCount());\n         UnHook(infos[i]);\n        }\n    }\n  ThreadHealper::unFreeze(pid);\n  free(infos);\n}\n\nGodinHook::HookStatus GodinHook::NativeHook::getFunctionStatus(size_t functionAddr)\n{\n   hook_map::iterator it = hook_map_.find(functionAddr);\n   if(it == hook_map_.end())\n     return ERRSTATUS;\n   else {\n       HookInfo * info = it->second;\n       if(NULL != info)\n        return info->getHookStatus();\n   }\n   return ERRSTATUS;\n}\n\nvoid GodinHook::NativeHook::addHookInfo(GodinHook::HookInfo *info)\n{\n  if(NULL == info)\n   return;\n  hook_map_.insert(hook_map::value_type(info->getOriginalAddr(),info));\n}\n\nbool GodinHook::NativeHook::Hook(HookInfo *info)\n{\n\n  /// 获取original方法指令集\n  FunctionType type = info->getOriginalFunctiontype();\n  if(ERRTYEPE == type)\n    return false;\n\n  /// 创建指令集处理对象\n  InstructionHelper *insHelper = NULL ;\n  if(ARM == type){\n      insHelper =  new ArmInstruction();\n  }else if(THUMB == type){\n     insHelper = new ThumbInstruction();\n     insHelper->isResetStubSize(info->getOriginalAddr());\n  }else if(ARM64 == type){\n\n  }\n\n  /// 创建stub\n  insHelper->createStub(info);\n\n  /// 创建call original instruction\n  /// 此时说明使用者不需要回调原方法\n  if(NULL == info->getCallOriginalAddr())\n    return true;\n  else{\n      void * callOriginal =info->getCallOriginalIns();\n      if(THUMB == type)\n        *(info->getCallOriginalAddr()) = (size_t *) InstructionHelper::valueToPc((size_t)callOriginal);\n      else\n        *(info->getCallOriginalAddr()) = (size_t *) callOriginal;\n  }\n  /// 设置状态\n  info->setHookStatus(HOOKED);\n  /// 再次刷新指令缓存\n  cacheflush((long) InstructionHelper::valueToMem(info->getOriginalAddr()),\n             (long) (InstructionHelper::valueToMem(info->getOriginalAddr()) + 12), 0);\n  free(insHelper);\n  return true;\n}\n\nbool GodinHook::NativeHook::UnHook(GodinHook::HookInfo *info)\n{\n  size_t addr = InstructionHelper::valueToMem(info->getOriginalAddr());\n  /// 去保护,还原，加保护\n  if(MemHelper::unProtectMemory(addr,info->getBackLen())){\n        memcpy((void *) addr, info->getOriginalStubBack(), info->getBackLen());\n        MemHelper::protectMemory(addr,info->getBackLen());\n\n        /// 刷新指令缓存\n        cacheflush(addr, (long) (addr + info->getBackLen()), 0);\n\n        /// 释放资源\n        if(info->getCallOriginalIns() != NULL)\n          MemHelper::freeExecMemory(info->getCallOriginalIns());\n        if(info->getOriginalStubBack() !=NULL )\n          free(info->getOriginalStubBack());\n        if(info->getCallOriginalAddr() !=NULL )\n          (*(info->getCallOriginalAddr()))=NULL;\n\n        hook_map::iterator it = hook_map_.find(info->getOriginalAddr());\n        if(it != hook_map_.end())\n          hook_map_.erase(it);\n        free(info);\n        info = NULL;\n        return true;\n   }else\n    return false;\n}\n\nGodinHook::HookInfo *GodinHook::NativeHook::getHookInfo(size_t functionAddr)\n{\n  hook_map::iterator it = hook_map_.find(functionAddr);\n  if(it == hook_map_.end())\n    return NULL;\n  else {\n      HookInfo * info = it->second;\n      if(NULL != info)\n       return info;\n  }\n  return NULL;\n}\n\n\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/native_hook.h",
    "content": "#ifndef NATIVE_HOOK_H\n#define NATIVE_HOOK_H\n\n#include <map>\n#include <string>\n#include <hookinfo.h>\n\nusing namespace std;\n\nnamespace  GodinHook {\n\n  enum GODINHOOK_STATUS {\n    GODINHOOK_ERROR_UNKNOWN = -1,\n    GODINHOOK_OK = 0,\n    GODINHOOK_ERROR_NOT_INITIALIZED,\n    GODINHOOK_ERROR_NOT_EXECUTABLE,\n    GODINHOOK_ERROR_NOT_REGISTERED,\n    GODINHOOK_ERROR_NOT_HOOKED,\n    GODINHOOK_ERROR_ALREADY_REGISTERED,\n    GODINHOOK_ERROR_ALREADY_HOOKED,\n    GODINHOOK_ERROR_SO_NOT_FOUND,\n    GODINHOOK_ERROR_FUNCTION_NOT_FOUND,\n    GODINHOOK_ERROR_MEMORY\n  };\n\n  class NativeHook{\n\n  public:\n\n    /**\n     * @brief registeredHook\n     *  注册成功后，就对相关的指令进行了修正，以及创建好回调原方法的环境，主要是为了线程安全做准备。\n     *\n     * @param originalFunAddress\n     *  原函数地址\n     * @param newFunAddress\n     *  新函数地址\n     * @param callOriginal\n     *  存储回调原函数的地址\n     *\n     * @return\n     *  返回 GODINHOOK_OK，表示注册成功;\n     *  返回 GODINHOOK_ERROR_ALREADY_REGISTERED，表示已经注册；\n     *  返回 GODINHOOK_ERROR_ALREADY_HOOKED，表示已经hook.\n     *  返回其他,则注册失败。\n     */\n    static int registeredHook(size_t originalFunAddress,size_t newFunAddress,size_t ** callOriginal);\n\n    /**\n     * @brief hook\n     *  对外提供的hook函数的接口，执行成功即完成了hook。\n     *\n     *  此方法中要对当前进程中的线程进行检查，并对正在运行被hook的\n     *  函数的线程，进行必要的修正。\n     *\n     * @param originalFunAddress\n     *  原函数地址\n     *\n     * @return\n     *  返回 GODINHOOK_OK，表示hook成功;\n     *  返回 GODINHOOK_ERROR_ALREADY_HOOKED，表示已经hook.\n     *  返回其他，则hook失败\n     */\n    static int hook(size_t originalFunAddress);\n\n    /**\n     * @brief isAlreadyHooked\n     * 判断该方法是否被hook了\n     * @param originalFunAddress\n     * 方法地址\n     * @return\n     * NULL，表明未被HOOK；\n     * 否则返回hook函数的地址；\n     */\n    static void* isAlreadyHooked(size_t originalFunAddress);\n\n    /**\n     * @brief getHookedCount\n     * 当前被hook的方法的数量\n     * @return\n     * 返回当前被hook的方法的数量\n     */\n    static int getHookedCount();\n\n    /**\n     * @brief unHook\n     * 卸载hook\n     * @param originalFunAddress\n     * 原方法地址\n     * @return\n     * true表示卸载成功；\n     * 反之卸载失败。\n     */\n    static bool unHook(size_t originalFunAddress);\n\n\n    /**\n     * @brief getAllHookInfo\n     * 得到当前所有的HookInfo,需要手动释放返回值指向的空间\n     * @return\n     * 返回存储有当前所有HookInfo地址的数组\n     */\n    static HookInfo ** getAllHookInfo();\n\n    /**\n     * @brief hookAllRegistered\n     * 将注册的所有方法进行hook操作\n     */\n    static void hookAllRegistered();\n\n    /**\n     * @brief unHookAll\n     * 卸载所有的hook\n     */\n    static void unHookAll();\n\n\n\n  private:\n    /**\n     * @brief getFunctionStatus\n     *  获取函数当前的状态\n     *\n     * @param functionAddr\n     *  必须是一个函数的地址\n     *\n     * @return\n     * UNREGISTERED,REGISTERED,HOOKED or UNHOOK.\n     */\n    static HookStatus getFunctionStatus(size_t functionAddr);\n\n    /**\n     * @brief addHookInfo\n     *  向hook_map_中添加新成员\n     * @param info\n     *  要加入的hook_map_的info\n     */\n    static void addHookInfo(HookInfo * info);\n\n    /**\n     * @brief registerAndHook\n     * 注册并进行hook操作\n     * @param info\n     * HookInfo类型的对象指针\n     * @return\n     * 成功返回true;\n     * 失败返回false.\n     */\n    static bool Hook(HookInfo* info);\n\n    static bool UnHook(HookInfo* info);\n\n\n    static HookInfo * getHookInfo(size_t functionAddr);\n\n  private:\n\n    /// 记录哪些方法被hook,key为原方法的地址\n    typedef map<size_t,HookInfo*> hook_map;\n    static hook_map hook_map_;\n  };\n\n}\n\n#endif // NATIVE_HOOK_H\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/thread_helper.cpp",
    "content": "#include \"thread_helper.h\"\n#include <sys/types.h>\n#include <dirent.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include \"instruction/instruction_helper.h\"\n#include \"native_hook.h\"\n\npid_t GodinHook::ThreadHealper::freezzAndRepairThread(GodinHook::HookInfo *info, int action)\n{\n  int count;\n  pid_t tids[1024];\n  pid_t pid;\n\n  pid = -1;\n  count = getAllTids(getpid(), tids);\n  if (count > 0) {\n\n      /// 创建进程\n      pid = fork();\n\n      /// 子进程进行冻结和修复工作\n      if (pid == 0) {\n          int i;\n\n          for (i = 0; i < count; ++i) {\n              if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) {\n                  //子进程进入暂停，立即返回\n                  waitpid(tids[i], NULL, WUNTRACED);\n                  repairThreadPc(tids[i], info, action);\n                }\n            }\n          //把信号发送给(进程)自身.\n          raise(SIGSTOP);\n\n          // 收到SIGCONT后，解除冻结状态\n          for (i = 0; i < count; ++i) {\n              ptrace(PTRACE_DETACH, tids[i], NULL, NULL);\n            }\n\n          exit(0);\n        }\n      else if (pid > 0) {\n          //等待前面创建的子进程暂停\n          waitpid(pid, NULL, WUNTRACED);\n        }\n    }\n\n  return pid;\n}\n\nvoid GodinHook::ThreadHealper::unFreeze(pid_t pid)\n{\n  if(pid < 0)\n    return;\n\n  /// 向执行冻结工作的进程发送信号，使其继续执行\n  kill(pid,SIGCONT);\n\n  /// 等待子进程退出\n  waitpid(pid,NULL,0);\n\n}\n\nint GodinHook::ThreadHealper::getAllTids(pid_t pid, pid_t *tids)\n{\n  char dir_path[32];\n  DIR *dir;\n  int i;\n  struct dirent *entry;\n  pid_t tid;\n\n  if (pid < 0) {\n      snprintf(dir_path, sizeof(dir_path), \"/proc/self/task\");\n    }\n  else {\n      snprintf(dir_path, sizeof(dir_path), \"/proc/%d/task\", pid);\n    }\n\n  dir = opendir(dir_path);\n  if (dir == NULL) {\n      return 0;\n    }\n\n  i = 0;\n  while((entry = readdir(dir)) != NULL) {\n      tid = atoi(entry->d_name);\n      if (tid != 0 && tid != getpid()) {\n          tids[i++] = tid;\n        }\n    }\n  closedir(dir);\n  return i;\n}\n\nvoid GodinHook::ThreadHealper::repairThreadPc(pid_t tid, GodinHook::HookInfo *info, int action)\n{\n  struct pt_regs regs;\n\n  if((NULL != info) || NativeHook::getHookedCount()>0){\n      if (ptrace(PTRACE_GETREGS, tid, NULL, &regs) == 0) {\n          if (info == NULL) {\n              int pos;\n              HookInfo ** infos = NativeHook::getAllHookInfo();\n              for (pos = 0; pos < NativeHook::getHookedCount(); ++pos) {\n                  if (doRepairThreadPC(infos[pos], &regs, action) == true) {\n                      break;\n                    }\n                }\n              free(infos);\n            }\n          else {\n              doRepairThreadPC(info, &regs, action);\n            }\n\n          ptrace(PTRACE_SETREGS, tid, NULL, &regs);\n        }\n   }\n}\n\nbool GodinHook::ThreadHealper::doRepairThreadPC(GodinHook::HookInfo *info, pt_regs *regs, int action)\n{\n  int offset;\n  int i;\n  switch (action)\n    {\n    /// 进行hook的时候，线程执行到正在被hook的函数，将其纠正到hook框架构建的调用原方法的对应机器指令上\n    case ACTION_ENABLE:\n      offset = regs->ARM_pc - InstructionHelper::valueToMem(info->getOriginalAddr());\n      for (i = 0; i < info->count; ++i) {\n          if (offset == info->orig_boundaries[i]) {\n              regs->ARM_pc = (uint32_t) info->getCallOriginalIns()+ info->trampoline_boundaries[i];\n              return true;\n            }\n        }\n      break;\n    ///  进行unhook的时候，线程正在执行被hook函数的新函数，将其纠正到unhook之后原方法的机器指令上\n    case ACTION_DISABLE:\n      offset = regs->ARM_pc - (int) info->getCallOriginalIns();\n      for (i = 0; i < info->count; ++i) {\n          if (offset == info->trampoline_boundaries[i]) {\n              regs->ARM_pc = InstructionHelper::valueToMem(info->getOriginalAddr()) + info->orig_boundaries[i];\n              return true;\n            }\n        }\n      break;\n    }\n\n  return false;\n}\n\n\n"
  },
  {
    "path": "lib/src/main/jni/GodinHook/thread_helper.h",
    "content": "#ifndef THREAD_HEALPER_H\n#define THREAD_HEALPER_H\n\n#include <sys/wait.h>\n#include <sys/ptrace.h>\n#include \"hookinfo.h\"\n\n\nnamespace GodinHook {\n\n  #define ACTION_ENABLE         0\n  #define ACTION_DISABLE\t1\n\n  class ThreadHealper{\n\n  public:\n    /**\n     * @brief freezzAndRepairThread\n     * 冻结和修复线程\n     * @param info\n     * 当HookInfo为NULL时，表示检查所有的HookInfo\n     * @param action\n     * ACTION_ENABLE或者ACTION_DISABLE\n     * @return\n     * 返回执行冻结和修复工作的进程ID\n     */\n    static pid_t freezzAndRepairThread(HookInfo * info,int action);\n\n    /**\n     * @brief unFreeze\n     * 解除冻结状态，线程恢复运行\n     * @param pid\n     * 执行冻结工作的进程\n     */\n    static void unFreeze(pid_t pid);\n  private:\n\n    /**\n     * @brief getAllTids\n     * 获取当前进程中除主线程之外的所有线程\n     * @param pid\n     * 当前进程\n     * @param tids\n     * 存储线程号的数组\n     * @return\n     * 除主线程之外的其他线程的数量\n     */\n    static int getAllTids(pid_t pid, pid_t *tids);\n\n    static void repairThreadPc(pid_t tid, HookInfo *info, int action);\n\n    static bool doRepairThreadPC(HookInfo *info, struct pt_regs *regs, int action);\n\n\n\n  };\n\n}\n\n#endif // THREAD_HEALPER_H\n"
  },
  {
    "path": "lib/src/main/jni/Helper.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef NDK_LOG_H\n#define NDK_LOG_H\n\n#include <android/log.h>\n\n#define TAG \"VA-Native\"\n\n#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,  TAG, __VA_ARGS__)\n#define LOGDT(T, ...) __android_log_print(ANDROID_LOG_DEBUG,  T, __VA_ARGS__)\n#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,  TAG, __VA_ARGS__)\n#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,  TAG, __VA_ARGS__)\n#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)\n\n#define FREE(ptr, org_ptr) { if ((void*) ptr != NULL && (void*) ptr != (void*) org_ptr) { free((void*) ptr); } }\n\n#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))\n\n#define NATIVE_METHOD(func_ptr, func_name, signature) { func_name, signature, reinterpret_cast<void*>(func_ptr) }\n\n#define JAVA_CLASS \"com/lody/virtual/client/NativeEngine\"\n\n#define ANDROID_JBMR2    18\n#define ANDROID_L        21\n#define ANDROID_N        24\n\n\n#endif //NDK_LOG_H\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/ARM.cpp",
    "content": "#include \"ARM.h\"\n#include \"PosixMemory.h\"\n\nvoid ARM::SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) {\n    if (symbol == NULL)\n        return;\n\n    uint32_t *area(reinterpret_cast<uint32_t *>(symbol));\n    uint32_t *arm(area);\n\n    const size_t used(8);\n\n    uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]};\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHexEx(area, used + sizeof(uint32_t), 4, name);\n    }\n\n    if (result != NULL) {\n\n    if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) {\n        *result = reinterpret_cast<void *>(backup[1]);\n        return;\n    }\n\n    size_t length(used);\n    for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)\n        if (A$pcrel$r(backup[offset])) {\n            if ((backup[offset] & 0x02000000) == 0 || (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f))\n                length += 2 * sizeof(uint32_t);\n            else\n                length += 4 * sizeof(uint32_t);\n        }\n\n    length += 2 * sizeof(uint32_t);\n\n    uint32_t *buffer(reinterpret_cast<uint32_t *>(mmap(\n        NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0\n    )));\n\n    if (buffer == MAP_FAILED) {\n        MSLog(MSLogLevelError, \"MS:Error:mmap() = %d\", errno);\n        *result = NULL;\n        return;\n    }\n\n    if (false) fail: {\n        munmap(buffer, length);\n        *result = NULL;\n        return;\n    }\n\n    size_t start(0), end(length / sizeof(uint32_t));\n    uint32_t *trailer(reinterpret_cast<uint32_t *>(buffer + end));\n    for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)\n        if (A$pcrel$r(backup[offset])) {\n            union {\n                uint32_t value;\n\n                struct {\n                    uint32_t rm : 4;\n                    uint32_t : 1;\n                    uint32_t shift : 2;\n                    uint32_t shiftamount : 5;\n                    uint32_t rd : 4;\n                    uint32_t rn : 4;\n                    uint32_t l : 1;\n                    uint32_t w : 1;\n                    uint32_t b : 1;\n                    uint32_t u : 1;\n                    uint32_t p : 1;\n                    uint32_t mode : 1;\n                    uint32_t type : 2;\n                    uint32_t cond : 4;\n                };\n            } bits = {backup[offset+0]}, copy(bits);\n\n            bool guard;\n            if (bits.mode == 0 || bits.rd != bits.rm) {\n                copy.rn = bits.rd;\n                guard = false;\n            } else {\n                copy.rn = bits.rm != A$r0 ? A$r0 : A$r1;\n                guard = true;\n            }\n\n            if (guard)\n                buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn));\n\n            buffer[start+0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, (end-1 - (start+0)) * 4 - 8);\n            buffer[start+1] = copy.value;\n\n            start += 2;\n\n            if (guard)\n                buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn));\n\n            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 8;\n            end -= 1;\n        } else\n            buffer[start++] = backup[offset];\n\n    buffer[start+0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n    buffer[start+1] = reinterpret_cast<uint32_t>(area + used / sizeof(uint32_t));\n\n    if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {\n        MSLog(MSLogLevelError, \"MS:Error:mprotect():%d\", errno);\n        goto fail;\n    }\n\n    *result = buffer;\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", *result);\n        MSLogHexEx(buffer, length, 4, name);\n    }\n\n    }\n\n    {\n        SubstrateHookMemory code(process, symbol, used);\n\n        arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n        arm[1] = reinterpret_cast<uint32_t>(replace);\n    }\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHexEx(area, used + sizeof(uint32_t), 4, name);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/ARM.h",
    "content": "/* Cydia Substrate - Powerful Code Insertion Platform\n * Copyright (C) 2008-2011  Jay Freeman (saurik)\n*/\n\n/* GNU Lesser General Public License, Version 3 {{{ */\n/*\n * Substrate is free software: you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the\n * Free Software Foundation, either version 3 of the License, or (at your\n * option) any later version.\n *\n * Substrate is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public\n * License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public License\n * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.\n**/\n/* }}} */\n\n#ifndef SUBSTRATE_ARM_HPP\n#define SUBSTRATE_ARM_HPP\n\n#include \"CydiaSubstrate.h\"\n#include \"Log.h\"\n#include \"Debug.h\"\n#include <sys/mman.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <errno.h>\n#include <stdint.h>\n\nenum A$r {\n    A$r0, A$r1, A$r2, A$r3,\n    A$r4, A$r5, A$r6, A$r7,\n    A$r8, A$r9, A$r10, A$r11,\n    A$r12, A$r13, A$r14, A$r15,\n    A$sp = A$r13,\n    A$lr = A$r14,\n    A$pc = A$r15\n};\n\nenum A$c {\n    A$eq, A$ne, A$cs, A$cc,\n    A$mi, A$pl, A$vs, A$vc,\n    A$hi, A$ls, A$ge, A$lt,\n    A$gt, A$le, A$al,\n    A$hs = A$cs,\n    A$lo = A$cc\n};\n\n#define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \\\n    (0xe10f0000 | ((rd) << 12))\n#define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \\\n    (0xe128f000 | (rm))\n#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \\\n    (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im))\n#define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \\\n    (0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im))\n#define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \\\n    (0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff))\n#define A$blx_rm(rm) /* blx rm */ \\\n    (0xe12fff30 | (rm))\n#define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \\\n    (0xe1a00000 | ((rd) << 12) | (rm))\n#define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \\\n    (0xe8b00000 | (A$sp << 16) | (rs))\n#define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \\\n    (0xe9200000 | (A$sp << 16) | (rs))\n#define A$stmia_sp$_$r0$  0xe8ad0001 /* stmia sp!, {r0}   */\n#define A$bx_r0           0xe12fff10 /* bx r0             */\n\nstatic inline bool A$pcrel$r(uint32_t ic) {\n\treturn (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000;\n}\n\nnamespace ARM{\n\textern \"C\" void SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result);\n}\n#endif//SUBSTRATE_ARM_HPP\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/CydiaSubstrate.h",
    "content": "#ifndef CYDIASUBSTRATE_H_\n#define CYDIASUBSTRATE_H_\n\n#include <dlfcn.h>\n#include <stdlib.h>\n\n#define _finline \\\n    inline __attribute__((__always_inline__))\n#define _disused \\\n    __attribute__((__unused__))\n#define _extern \\\n    extern \"C\" __attribute__((__visibility__(\"default\")))\n\n#include \"SubstrateStruct.h\"\n#endif /* CYDIASUBSTRATE_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/Debug.cpp",
    "content": "/* Cydia Substrate - Powerful Code Insertion Platform\n * Copyright (C) 2008-2011  Jay Freeman (saurik)\n*/\n\n/* GNU Lesser General Public License, Version 3 {{{ */\n/*\n * Substrate is free software: you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the\n * Free Software Foundation, either version 3 of the License, or (at your\n * option) any later version.\n *\n * Substrate is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public\n * License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public License\n * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.\n**/\n/* }}} */\n\n#include \"CydiaSubstrate.h\"\n#include \"Debug.h\"\n\n#include <stdio.h>\n\n_extern bool MSDebug;\nbool MSDebug = false;\n\nstatic char _MSHexChar(uint8_t value) {\n    return value < 0x20 || value >= 0x80 ? '.' : value;\n}\n\n#define HexWidth_ 16\n#define HexDepth_ 4\n\n\nvoid MSLogHexExInner(const void *vdata, size_t size, size_t stride, const char *mark) {\n    const uint8_t *data((const uint8_t *) vdata);\n\n    size_t i(0), j;\n\n    char d[256];\n    size_t b(0);\n    d[0] = '\\0';\n\n    while (i != size) {\n        if (i % HexWidth_ == 0) {\n            if (mark != NULL)\n                b += sprintf(d + b, \"[%s] \", mark);\n            b += sprintf(d + b, \"0x%.3zx:\", i);\n        }\n\n        b += sprintf(d + b, \" \");\n\n        for (size_t q(0); q != stride; ++q)\n            b += sprintf(d + b, \"%.2x\", data[i + stride - q - 1]);\n\n        i += stride;\n\n        for (size_t q(1); q != stride; ++q)\n            b += sprintf(d + b, \" \");\n\n        if (i % HexDepth_ == 0)\n            b += sprintf(d + b, \" \");\n\n        if (i % HexWidth_ == 0) {\n            b += sprintf(d + b, \" \");\n            for (j = i - HexWidth_; j != i; ++j)\n                b += sprintf(d + b, \"%c\", _MSHexChar(data[j]));\n\n            lprintf(\"%s\", d);\n            b = 0;\n            d[0] = '\\0';\n        }\n    }\n\n    if (i % HexWidth_ != 0) {\n        for (j = i % HexWidth_; j != HexWidth_; ++j)\n            b += sprintf(d + b, \"   \");\n        for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j)\n            b += sprintf(d + b, \" \");\n        b += sprintf(d + b, \" \");\n        for (j = i / HexWidth_ * HexWidth_; j != i; ++j)\n            b += sprintf(d + b, \"%c\", _MSHexChar(data[j]));\n\n        lprintf(\"%s\", d);\n        b = 0;\n        d[0] = '\\0';\n    }\n}\n\nvoid MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) {\n    if (MSDebug) {\n        MSLogHexExInner(vdata, size, stride, mark);\n    }\n}\n\nvoid MSLogHex(const void *vdata, size_t size, const char *mark) {\n    if (MSDebug) {\n        MSLogHexEx(vdata, size, 1, mark);\n    }\n}\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/Debug.h",
    "content": "/* Cydia Substrate - Powerful Code Insertion Platform\n * Copyright (C) 2008-2011  Jay Freeman (saurik)\n*/\n\n/* GNU Lesser General Public License, Version 3 {{{ */\n/*\n * Substrate is free software: you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the\n * Free Software Foundation, either version 3 of the License, or (at your\n * option) any later version.\n *\n * Substrate is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public\n * License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public License\n * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.\n**/\n/* }}} */\n\n#ifndef SUBSTRATE_DEBUG_HPP\n#define SUBSTRATE_DEBUG_HPP\n\n#include \"Log.h\"\n#include <stddef.h>\n#define lprintf(format, ...) \\\n    MSLog(MSLogLevelNotice, format, ## __VA_ARGS__)\n\nextern \"C\" bool MSDebug;\nvoid MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0);\nvoid MSLogHex(const void *vdata, size_t size, const char *mark = 0);\n\n#endif//SUBSTRATE_DEBUG_HPP\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/Hooker.cpp",
    "content": "#include <unistd.h>\n#include \"Hooker.h\"\n#include \"util.h\"\n#include \"ARM.h\"\n#include \"Thumb.h\"\n#include \"x86.h\"\n\nvoid Cydia::MSHookFunction(void *symbol, void *replace, void **result) {\n\n    SubstrateProcessRef process = NULL;\n    if (MSDebug){\n        MSLog(MSLogLevelNotice, \"SubstrateHookFunction(process:%p, symbol:%p, replace:%p, result:%p)\", process, symbol, replace, result);\n    }\n#if defined(__arm__) || defined(__thumb__)\n    if ((reinterpret_cast<uintptr_t>(symbol) & 0x1) == 0){\n        return ARM::SubstrateHookFunctionARM(process, symbol, replace, result);\n    }else{\n        return Thumb::SubstrateHookFunctionThumb(process, reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(symbol) & ~0x1), replace, result);\n    }\n#endif\n\n\n#if defined(__i386__) || defined(__x86_64__)\n    return x86::SubstrateHookFunctionx86(process, symbol, replace, result);\n#endif\n}\n\nvoid Cydia::MSHookFunction(const char *soname, const char *symbol, void *replace_func,\n                           void **old_func) {\n    void *addr = NULL;\n    if (find_name(getpid(), symbol, soname, (unsigned long *)&addr) < 0) {\n        MSLog(MSLogLevelError, \"Not found %s in %s.\", symbol, soname);\n        return;\n    }\n    Cydia::MSHookFunction(addr, replace_func, old_func);\n}"
  },
  {
    "path": "lib/src/main/jni/MSHook/Hooker.h",
    "content": "#ifndef HOOKER_H_\n#define HOOKER_H_\n\n#include <sys/mman.h>\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"Debug.h\"\n#include \"Log.h\"\n#include \"PosixMemory.h\"\n#include \"CydiaSubstrate.h\"\n\nnamespace Cydia{\n\n\tvoid MSHookFunction(const char *soname, const char *symbol, void *replace_func, void **old_func);\n\tvoid MSHookFunction(void *symbol, void *replace, void **result);\n}\n#endif /* HOOKER_H_ */"
  },
  {
    "path": "lib/src/main/jni/MSHook/Log.h",
    "content": "/* Cydia Substrate - Powerful Code Insertion Platform\n * Copyright (C) 2008-2011  Jay Freeman (saurik)\n*/\n\n/* GNU Lesser General Public License, Version 3 {{{ */\n/*\n * Substrate is free software: you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the\n * Free Software Foundation, either version 3 of the License, or (at your\n * option) any later version.\n *\n * Substrate is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public\n * License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public License\n * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.\n**/\n/* }}} */\n\n#ifndef SUBSTRATE_LOG_HPP\n#define SUBSTRATE_LOG_HPP\n\n#include <android/log.h>\n\n#define MSLogLevelNotice ANDROID_LOG_INFO\n#define MSLogLevelWarning ANDROID_LOG_WARN\n#define MSLogLevelError ANDROID_LOG_ERROR\n#define LOG_TAG \"zzz\"\n#define MS_DEBUG 1\n//#define MS_EXE_PRINTF 0\n#ifndef MS_LOG_TAG\n\t#define MS_LOG_TAG \"VA-Native\"\n#endif\n\n#if MS_DEBUG\n\t#ifdef MS_EXE_PRINTF\n\t\t#define MS_LOGD(fmt,...) printf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGI(fmt,...) printf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGV(fmt,...) printf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGW(fmt,...) printf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGE(fmt,...) printf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGF(fmt,...) printf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__)\n\n\t#else\n\t\t#define MS_LOGD(fmt,...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGI(fmt,...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGV(fmt,...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGW(fmt,...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGE(fmt,...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__)\n\t\t#define MS_LOGF(fmt,...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__)\n\t#endif\n#else\n\t#define MS_LOGD(...) while(0){}\n\t#define MS_LOGI(...) while(0){}\n\t#define MS_LOGV(...) while(0){}\n\t#define MS_LOGW(...) while(0){}\n\t#define MS_LOGE(...) while(0){}\n\t#define MS_LOGW(...) while(0){}\n#endif\n\n#define MSLog(level, fmt,...) do { \\\n\tprintf(\"[%12s] \" fmt \"\\n\", __FUNCTION__,##__VA_ARGS__); \\\n    __android_log_print(level, MS_LOG_TAG, \"[%s]\" fmt, __FUNCTION__,##__VA_ARGS__); \\\n} while (false)\n#endif//SUBSTRATE_LOG_HPP\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/MSHook.cpp",
    "content": "﻿#include <stdio.h>\n#include <unistd.h>\n#include \"util.h\"\n\n#include \"Hooker.h\"\n#include \"MSHook.h\"\n\n\nint inlineHook(const char *soname, const char *symbol, void *replace_func,\n               void **old_func) {\n    int ret = -1;\n    void *addr = NULL;\n    if (findSymbol(symbol, soname, (unsigned long *) &addr) < 0) {\n        return -1;\n    }\n    Cydia::MSHookFunction(addr, replace_func, old_func);\n    ret = 0;\n    return ret;\n}\n\nint findSymbol(const char *name, const char *libn,\n              unsigned long *addr) {\n    return find_name(getpid(), name, libn, addr);\n}\n\nint inlineHookDirect(unsigned int addr, void *replace_func, void **old_func) {\n    if (addr == 0) {\n        return -1;\n    }\n    Cydia::MSHookFunction((void *) addr, replace_func, old_func);\n    return 0;\n}\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/MSHook.h",
    "content": "#ifndef LIBHOOK_H_\n#define LIBHOOK_H_\n\n#define HOOK_FAILED -1\n#define HOOK_SUCCESS 0\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint findSymbol(const char *name, const char *libn,\n               unsigned long *addr);\nint inlineHook(const char *soname, const char *symbol, void *replace_func, void **old_func);\nint inlineHookDirect(unsigned int addr, void *replace_func, void **old_func);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* LIBHOOK_HOOK2_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/PosixMemory.cpp",
    "content": "/* Cydia Substrate - Powerful Code Insertion Platform\n * Copyright (C) 2008-2011  Jay Freeman (saurik)\n */\n\n/* GNU Lesser General Public License, Version 3 {{{ */\n/*\n * Substrate is free software: you can redistribute it and/or modify it under\n * the terms of the GNU Lesser General Public License as published by the\n * Free Software Foundation, either version 3 of the License, or (at your\n * option) any later version.\n *\n * Substrate is distributed in the hope that it will be useful, but WITHOUT\n * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public\n * License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public License\n * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.\n **/\n/* }}} */\n\n#include \"CydiaSubstrate.h\"\n#include \"PosixMemory.h\"\n#include \"Log.h\"\n\n#include <sys/mman.h>\n#include <errno.h>\n#include <stdio.h>\n#include <unistd.h>\n\nextern \"C\" SubstrateMemoryRef SubstrateMemoryCreate(\n\t\tSubstrateAllocatorRef allocator, SubstrateProcessRef process,\n\t\tvoid *data, size_t size) {\n\tif (allocator != NULL) {\n\t\tMSLog(MSLogLevelError, \"MS:Error:allocator != NULL\");\n\t\treturn NULL;\n\t}\n\n\tif (size == 0)\n\t\treturn NULL;\n\n\tint page(PAGE_SIZE/*getpagesize()*/);\n\n\tuintptr_t base(reinterpret_cast<uintptr_t>(data) / page * page);\n\tsize_t width(\n\t\t\t((reinterpret_cast<uintptr_t>(data) + size - 1) / page + 1) * page\n\t\t\t\t\t- base);\n\tvoid *address(reinterpret_cast<void *>(base));\n\n\tif (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {\n\t\tMSLog(MSLogLevelError, \"MS:Error:mprotect() = %d\", errno);\n\t\treturn NULL;\n\t}\n\n\treturn new SubstrateMemory(address, width);\n}\n\nextern \"C\" void SubstrateMemoryRelease(SubstrateMemoryRef memory) {\n\tif (mprotect(memory->address_, memory->width_,\n\t\t\tPROT_READ | PROT_WRITE | PROT_EXEC) == -1)\n\t\tMSLog(MSLogLevelError, \"MS:Error:mprotect() = %d\", errno);\n\n\t__clear_cache(reinterpret_cast<char *>(memory->address_),\n\t\t\treinterpret_cast<char *>(memory->address_) + memory->width_);\n\n\tdelete memory;\n}\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/PosixMemory.h",
    "content": "/*\n * PosixMemory.h\n *\n *  Created on: 2016222\n *      Author: peng\n */\n\n#ifndef POSIXMEMORY_H_\n#define POSIXMEMORY_H_\n\n#include \"CydiaSubstrate.h\"\n\n\nextern \"C\" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size);\nextern \"C\" void SubstrateMemoryRelease(SubstrateMemoryRef memory);\nextern \"C\" void __clear_cache(void *beg, void *end);\n\nstruct SubstrateHookMemory {\n\tSubstrateMemoryRef handle_;\n\n\tSubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) :\thandle_(SubstrateMemoryCreate(NULL, NULL, data, size)) {}\n\n\t~SubstrateHookMemory() {\n\t\tif (handle_ != NULL)\n\t\t\tSubstrateMemoryRelease(handle_);\n\t}\n};\n\n#endif /* POSIXMEMORY_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/SubstrateStruct.h",
    "content": "/*\n * SubstrateMacro.h\n *\n *  Created on: 2016222\n *      Author: peng\n */\n\n#ifndef SUBSTRATEMACRO_H_\n#define SUBSTRATEMACRO_H_\n\n#include <stdlib.h>\ntypedef struct __SubstrateProcess *SubstrateProcessRef;\ntypedef void *SubstrateAllocatorRef;\ntypedef struct SubstrateMemory {\n    void *address_;\n    size_t width_;\n\tSubstrateMemory(void *address, size_t width):address_(address), width_(width) {}\n}*SubstrateMemoryRef;\n\n#endif /* SUBSTRATEMACRO_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/Thumb.cpp",
    "content": "#include <string.h>\n#include \"ARM.h\"\n#include \"Thumb.h\"\n\nstatic size_t Thumb::MSGetInstructionWidth(void *start) {\n\tif ((reinterpret_cast<uintptr_t>(start) & 0x1) == 0)\n\t\treturn MSGetInstructionWidthARM(start);\n\telse\n\t\treturn MSGetInstructionWidthThumb(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(start) & ~0x1));\n}\n\nvoid Thumb::SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result){\n    if (symbol == NULL)\n        return;\n\n    uint16_t *area(reinterpret_cast<uint16_t *>(symbol));\n\n    unsigned align((reinterpret_cast<uintptr_t>(area) & 0x2) == 0 ? 0 : 1);\n    uint16_t *thumb(area + align);\n\n    uint32_t *arm(reinterpret_cast<uint32_t *>(thumb + 2));\n    uint16_t *trail(reinterpret_cast<uint16_t *>(arm + 2));\n\n    if (\n        (align == 0 || area[0] == T$nop) &&\n        thumb[0] == T$bx(A$pc) &&\n        thumb[1] == T$nop &&\n        arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)\n    ) {\n        if (result != NULL)\n            *result = reinterpret_cast<void *>(arm[1]);\n\n        SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1);\n\n        arm[1] = reinterpret_cast<uint32_t>(replace);\n\n        return;\n    }\n\n    size_t required((trail - area) * sizeof(uint16_t));\n\n    size_t used(0);\n    while (used < required)\n        used += MSGetInstructionWidthThumb(reinterpret_cast<uint8_t *>(area) + used);\n    used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t);\n\n    size_t blank((used - required) / sizeof(uint16_t));\n\n    uint16_t backup[used / sizeof(uint16_t)];\n    memcpy(backup, area, used);\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHexEx(area, used + sizeof(uint16_t), 2, name);\n    }\n\n    if (result != NULL) {\n\n    size_t length(used);\n    for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset)\n        if (T$pcrel$ldr(backup[offset]))\n            length += 3 * sizeof(uint16_t);\n        else if (T$pcrel$b(backup[offset]))\n            length += 6 * sizeof(uint16_t);\n        else if (T2$pcrel$b(backup + offset)) {\n            length += 5 * sizeof(uint16_t);\n            ++offset;\n        } else if (T$pcrel$bl(backup + offset)) {\n            length += 5 * sizeof(uint16_t);\n            ++offset;\n        } else if (T$pcrel$cbz(backup[offset])) {\n            length += 16 * sizeof(uint16_t);\n        } else if (T$pcrel$ldrw(backup[offset])) {\n            length += 4 * sizeof(uint16_t);\n            ++offset;\n        } else if (T$pcrel$add(backup[offset]))\n            length += 6 * sizeof(uint16_t);\n        else if (T$32bit$i(backup[offset]))\n            ++offset;\n\n    unsigned pad((length & 0x2) == 0 ? 0 : 1);\n    length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t);\n\n    uint16_t *buffer(reinterpret_cast<uint16_t *>(mmap(\n        NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0\n    )));\n\n    if (buffer == MAP_FAILED) {\n        MSLog(MSLogLevelError, \"MS:Error:mmap() = %d\", errno);\n        *result = NULL;\n        return;\n    }\n\n    if (false) fail: {\n        munmap(buffer, length);\n        *result = NULL;\n        return;\n    }\n\n    size_t start(pad), end(length / sizeof(uint16_t));\n    uint32_t *trailer(reinterpret_cast<uint32_t *>(buffer + end));\n    for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) {\n        if (T$pcrel$ldr(backup[offset])) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t immediate : 8;\n                    uint16_t rd : 3;\n                    uint16_t : 5;\n                };\n            } bits = {backup[offset+0]};\n\n            buffer[start+0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+0, end-2) / 4);\n            buffer[start+1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0);\n\n            // XXX: this code \"works\", but is \"wrong\": the mechanism is more complex than this\n            *--trailer = ((reinterpret_cast<uint32_t>(area + offset) + 4) & ~0x2) + bits.immediate * 4;\n\n            start += 2;\n            end -= 2;\n        } else if (T$pcrel$b(backup[offset])) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t imm8 : 8;\n                    uint16_t cond : 4;\n                    uint16_t /*1101*/ : 4;\n                };\n            } bits = {backup[offset+0]};\n\n            intptr_t jump(bits.imm8 << 1);\n            jump |= 1;\n            jump <<= 23;\n            jump >>= 23;\n\n            buffer[start+0] = T$b$_$im(bits.cond, (end-6 - (start+0)) * 2 - 4);\n\n            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;\n            *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n            *--trailer = T$nop << 16 | T$bx(A$pc);\n\n            start += 1;\n            end -= 6;\n        } else if (T2$pcrel$b(backup + offset)) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t imm6 : 6;\n                    uint16_t cond : 4;\n                    uint16_t s : 1;\n                    uint16_t : 5;\n                };\n            } bits = {backup[offset+0]};\n\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t imm11 : 11;\n                    uint16_t j2 : 1;\n                    uint16_t a : 1;\n                    uint16_t j1 : 1;\n                    uint16_t : 2;\n                };\n            } exts = {backup[offset+1]};\n\n            intptr_t jump(1);\n            jump |= exts.imm11 << 1;\n            jump |= bits.imm6 << 12;\n\n            if (exts.a) {\n                jump |= bits.s << 24;\n                jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;\n                jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;\n                jump |= bits.cond << 18;\n                jump <<= 7;\n                jump >>= 7;\n            } else {\n                jump |= bits.s << 20;\n                jump |= exts.j2 << 19;\n                jump |= exts.j1 << 18;\n                jump <<= 11;\n                jump >>= 11;\n            }\n\n            buffer[start+0] = T$b$_$im(exts.a ? A$al : bits.cond, (end-6 - (start+0)) * 2 - 4);\n\n            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;\n            *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n            *--trailer = T$nop << 16 | T$bx(A$pc);\n\n            ++offset;\n            start += 1;\n            end -= 6;\n        } else if (T$pcrel$bl(backup + offset)) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t immediate : 10;\n                    uint16_t s : 1;\n                    uint16_t : 5;\n                };\n            } bits = {backup[offset+0]};\n\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t immediate : 11;\n                    uint16_t j2 : 1;\n                    uint16_t x : 1;\n                    uint16_t j1 : 1;\n                    uint16_t : 2;\n                };\n            } exts = {backup[offset+1]};\n\n            int32_t jump(0);\n            jump |= bits.s << 24;\n            jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;\n            jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;\n            jump |= bits.immediate << 12;\n            jump |= exts.immediate << 1;\n            jump |= exts.x;\n            jump <<= 7;\n            jump >>= 7;\n\n            buffer[start+0] = T$push_r(1 << A$r7);\n            buffer[start+1] = T$ldr_rd_$pc_im_4$(A$r7, ((end-2 - (start+1)) * 2 - 4 + 2) / 4);\n            buffer[start+2] = T$mov_rd_rm(A$lr, A$r7);\n            buffer[start+3] = T$pop_r(1 << A$r7);\n            buffer[start+4] = T$blx(A$lr);\n\n            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;\n\n            ++offset;\n            start += 5;\n            end -= 2;\n        } else if (T$pcrel$cbz(backup[offset])) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t rn : 3;\n                    uint16_t immediate : 5;\n                    uint16_t : 1;\n                    uint16_t i : 1;\n                    uint16_t : 1;\n                    uint16_t op : 1;\n                    uint16_t : 4;\n                };\n            } bits = {backup[offset+0]};\n\n            intptr_t jump(1);\n            jump |= bits.i << 6;\n            jump |= bits.immediate << 1;\n\n            //jump <<= 24;\n            //jump >>= 24;\n\n            unsigned rn(bits.rn);\n            unsigned rt(rn == A$r7 ? A$r6 : A$r7);\n\n            buffer[start+0] = T$push_r(1 << rt);\n            buffer[start+1] = T1$mrs_rd_apsr(rt);\n            buffer[start+2] = T2$mrs_rd_apsr(rt);\n            buffer[start+3] = T$cbz$_rn_$im(bits.op, rn, (end-10 - (start+3)) * 2 - 4);\n            buffer[start+4] = T1$msr_apsr_nzcvqg_rn(rt);\n            buffer[start+5] = T2$msr_apsr_nzcvqg_rn(rt);\n            buffer[start+6] = T$pop_r(1 << rt);\n\n            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;\n            *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n            *--trailer = T$nop << 16 | T$bx(A$pc);\n            *--trailer = T$nop << 16 | T$pop_r(1 << rt);\n            *--trailer = T$msr_apsr_nzcvqg_rn(rt);\n\n#if 0\n            if ((start & 0x1) == 0)\n                buffer[start++] = T$nop;\n            buffer[start++] = T$bx(A$pc);\n            buffer[start++] = T$nop;\n\n            uint32_t *arm(reinterpret_cast<uint32_t *>(buffer + start));\n            arm[0] = A$add(A$lr, A$pc, 1);\n            arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8);\n#endif\n\n            start += 7;\n            end -= 10;\n        } else if (T$pcrel$ldrw(backup[offset])) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t : 7;\n                    uint16_t u : 1;\n                    uint16_t : 8;\n                };\n            } bits = {backup[offset+0]};\n\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t immediate : 12;\n                    uint16_t rt : 4;\n                };\n            } exts = {backup[offset+1]};\n\n            buffer[start+0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));\n            buffer[start+1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));\n\n            buffer[start+2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);\n            buffer[start+3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);\n\n            // XXX: this code \"works\", but is \"wrong\": the mechanism is more complex than this\n            *--trailer = ((reinterpret_cast<uint32_t>(area + offset) + 4) & ~0x2) + (bits.u == 0 ? -exts.immediate : exts.immediate);\n\n            ++offset;\n            start += 4;\n            end -= 2;\n        } else if (T$pcrel$add(backup[offset])) {\n            union {\n                uint16_t value;\n\n                struct {\n                    uint16_t rd : 3;\n                    uint16_t rm : 3;\n                    uint16_t h2 : 1;\n                    uint16_t h1 : 1;\n                    uint16_t : 8;\n                };\n            } bits = {backup[offset+0]};\n\n            if (bits.h1) {\n                MSLog(MSLogLevelError, \"MS:Error:pcrel(%u):add (rd > r7)\", offset);\n                goto fail;\n            }\n\n            unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7);\n\n            buffer[start+0] = T$push_r(1 << rt);\n            buffer[start+1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd);\n            buffer[start+2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+2, end-2) / 4);\n            buffer[start+3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt);\n            buffer[start+4] = T$pop_r(1 << rt);\n            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4;\n\n            start += 5;\n            end -= 2;\n        } else if (T$32bit$i(backup[offset])) {\n            buffer[start++] = backup[offset];\n            buffer[start++] = backup[++offset];\n        } else {\n            buffer[start++] = backup[offset];\n        }\n    }\n\n    buffer[start++] = T$bx(A$pc);\n    buffer[start++] = T$nop;\n\n    uint32_t *transfer = reinterpret_cast<uint32_t *>(buffer + start);\n    transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n    transfer[1] = reinterpret_cast<uint32_t>(area + used / sizeof(uint16_t)) + 1;\n\n    if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {\n        MSLog(MSLogLevelError, \"MS:Error:mprotect():%d\", errno);\n        return;\n    }\n\n    *result = reinterpret_cast<uint8_t *>(buffer + pad) + 1;\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", *result);\n        MSLogHexEx(buffer, length, 2, name);\n    }\n\n    }\n\n    {\n        SubstrateHookMemory code(process, area, used);\n\n        if (align != 0)\n            area[0] = T$nop;\n\n        thumb[0] = T$bx(A$pc);\n        thumb[1] = T$nop;\n\n        arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);\n        arm[1] = reinterpret_cast<uint32_t>(replace);\n\n        for (unsigned offset(0); offset != blank; ++offset)\n            trail[offset] = T$nop;\n    }\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHexEx(area, used + sizeof(uint16_t), 2, name);\n    }\n}\n\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/Thumb.h",
    "content": "/*\n * Thumb.h\n *\n *  Created on: 2016��2��22��\n *      Author: peng\n */\n\n#ifndef THUMB_H_\n#define THUMB_H_\n\n#include \"Debug.h\"\n#include \"Log.h\"\n#include \"PosixMemory.h\"\n#include <stdlib.h>\n#include <errno.h>\n#include <sys/mman.h>\n\n#define T$Label(l, r) \\\n\t(((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2))\n#define T$pop_$r0$ 0xbc01 // pop {r0}\n#define T$b(im) /* b im */ \\\n\t(0xde00 | (im & 0xff))\n#define T$blx(rm) /* blx rm */ \\\n\t(0x4780 | (rm << 3))\n#define T$bx(rm) /* bx rm */ \\\n\t(0x4700 | (rm << 3))\n#define T$nop /* nop */ \\\n\t(0x46c0)\n#define T$add_rd_rm(rd, rm) /* add rd, rm */ \\\n\t(0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))\n#define T$push_r(r) /* push r... */ \\\n\t(0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff))\n#define T$pop_r(r) /* pop r... */ \\\n\t(0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff))\n#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \\\n\t(0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))\n#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \\\n\t(0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd))\n#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \\\n\t(0x4800 | ((rd) << 8) | ((im) & 0xff))\n#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \\\n\t(0x2000 | ((rn) << 8) | ((im) & 0xff))\n#define T$it$_cd(cd, ms) /* it<ms>, cd */ \\\n\t(0xbf00 | ((cd) << 4) | (ms))\n#define T$cbz$_rn_$im(op,rn,im) /* cb<op>z rn, #im */ \\\n\t(0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn))\n#define T$b$_$im(cond,im) /* b<cond> #im */ \\\n\t(cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff))\n#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \\\n\t(0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn))\n#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \\\n\t(((rt) << 12) | abs(im))\n#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \\\n\t(0xf3ef)\n#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \\\n\t(0x8000 | ((rd) << 8))\n#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \\\n\t(0xf380 | (rn))\n#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \\\n\t(0x8c00)\n#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \\\n\t(T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn))\n#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \\\n    (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im))\n\nstatic inline bool T$32bit$i(uint16_t ic) {\n\treturn ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000);\n}\n\nstatic inline bool T$pcrel$cbz(uint16_t ic) {\n\treturn (ic & 0xf500) == 0xb100;\n}\n\nstatic inline bool T$pcrel$b(uint16_t ic) {\n\treturn (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00;\n}\n\nstatic inline bool T2$pcrel$b(uint16_t *ic) {\n\treturn (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0x9000 || ((ic[1] & 0xd000) == 0x8000 && (ic[0] & 0x0380) != 0x0380));\n}\n\nstatic inline bool T$pcrel$bl(uint16_t *ic) {\n\treturn (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000);\n}\n\nstatic inline bool T$pcrel$ldr(uint16_t ic) {\n\treturn (ic & 0xf800) == 0x4800;\n}\n\nstatic inline bool T$pcrel$add(uint16_t ic) {\n\treturn (ic & 0xff78) == 0x4478;\n}\n\nstatic inline bool T$pcrel$ldrw(uint16_t ic) {\n\treturn (ic & 0xff7f) == 0xf85f;\n}\n\nstatic size_t MSGetInstructionWidthThumb(void *start) {\n\tuint16_t *thumb(reinterpret_cast<uint16_t *>(start));\n\treturn T$32bit$i(thumb[0]) ? 4 : 2;\n}\n\nstatic size_t MSGetInstructionWidthARM(void *start) {\n\treturn 4;\n}\n\nnamespace Thumb{\n\tstatic size_t MSGetInstructionWidth(void *start);\n\textern \"C\" void SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result);\n}\n#endif /* THUMB_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/hde64.h",
    "content": "/*\n * Hacker Disassembler Engine 64\n * Copyright (c) 2008-2009, Vyacheslav Patkov.\n * All rights reserved.\n *\n * hde64.h: C/C++ header file\n *\n */\n\n#ifndef _HDE64_H_\n#define _HDE64_H_\n\n/* stdint.h - C99 standard header\n * http://en.wikipedia.org/wiki/stdint.h\n *\n * if your compiler doesn't contain \"stdint.h\" header (for\n * example, Microsoft Visual C++), you can download file:\n *   http://www.azillionmonkeys.com/qed/pstdint.h\n * and change next line to:\n *   #include \"pstdint.h\"\n */\n#include <stdint.h>\n\n#define F_MODRM         0x00000001\n#define F_SIB           0x00000002\n#define F_IMM8          0x00000004\n#define F_IMM16         0x00000008\n#define F_IMM32         0x00000010\n#define F_IMM64         0x00000020\n#define F_DISP8         0x00000040\n#define F_DISP16        0x00000080\n#define F_DISP32        0x00000100\n#define F_RELATIVE      0x00000200\n#define F_ERROR         0x00001000\n#define F_ERROR_OPCODE  0x00002000\n#define F_ERROR_LENGTH  0x00004000\n#define F_ERROR_LOCK    0x00008000\n#define F_ERROR_OPERAND 0x00010000\n#define F_PREFIX_REPNZ  0x01000000\n#define F_PREFIX_REPX   0x02000000\n#define F_PREFIX_REP    0x03000000\n#define F_PREFIX_66     0x04000000\n#define F_PREFIX_67     0x08000000\n#define F_PREFIX_LOCK   0x10000000\n#define F_PREFIX_SEG    0x20000000\n#define F_PREFIX_REX    0x40000000\n#define F_PREFIX_ANY    0x7f000000\n\n#define PREFIX_SEGMENT_CS   0x2e\n#define PREFIX_SEGMENT_SS   0x36\n#define PREFIX_SEGMENT_DS   0x3e\n#define PREFIX_SEGMENT_ES   0x26\n#define PREFIX_SEGMENT_FS   0x64\n#define PREFIX_SEGMENT_GS   0x65\n#define PREFIX_LOCK         0xf0\n#define PREFIX_REPNZ        0xf2\n#define PREFIX_REPX         0xf3\n#define PREFIX_OPERAND_SIZE 0x66\n#define PREFIX_ADDRESS_SIZE 0x67\n\n#pragma pack(push,1)\n\ntypedef struct {\n    uint8_t len;\n    uint8_t p_rep;\n    uint8_t p_lock;\n    uint8_t p_seg;\n    uint8_t p_66;\n    uint8_t p_67;\n    uint8_t rex;\n    uint8_t rex_w;\n    uint8_t rex_r;\n    uint8_t rex_x;\n    uint8_t rex_b; \n    uint8_t opcode;\n    uint8_t opcode2;\n    uint8_t modrm;\n    uint8_t modrm_mod;\n    uint8_t modrm_reg;\n    uint8_t modrm_rm;\n    uint8_t sib;\n    uint8_t sib_scale;\n    uint8_t sib_index;\n    uint8_t sib_base;\n    union {\n        uint8_t imm8;\n        uint16_t imm16;\n        uint32_t imm32;\n        uint64_t imm64;\n    } imm;\n    union {\n        uint8_t disp8;\n        uint16_t disp16;\n        uint32_t disp32;\n    } disp;\n    uint32_t flags;\n} hde64s;\n\n#pragma pack(pop)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* __cdecl */\nunsigned int hde64_disasm(const void *code, hde64s *hs);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _HDE64_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/util.cpp",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#include <elf.h>\n#include <sys/mman.h>\n#include <Helper.h>\n#include <unistd.h>\n#include \"Log.h\"\n\n/* memory map for libraries */\n#define MAX_NAME_LEN 256\n#define MEMORY_ONLY  \"[memory]\"\nstruct mm {\n\tchar name[MAX_NAME_LEN];\n\tunsigned long start, end;\n};\n\ntypedef struct symtab *symtab_t;\nstruct symlist {\n\tElf32_Sym *sym; /* symbols */\n\tchar *str; /* symbol strings */\n\tunsigned num; /* number of symbols */\n};\nstruct symtab {\n\tstruct symlist *st; /* \"static\" symbols */\n\tstruct symlist *dyn; /* dynamic symbols */\n};\n\nstatic void* xmalloc(size_t size) {\n\tvoid *p;\n\tp = malloc(size);\n\tif (!p) {\n\t\tprintf(\"Out of memory\\n\");\n\t\texit(1);\n\t}\n\treturn p;\n}\n\nstatic int my_pread(int fd, void *buf, size_t count, off_t offset) {\n\tlseek(fd, offset, SEEK_SET);\n\treturn read(fd, buf, count);\n}\n\nstatic struct symlist* get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) {\n\tstruct symlist *sl, *ret;\n\tint rv;\n\n\tret = NULL;\n\tsl = (struct symlist *) xmalloc(sizeof(struct symlist));\n\tsl->str = NULL;\n\tsl->sym = NULL;\n\n\t/* sanity */\n\tif (symh->sh_size % sizeof(Elf32_Sym)) {\n\t\t//printf(\"elf_error\\n\");\n\t\tgoto out;\n\t}\n\n\t/* symbol table */\n\tsl->num = symh->sh_size / sizeof(Elf32_Sym);\n\tsl->sym = (Elf32_Sym *) xmalloc(symh->sh_size);\n\trv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset);\n\tif (0 > rv) {\n\t\t//perror(\"read\");\n\t\tgoto out;\n\t}\n\tif (rv != symh->sh_size) {\n\t\t//printf(\"elf error\\n\");\n\t\tgoto out;\n\t}\n\n\t/* string table */\n\tsl->str = (char *) xmalloc(strh->sh_size);\n\trv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset);\n\tif (0 > rv) {\n\t\t//perror(\"read\");\n\t\tgoto out;\n\t}\n\tif (rv != strh->sh_size) {\n\t\t//printf(\"elf error\");\n\t\tgoto out;\n\t}\n\n\tret = sl;\n\tout: return ret;\n}\n\nstatic int do_load(int fd, symtab_t symtab) {\n\tint rv;\n\tsize_t size;\n\tElf32_Ehdr ehdr;\n\tElf32_Shdr *shdr = NULL, *p;\n\tElf32_Shdr *dynsymh, *dynstrh;\n\tElf32_Shdr *symh, *strh;\n\tchar *shstrtab = NULL;\n\tint i;\n\tint ret = -1;\n\n\t/* elf header */\n\trv = read(fd, &ehdr, sizeof(ehdr));\n\tif (0 > rv) {\n\t\tLOGD(\"read\\n\");\n\t\tgoto out;\n\t}\n\tif (rv != sizeof(ehdr)) {\n\t\tLOGD(\"elf error 1\\n\");\n\t\tgoto out;\n\t}\n\tif (strncmp((const char *) ELFMAG, (const char *) ehdr.e_ident, SELFMAG)) { /* sanity */\n\t\tLOGD(\"not an elf\\n\");\n\t\tgoto out;\n\t}\n\tif (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */\n\t\tLOGD(\"elf error 2\\n\");\n\t\tgoto out;\n\t}\n\n\t/* section header table */\n\tsize = ehdr.e_shentsize * ehdr.e_shnum;\n\tshdr = (Elf32_Shdr *) xmalloc(size);\n\trv = my_pread(fd, shdr, size, ehdr.e_shoff);\n\tif (0 > rv) {\n\t\tLOGD(\"read\\n\");\n\t\tgoto out;\n\t}\n\tif (rv != size) {\n\t\tLOGD(\"elf error 3 %d %d\\n\", rv, size);\n\t\tgoto out;\n\t}\n\n\t/* section header string table */\n\tsize = shdr[ehdr.e_shstrndx].sh_size;\n\tshstrtab = (char *) xmalloc(size);\n\trv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset);\n\tif (0 > rv) {\n\t\tLOGD(\"read\\n\");\n\t\tgoto out;\n\t}\n\tif (rv != size) {\n\t\tLOGD(\"elf error 4 %d %d\\n\", rv, size);\n\t\tgoto out;\n\t}\n\n\t/* symbol table headers */\n\tsymh = dynsymh = NULL;\n\tstrh = dynstrh = NULL;\n\tfor (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++)\n\t\tif (SHT_SYMTAB == p->sh_type) {\n\t\t\tif (symh) {\n\t\t\t\tLOGD(\"too many symbol tables\\n\");\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tsymh = p;\n\t\t} else if (SHT_DYNSYM == p->sh_type) {\n\t\t\tif (dynsymh) {\n\t\t\t\tLOGD(\"too many symbol tables\\n\");\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tdynsymh = p;\n\t\t} else if (SHT_STRTAB == p->sh_type\n\t\t\t\t&& !strncmp(shstrtab + p->sh_name, \".strtab\", 7)) {\n\t\t\tif (strh) {\n\t\t\t\tLOGD(\"too many string tables\\n\");\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tstrh = p;\n\t\t} else if (SHT_STRTAB == p->sh_type\n\t\t\t\t&& !strncmp(shstrtab + p->sh_name, \".dynstr\", 7)) {\n\t\t\tif (dynstrh) {\n\t\t\t\tLOGD(\"too many string tables\\n\");\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tdynstrh = p;\n\t\t}\n\t/* sanity checks */\n\tif ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) {\n\t\tLOGD(\"bad dynamic symbol table\\n\");\n\t\tgoto out;\n\t}\n\tif ((!symh && strh) || (symh && !strh)) {\n\t\tLOGD(\"bad symbol table\\n\");\n\t\tgoto out;\n\t}\n\tif (!dynsymh && !symh) {\n\t\tLOGD(\"no symbol table\\n\");\n\t\tgoto out;\n\t}\n\n\t/* symbol tables */\n\tif (dynsymh)\n\t\tsymtab->dyn = get_syms(fd, dynsymh, dynstrh);\n\tif (symh)\n\t\tsymtab->st = get_syms(fd, symh, strh);\n\tret = 0;\n\tout: free(shstrtab);\n\tfree(shdr);\n\treturn ret;\n}\n\nstatic symtab_t load_symtab(char *filename) {\n\tint fd;\n\tsymtab_t symtab;\n\n\tsymtab = (symtab_t) xmalloc(sizeof(*symtab));\n\tmemset(symtab, 0, sizeof(*symtab));\n\n\tfd = open(filename, O_RDONLY);\n\tif (0 > fd) {\n\t\tLOGE(\"%s open\\n\", __func__);\n\t\treturn NULL;\n\t}\n\tif (0 > do_load(fd, symtab)) {\n\t\tLOGE(\"Error ELF parsing %s\\n\", filename);\n\t\tfree(symtab);\n\t\tsymtab = NULL;\n\t}\n\tclose(fd);\n\treturn symtab;\n}\n\nstatic int load_memmap(pid_t pid, struct mm *mm, int *nmmp) {\n\tsize_t buf_size = 0x40000;\n\tchar *p_buf = (char *) malloc(buf_size); // increase this if needed for larger \"maps\"\n\tchar name[MAX_NAME_LEN] = { 0 };\n\tchar *p;\n\tunsigned long start, end;\n\tstruct mm *m;\n\tint nmm = 0;\n\tint fd, rv;\n\tint i;\n\n\tsprintf(p_buf, \"/proc/%d/maps\", pid);\n\tfd = open(p_buf, O_RDONLY);\n\tif (0 > fd) {\n\t\tLOGE(\"Can't open %s for reading\\n\", p_buf);\n\t\tfree(p_buf);\n\t\treturn -1;\n\t}\n\n\t/* Zero to ensure data is null terminated */\n\tmemset(p_buf, 0, buf_size);\n\n\tp = p_buf;\n\twhile (1) {\n\t\trv = read(fd, p, buf_size - (p - p_buf));\n\t\tif (0 > rv) {\n\t\t\tLOGE(\"%s read\", __FUNCTION__);\n\t\t\tfree(p_buf);\n\t\t\treturn -1;\n\t\t}\n\t\tif (0 == rv)\n\t\t\tbreak;\n\t\tp += rv;\n\t\tif (p - p_buf >= buf_size) {\n\t\t\tLOGE(\"Too many memory mapping\\n\");\n\t\t\tfree(p_buf);\n\t\t\treturn -1;\n\t\t}\n\t}\n\tclose(fd);\n\n\tp = strtok(p_buf, \"\\n\");\n\tm = mm;\n\twhile (p) {\n\t\t/* parse current map line */\n\t\trv = sscanf(p, \"%08lx-%08lx %*s %*s %*s %*s %s\\n\", &start, &end, name);\n\n\t\tp = strtok(NULL, \"\\n\");\n\n\t\tif (rv == 2) {\n\t\t\tm = &mm[nmm++];\n\t\t\tm->start = start;\n\t\t\tm->end = end;\n\t\t\tmemcpy(m->name, MEMORY_ONLY, sizeof(MEMORY_ONLY));\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* search backward for other mapping with same name */\n\t\tfor (i = nmm - 1; i >= 0; i--) {\n\t\t\tm = &mm[i];\n\t\t\tif (!strcmp(m->name, name))\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (i >= 0) {\n\t\t\tif (start < m->start)\n\t\t\t\tm->start = start;\n\t\t\tif (end > m->end)\n\t\t\t\tm->end = end;\n\t\t} else {\n\t\t\t/* new entry */\n\t\t\tm = &mm[nmm++];\n\t\t\tm->start = start;\n\t\t\tm->end = end;\n\t\t\tmemcpy(m->name, name, strlen(name));\n\t\t}\n\t}\n\n\t*nmmp = nmm;\n\tfree(p_buf);\n\treturn 0;\n}\n\n/* Find libc in MM, storing no more than LEN-1 chars of\n its name in NAME and set START to its starting\n address.  If libc cannot be found return -1 and\n leave NAME and START untouched.  Otherwise return 0\n and null-terminated NAME. */\nstatic int find_libname(const char *libn, char *name, int len, unsigned long *start,\n\t\tstruct mm *mm, int nmm) {\n\tint i;\n\tstruct mm *m;\n\tchar *p;\n\tfor (i = 0, m = mm; i < nmm; i++, m++) {\n\t\tif (!strcmp(m->name, MEMORY_ONLY))\n\t\t\tcontinue;\n\t\tp = strrchr(m->name, '/');\n\t\tif (!p)\n\t\t\tcontinue;\n\t\tp++;\n\t\tif (strncmp(libn, p, strlen(libn)))\n\t\t\tcontinue;\n\t\tp += strlen(libn);\n\n\t\t/* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */\n\t\tif (!strncmp(\"so\", p, 2) || 1) // || (p[0] == '-' && isdigit(p[1])))\n\t\t\tbreak;\n\t}\n\tif (i >= nmm)\n\t\t/* not found */\n\t\treturn -1;\n\n\t*start = m->start;\n\tstrncpy(name, m->name, len);\n\tif (strlen(m->name) >= len)\n\t\tname[len - 1] = '\\0';\n\n\tmprotect((void*) m->start, m->end - m->start,\n\t\t\tPROT_READ | PROT_WRITE | PROT_EXEC);\n\treturn 0;\n}\n\nstatic int lookup2(struct symlist *sl, unsigned char type, char *name,\n\t\tunsigned long *val) {\n\tElf32_Sym *p;\n\tint len;\n\tint i;\n\n\tlen = strlen(name);\n\tfor (i = 0, p = sl->sym; i < sl->num; i++, p++) {\n\t\t//LOGD(\"name: %s %x\\n\", sl->str+p->st_name, p->st_value)\n\t\tif (!strncmp(sl->str + p->st_name, name, len)\n\t\t\t\t&& *(sl->str + p->st_name + len) == 0\n\t\t\t\t&& ELF32_ST_TYPE(p->st_info) == type) {\n\t\t\t//if (p->st_value != 0) {\n\t\t\t*val = p->st_value;\n\t\t\treturn 0;\n\t\t\t//}\n\t\t}\n\t}\n\treturn -1;\n}\n\nstatic int lookup_sym(symtab_t s, unsigned char type, char *name,\n\t\tunsigned long *val) {\n\tif (s->dyn && !lookup2(s->dyn, type, name, val))\n\t\treturn 0;\n\tif (s->st && !lookup2(s->st, type, name, val))\n\t\treturn 0;\n\treturn -1;\n}\n\nstatic int lookup_func_sym(symtab_t s, char *name, unsigned long *val) {\n\treturn lookup_sym(s, STT_FUNC, name, val);\n}\n\nint find_name(pid_t pid, const char *name, const char *libn,\n\t\tunsigned long *addr) {\n\tstruct mm mm[1000] = { 0 };\n\tunsigned long libcaddr;\n\tint nmm;\n\tchar libc[1024] = { 0 };\n\tsymtab_t s;\n\n\tif (0 > load_memmap(pid, mm, &nmm)) {\n\t\tLOGD(\"cannot read memory map\\n\");\n\t\treturn -1;\n\t}\n\tif (0\n\t\t\t> find_libname((char *) libn, (char *) libc, sizeof(libc),\n\t\t\t\t\t&libcaddr, mm, nmm)) {\n\t\tLOGD(\"cannot find lib: %s\\n\", libn);\n\t\treturn -1;\n\t}\n\t//LOGD(\"lib: >%s<\\n\", libc)\n\ts = load_symtab(libc);\n\tif (!s) {\n\t\tLOGD(\"cannot read symbol table\\n\");\n\t\treturn -1;\n\t}\n\tif (0 > lookup_func_sym(s, (char *) name, addr)) {\n\t\tLOGD(\"cannot find function: %s\\n\", name);\n\t\treturn -1;\n\t}\n\t*addr += libcaddr;\n\treturn 0;\n}\n\nint find_libbase(pid_t pid, const char *libn, unsigned long *addr) {\n\tstruct mm mm[1000] = { 0 };\n\tunsigned long libcaddr;\n\tint nmm;\n\tchar libc[1024] = { 0 };\n\tsymtab_t s;\n\n\tif (0 > load_memmap(pid, mm, &nmm)) {\n\t\tLOGD(\"cannot read memory map\\n\");\n\t\treturn -1;\n\t}\n\tif (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) {\n\t\tLOGD(\"cannot find lib\\n\");\n\t\treturn -1;\n\t}\n\t*addr = libcaddr;\n\treturn 0;\n}\n\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/util.h",
    "content": "#ifndef HOOK_UTIL_H_\n#define HOOK_UTIL_H_\n\n#include <unistd.h>\n\nextern int find_name(pid_t pid, const char *name,const  char *libn, unsigned long *addr);\nextern int find_libbase(pid_t pid, const char *libn, unsigned long *addr);\n#endif\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/x86.cpp",
    "content": "#include \"x86.h\"\n#include \"x86_64.h\"\n\nstatic size_t MSGetInstructionWidthIntel(void *start) {\n    hde64s decode;\n    return hde64_disasm(start, &decode);\n}\n\nvoid x86::SubstrateHookFunctionx86(SubstrateProcessRef process, void *symbol, void *replace, void **result){\n    if (MSDebug)\n        MSLog(MSLogLevelNotice, \"SubstrateHookFunctionx86(process:%p, symbol:%p, replace:%p, result:%p)\", process, symbol, replace, result);\n    if (symbol == NULL)\n        return;\n\n    uintptr_t source(reinterpret_cast<uintptr_t>(symbol));\n    uintptr_t target(reinterpret_cast<uintptr_t>(replace));\n\n    uint8_t *area(reinterpret_cast<uint8_t *>(symbol));\n\n    size_t required(MSSizeOfJump(target, source));\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHex(area, 32, name);\n    }\n\n    size_t used(0);\n    while (used < required) {\n        size_t width(MSGetInstructionWidthIntel(area + used));\n        if (width == 0) {\n            MSLog(MSLogLevelError, \"MS:Error:MSGetInstructionWidthIntel(%p) == 0\", area + used);\n            return;\n        }\n\n        used += width;\n    }\n\n    size_t blank(used - required);\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHex(area, used + sizeof(uint16_t), name);\n    }\n\n    uint8_t backup[used];\n    memcpy(backup, area, used);\n\n    if (result != NULL) {\n\n    if (backup[0] == 0xe9) {\n        *result = reinterpret_cast<void *>(source + 5 + *reinterpret_cast<uint32_t *>(backup + 1));\n        return;\n    }\n\n    if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) {\n        *result = *reinterpret_cast<void **>(source + 6 + *reinterpret_cast<uint32_t *>(backup + 2));\n        return;\n    }\n\n    size_t length(used + MSSizeOfJump(source + used));\n\n    for (size_t offset(0), width; offset != used; offset += width) {\n        hde64s decode;\n        hde64_disasm(backup + offset, &decode);\n        width = decode.len;\n        //_assert(width != 0 && offset + width <= used);\n\n#ifdef __LP64__\n        if ((decode.modrm & 0xc7) == 0x05) {\n            if (decode.opcode == 0x8b) {\n                void *destiny(area + offset + width + int32_t(decode.disp.disp32));\n                uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);\n                length -= decode.len;\n                length += MSSizeOfPushPointer(destiny);\n                length += MSSizeOfPop(reg);\n                length += MSSizeOfMove64();\n            } else {\n                MSLog(MSLogLevelError, \"MS:Error: Unknown RIP-Relative (%.2x %.2x)\", decode.opcode, decode.opcode2);\n                continue;\n            }\n        } else\n#endif\n\n        if (backup[offset] == 0xe8) {\n            int32_t relative(*reinterpret_cast<int32_t *>(backup + offset + 1));\n            void *destiny(area + offset + decode.len + relative);\n\n            if (relative == 0) {\n                length -= decode.len;\n                length += MSSizeOfPushPointer(destiny);\n            } else {\n                length += MSSizeOfSkip();\n                length += MSSizeOfJump(destiny);\n            }\n        } else if (backup[offset] == 0xeb) {\n            length -= decode.len;\n            length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));\n        } else if (backup[offset] == 0xe9) {\n            length -= decode.len;\n            length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int32_t *>(backup + offset + 1));\n        } else if (\n            backup[offset] == 0xe3 ||\n            (backup[offset] & 0xf0) == 0x70\n            // XXX: opcode2 & 0xf0 is 0x80?\n        ) {\n            length += decode.len;\n            length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));\n        }\n    }\n\n    uint8_t *buffer(reinterpret_cast<uint8_t *>(mmap(\n        NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0\n    )));\n\n    if (buffer == MAP_FAILED) {\n        MSLog(MSLogLevelError, \"MS:Error:mmap() = %d\", errno);\n        *result = NULL;\n        return;\n    }\n\n    if (false) fail: {\n        munmap(buffer, length);\n        *result = NULL;\n        return;\n    }\n\n    {\n        uint8_t *current(buffer);\n\n        for (size_t offset(0), width; offset != used; offset += width) {\n            hde64s decode;\n            hde64_disasm(backup + offset, &decode);\n            width = decode.len;\n            //_assert(width != 0 && offset + width <= used);\n\n#ifdef __LP64__\n            if ((decode.modrm & 0xc7) == 0x05) {\n                if (decode.opcode == 0x8b) {\n                    void *destiny(area + offset + width + int32_t(decode.disp.disp32));\n                    uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);\n                    MSPushPointer(current, destiny);\n                    MSWritePop(current, reg);\n                    MSWriteMove64(current, reg, reg);\n                } else {\n                    MSLog(MSLogLevelError, \"MS:Error: Unknown RIP-Relative (%.2x %.2x)\", decode.opcode, decode.opcode2);\n                    goto copy;\n                }\n            } else\n#endif\n\n            if (backup[offset] == 0xe8) {\n                int32_t relative(*reinterpret_cast<int32_t *>(backup + offset + 1));\n                if (relative == 0)\n                    MSPushPointer(current, area + offset + decode.len);\n                else {\n                    MSWrite<uint8_t>(current, 0xe8);\n                    MSWrite<int32_t>(current, MSSizeOfSkip());\n                    void *destiny(area + offset + decode.len + relative);\n                    MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip()));\n                    MSWriteJump(current, destiny);\n                }\n            } else if (backup[offset] == 0xeb)\n                MSWriteJump(current, area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));\n            else if (backup[offset] == 0xe9)\n                MSWriteJump(current, area + offset + decode.len + *reinterpret_cast<int32_t *>(backup + offset + 1));\n            else if (\n                backup[offset] == 0xe3 ||\n                (backup[offset] & 0xf0) == 0x70\n            ) {\n                MSWrite<uint8_t>(current, backup[offset]);\n                MSWrite<uint8_t>(current, 2);\n                MSWrite<uint8_t>(current, 0xeb);\n                void *destiny(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));\n                MSWrite<uint8_t>(current, MSSizeOfJump(destiny, current + 1));\n                MSWriteJump(current, destiny);\n            } else\n#ifdef __LP64__\n                copy:\n#endif\n            {\n                MSWrite(current, backup + offset, width);\n            }\n        }\n\n        MSWriteJump(current, area + used);\n    }\n\n    if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {\n        MSLog(MSLogLevelError, \"MS:Error:mprotect():%d\", errno);\n        goto fail;\n    }\n\n    *result = buffer;\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", *result);\n        MSLogHex(buffer, length, name);\n    }\n\n    }\n\n    {\n        SubstrateHookMemory code(process, area, used);\n\n        uint8_t *current(area);\n        MSWriteJump(current, target);\n        for (unsigned offset(0); offset != blank; ++offset)\n            MSWrite<uint8_t>(current, 0x90);\n    }\n\n    if (MSDebug) {\n        char name[16];\n        sprintf(name, \"%p\", area);\n        MSLogHex(area, used + sizeof(uint16_t), name);\n    }\n}\n\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/x86.h",
    "content": "/*\n * x86.h\n *\n *  Created on: 2016��2��22��\n *      Author: peng\n */\n\n#ifndef X86_H_\n#define X86_H_\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <sys/mman.h>\n#include \"CydiaSubstrate.h\"\n#include \"PosixMemory.h\"\n#include \"Log.h\"\n#include \"Debug.h\"\n\ntemplate <typename Type_>\n_disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) {\n    *reinterpret_cast<Type_ *>(buffer) = value;\n    buffer += sizeof(Type_);\n}\n\n_disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) {\n    memcpy(buffer, data, size);\n    buffer += size;\n}\n\n#ifdef __LP64__\nstatic const bool ia32 = false;\n#else\nstatic const bool ia32 = true;\n#endif\n\nenum I$r {\n    I$rax, I$rcx, I$rdx, I$rbx,\n    I$rsp, I$rbp, I$rsi, I$rdi,\n    I$r8, I$r9, I$r10, I$r11,\n    I$r12, I$r13, I$r14, I$r15,\n};\n\n_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) {\n    intptr_t offset(target - source);\n    return int32_t(offset) == offset;\n}\n\n_disused static size_t MSSizeOfSkip() {\n    return 5;\n}\n\n_disused static size_t MSSizeOfPushPointer(uintptr_t target) {\n    return uint64_t(target) >> 32 == 0 ? 5 : 13;\n}\n\n_disused static size_t MSSizeOfPushPointer(void *target) {\n    return MSSizeOfPushPointer(reinterpret_cast<uintptr_t>(target));\n}\n\n_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) {\n    if (ia32 || (!blind && MSIs32BitOffset(target, source + 5)))\n        return MSSizeOfSkip();\n    else\n        return MSSizeOfPushPointer(target) + 1;\n}\n\n_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) {\n    return MSSizeOfJump(false, target, source);\n}\n\n_disused static size_t MSSizeOfJump(uintptr_t target) {\n    return MSSizeOfJump(true, target);\n}\n\n_disused static size_t MSSizeOfJump(void *target, void *source) {\n    return MSSizeOfJump(reinterpret_cast<uintptr_t>(target), reinterpret_cast<uintptr_t>(source));\n}\n\n_disused static size_t MSSizeOfJump(void *target) {\n    return MSSizeOfJump(reinterpret_cast<uintptr_t>(target));\n}\n\n_disused static void MSWriteSkip(uint8_t *&current, ssize_t size) {\n    MSWrite<uint8_t>(current, 0xe9);\n    MSWrite<uint32_t>(current, size);\n}\n\n_disused static void MSPushPointer(uint8_t *&current, uintptr_t target) {\n    MSWrite<uint8_t>(current, 0x68);\n    MSWrite<uint32_t>(current, target);\n\n    if (uint32_t high = uint64_t(target) >> 32) {\n        MSWrite<uint8_t>(current, 0xc7);\n        MSWrite<uint8_t>(current, 0x44);\n        MSWrite<uint8_t>(current, 0x24);\n        MSWrite<uint8_t>(current, 0x04);\n        MSWrite<uint32_t>(current, high);\n    }\n}\n\n_disused static void MSPushPointer(uint8_t *&current, void *target) {\n    return MSPushPointer(current, reinterpret_cast<uintptr_t>(target));\n}\n\n_disused static void MSWriteCall(uint8_t *&current, I$r target) {\n    if (target >> 3 != 0)\n        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);\n    MSWrite<uint8_t>(current, 0xff);\n    MSWrite<uint8_t>(current, 0xd0 | (target & 0x07));\n}\n\n_disused static void MSWriteCall(uint8_t *&current, uintptr_t target) {\n    uintptr_t source(reinterpret_cast<uintptr_t>(current));\n\n    if (ia32 || MSIs32BitOffset(target, source + 5)) {\n        MSWrite<uint8_t>(current, 0xe8);\n        MSWrite<uint32_t>(current, target - (source + 5));\n    } else {\n        MSPushPointer(current, target);\n\n        MSWrite<uint8_t>(current, 0x83);\n        MSWrite<uint8_t>(current, 0xc4);\n        MSWrite<uint8_t>(current, 0x08);\n\n        MSWrite<uint8_t>(current, 0x67);\n        MSWrite<uint8_t>(current, 0xff);\n        MSWrite<uint8_t>(current, 0x54);\n        MSWrite<uint8_t>(current, 0x24);\n        MSWrite<uint8_t>(current, 0xf8);\n    }\n}\n\ntemplate <typename Type_>\n_disused static void MSWriteCall(uint8_t *&current, Type_ *target) {\n    return MSWriteCall(current, reinterpret_cast<uintptr_t>(target));\n}\n\n_disused static void MSWriteJump(uint8_t *&current, uintptr_t target) {\n    uintptr_t source(reinterpret_cast<uintptr_t>(current));\n\n    if (ia32 || MSIs32BitOffset(target, source + 5))\n        MSWriteSkip(current, target - (source + 5));\n    else {\n        MSPushPointer(current, target);\n        MSWrite<uint8_t>(current, 0xc3);\n    }\n}\n\n_disused static void MSWriteJump(uint8_t *&current, void *target) {\n    return MSWriteJump(current, reinterpret_cast<uintptr_t>(target));\n}\n\n_disused static void MSWriteJump(uint8_t *&current, I$r target) {\n    if (target >> 3 != 0)\n        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);\n    MSWrite<uint8_t>(current, 0xff);\n    MSWrite<uint8_t>(current, 0xe0 | (target & 0x07));\n}\n\n_disused static void MSWritePop(uint8_t *&current, uint8_t target) {\n    if (target >> 3 != 0)\n        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);\n    MSWrite<uint8_t>(current, 0x58 | (target & 0x07));\n}\n\n_disused static size_t MSSizeOfPop(uint8_t target) {\n    return target >> 3 != 0 ? 2 : 1;\n}\n\n_disused static void MSWritePush(uint8_t *&current, I$r target) {\n    if (target >> 3 != 0)\n        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);\n    MSWrite<uint8_t>(current, 0x50 | (target & 0x07));\n}\n\n_disused static void MSWriteAdd(uint8_t *&current, I$r target, uint8_t source) {\n    MSWrite<uint8_t>(current, 0x83);\n    MSWrite<uint8_t>(current, 0xc4 | (target & 0x07));\n    MSWrite<uint8_t>(current, source);\n}\n\n_disused static void MSWriteSet64(uint8_t *&current, I$r target, uintptr_t source) {\n    MSWrite<uint8_t>(current, 0x48 | (target & 0x08) >> 3 << 2);\n    MSWrite<uint8_t>(current, 0xb8 | (target & 0x7));\n    MSWrite<uint64_t>(current, source);\n}\n\ntemplate <typename Type_>\n_disused static void MSWriteSet64(uint8_t *&current, I$r target, Type_ *source) {\n    return MSWriteSet64(current, target, reinterpret_cast<uintptr_t>(source));\n}\n\n_disused static void MSWriteMove64(uint8_t *&current, uint8_t source, uint8_t target) {\n    MSWrite<uint8_t>(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3);\n    MSWrite<uint8_t>(current, 0x8b);\n    MSWrite<uint8_t>(current, (target & 0x07) << 3 | (source & 0x07));\n}\n\n_disused static size_t MSSizeOfMove64() {\n    return 3;\n}\n\nnamespace x86{\n\textern \"C\" void SubstrateHookFunctionx86(SubstrateProcessRef process, void *symbol, void *replace, void **result);\n}\n\n#endif /* X86_H_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/x86_64.cpp",
    "content": "#ifndef X86_64_CPP_\n#define X86_64_CPP_\n\n#include <stdint.h>\n#include <string.h>\n#include \"x86_64.h\"\n\nunsigned char hde64_table[] = {\n  0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5,\n  0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1,\n  0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea,\n  0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0,\n  0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab,\n  0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92,\n  0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90,\n  0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b,\n  0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,\n  0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc,\n  0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20,\n  0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff,\n  0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00,\n  0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01,\n  0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10,\n  0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00,\n  0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00,\n  0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00,\n  0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00,\n  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,\n  0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,\n  0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40,\n  0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43,\n  0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,\n  0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40,\n  0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06,\n  0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07,\n  0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,\n  0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10,\n  0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00,\n  0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb,\n  0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff,\n  0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09,\n  0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff,\n  0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08,\n  0x00,0xf0,0x02,0x00\n};\n\n/*\n * Hacker Disassembler Engine 64 C\n * Copyright (c) 2008-2009, Vyacheslav Patkov.\n * All rights reserved.\n *\n */\nunsigned int hde64_disasm(const void *code, hde64s *hs)\n{\n    uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;\n    uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;\n    uint8_t op64 = 0;\n\n    memset(hs,0,sizeof(hde64s));\n\n    for (x = 16; x; x--)\n        switch (c = *p++) {\n            case 0xf3:\n                hs->p_rep = c;\n                pref |= PRE_F3;\n                break;\n            case 0xf2:\n                hs->p_rep = c;\n                pref |= PRE_F2;\n                break;\n            case 0xf0:\n                hs->p_lock = c;\n                pref |= PRE_LOCK;\n                break;\n            case 0x26: case 0x2e: case 0x36:\n            case 0x3e: case 0x64: case 0x65:\n                hs->p_seg = c;\n                pref |= PRE_SEG;\n                break;\n            case 0x66:\n                hs->p_66 = c;\n                pref |= PRE_66;\n                break;\n            case 0x67:\n                hs->p_67 = c;\n                pref |= PRE_67;\n                break;\n            default:\n                goto pref_done;\n        }\n  pref_done:\n\n    hs->flags = (uint32_t)pref << 23;\n\n    if (!pref)\n        pref |= PRE_NONE;\n\n    if ((c & 0xf0) == 0x40) {\n        hs->flags |= F_PREFIX_REX;\n        if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)\n            op64++;\n        hs->rex_r = (c & 7) >> 2;\n        hs->rex_x = (c & 3) >> 1;\n        hs->rex_b = c & 1;\n        if (((c = *p++) & 0xf0) == 0x40) {\n            opcode = c;\n            goto error_opcode;\n        }\n    }\n\n    if ((hs->opcode = c) == 0x0f) {\n        hs->opcode2 = c = *p++;\n        ht += DELTA_OPCODES;\n    } else if (c >= 0xa0 && c <= 0xa3) {\n        op64++;\n        if (pref & PRE_67)\n            pref |= PRE_66;\n        else\n            pref &= ~PRE_66;\n    }\n\n    opcode = c;\n    cflags = ht[ht[opcode / 4] + (opcode % 4)];\n\n    if (cflags == C_ERROR) {\n      error_opcode:\n        hs->flags |= F_ERROR | F_ERROR_OPCODE;\n        cflags = 0;\n        if ((opcode & -3) == 0x24)\n            cflags++;\n    }\n\n    x = 0;\n    if (cflags & C_GROUP) {\n        uint16_t t;\n        t = *(uint16_t *)(ht + (cflags & 0x7f));\n        cflags = (uint8_t)t;\n        x = (uint8_t)(t >> 8);\n    }\n\n    if (hs->opcode2) {\n        ht = hde64_table + DELTA_PREFIXES;\n        if (ht[ht[opcode / 4] + (opcode % 4)] & pref)\n            hs->flags |= F_ERROR | F_ERROR_OPCODE;\n    }\n\n    if (cflags & C_MODRM) {\n        hs->flags |= F_MODRM;\n        hs->modrm = c = *p++;\n        hs->modrm_mod = m_mod = c >> 6;\n        hs->modrm_rm = m_rm = c & 7;\n        hs->modrm_reg = m_reg = (c & 0x3f) >> 3;\n\n        if (x && ((x << m_reg) & 0x80))\n            hs->flags |= F_ERROR | F_ERROR_OPCODE;\n\n        if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {\n            uint8_t t = opcode - 0xd9;\n            if (m_mod == 3) {\n                ht = hde64_table + DELTA_FPU_MODRM + t*8;\n                t = ht[m_reg] << m_rm;\n            } else {\n                ht = hde64_table + DELTA_FPU_REG;\n                t = ht[t] << m_reg;\n            }\n            if (t & 0x80)\n                hs->flags |= F_ERROR | F_ERROR_OPCODE;\n        }\n\n        if (pref & PRE_LOCK) {\n            if (m_mod == 3) {\n                hs->flags |= F_ERROR | F_ERROR_LOCK;\n            } else {\n                uint8_t *table_end, op = opcode;\n                if (hs->opcode2) {\n                    ht = hde64_table + DELTA_OP2_LOCK_OK;\n                    table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;\n                } else {\n                    ht = hde64_table + DELTA_OP_LOCK_OK;\n                    table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;\n                    op &= -2;\n                }\n                for (; ht != table_end; ht++)\n                    if (*ht++ == op) {\n                        if (!((*ht << m_reg) & 0x80))\n                            goto no_lock_error;\n                        else\n                            break;\n                    }\n                hs->flags |= F_ERROR | F_ERROR_LOCK;\n              no_lock_error:\n                ;\n            }\n        }\n\n        if (hs->opcode2) {\n            switch (opcode) {\n                case 0x20: case 0x22:\n                    m_mod = 3;\n                    if (m_reg > 4 || m_reg == 1)\n                        goto error_operand;\n                    else\n                        goto no_error_operand;\n                case 0x21: case 0x23:\n                    m_mod = 3;\n                    if (m_reg == 4 || m_reg == 5)\n                        goto error_operand;\n                    else\n                        goto no_error_operand;\n            }\n        } else {\n            switch (opcode) {\n                case 0x8c:\n                    if (m_reg > 5)\n                        goto error_operand;\n                    else\n                        goto no_error_operand;\n                case 0x8e:\n                    if (m_reg == 1 || m_reg > 5)\n                        goto error_operand;\n                    else\n                        goto no_error_operand;\n            }\n        }\n\n        if (m_mod == 3) {\n            uint8_t *table_end;\n            if (hs->opcode2) {\n                ht = hde64_table + DELTA_OP2_ONLY_MEM;\n                table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;\n            } else {\n                ht = hde64_table + DELTA_OP_ONLY_MEM;\n                table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;\n            }\n            for (; ht != table_end; ht += 2)\n                if (*ht++ == opcode) {\n                    if (*ht++ & pref && !((*ht << m_reg) & 0x80))\n                        goto error_operand;\n                    else\n                        break;\n                }\n            goto no_error_operand;\n        } else if (hs->opcode2) {\n            switch (opcode) {\n                case 0x50: case 0xd7: case 0xf7:\n                    if (pref & (PRE_NONE | PRE_66))\n                        goto error_operand;\n                    break;\n                case 0xd6:\n                    if (pref & (PRE_F2 | PRE_F3))\n                        goto error_operand;\n                    break;\n                case 0xc5:\n                    goto error_operand;\n            }\n            goto no_error_operand;\n        } else\n            goto no_error_operand;\n\n      error_operand:\n        hs->flags |= F_ERROR | F_ERROR_OPERAND;\n      no_error_operand:\n\n        c = *p++;\n        if (m_reg <= 1) {\n            if (opcode == 0xf6)\n                cflags |= C_IMM8;\n            else if (opcode == 0xf7)\n                cflags |= C_IMM_P66;\n        }\n\n        switch (m_mod) {\n            case 0:\n                if (pref & PRE_67) {\n                    if (m_rm == 6)\n                        disp_size = 2;\n                } else\n                    if (m_rm == 5)\n                        disp_size = 4;\n                break;\n            case 1:\n                disp_size = 1;\n                break;\n            case 2:\n                disp_size = 2;\n                if (!(pref & PRE_67))\n                    disp_size <<= 1;\n        }\n\n        if (m_mod != 3 && m_rm == 4) {\n            hs->flags |= F_SIB;\n            p++;\n            hs->sib = c;\n            hs->sib_scale = c >> 6;\n            hs->sib_index = (c & 0x3f) >> 3;\n            if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))\n                disp_size = 4;\n        }\n\n        p--;\n        switch (disp_size) {\n            case 1:\n                hs->flags |= F_DISP8;\n                hs->disp.disp8 = *p;\n                break;\n            case 2:\n                hs->flags |= F_DISP16;\n                hs->disp.disp16 = *(uint16_t *)p;\n                break;\n            case 4:\n                hs->flags |= F_DISP32;\n                hs->disp.disp32 = *(uint32_t *)p;\n        }\n        p += disp_size;\n    } else if (pref & PRE_LOCK)\n        hs->flags |= F_ERROR | F_ERROR_LOCK;\n\n    if (cflags & C_IMM_P66) {\n        if (cflags & C_REL32) {\n            if (pref & PRE_66) {\n                hs->flags |= F_IMM16 | F_RELATIVE;\n                hs->imm.imm16 = *(uint16_t *)p;\n                p += 2;\n                goto disasm_done;\n            }\n            goto rel32_ok;\n        }\n        if (op64) {\n            hs->flags |= F_IMM64;\n            hs->imm.imm64 = *(uint64_t *)p;\n            p += 8;\n        } else if (!(pref & PRE_66)) {\n            hs->flags |= F_IMM32;\n            hs->imm.imm32 = *(uint32_t *)p;\n            p += 4;\n        } else\n            goto imm16_ok;\n    }\n\n\n    if (cflags & C_IMM16) {\n      imm16_ok:\n        hs->flags |= F_IMM16;\n        hs->imm.imm16 = *(uint16_t *)p;\n        p += 2;\n    }\n    if (cflags & C_IMM8) {\n        hs->flags |= F_IMM8;\n        hs->imm.imm8 = *p++;\n    }\n\n    if (cflags & C_REL32) {\n      rel32_ok:\n        hs->flags |= F_IMM32 | F_RELATIVE;\n        hs->imm.imm32 = *(uint32_t *)p;\n        p += 4;\n    } else if (cflags & C_REL8) {\n        hs->flags |= F_IMM8 | F_RELATIVE;\n        hs->imm.imm8 = *p++;\n    }\n\n  disasm_done:\n\n    if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {\n        hs->flags |= F_ERROR | F_ERROR_LENGTH;\n        hs->len = 15;\n    }\n\n    return (unsigned int)hs->len;\n}\n\n\n\n#endif /* X86_64_CPP_ */\n"
  },
  {
    "path": "lib/src/main/jni/MSHook/x86_64.h",
    "content": "/*\n * x86_64.h\n *\n *  Created on: 2016222\n *      Author: peng\n */\n\n#ifndef X86_64_H_\n#define X86_64_H_\n\n/*\n * Hacker Disassembler Engine 64 C\n * Copyright (c) 2008-2009, Vyacheslav Patkov.\n * All rights reserved.\n *\n */\n#define C_NONE    0x00\n#define C_MODRM   0x01\n#define C_IMM8    0x02\n#define C_IMM16   0x04\n#define C_IMM_P66 0x10\n#define C_REL8    0x20\n#define C_REL32   0x40\n#define C_GROUP   0x80\n#define C_ERROR   0xff\n\n#define PRE_ANY  0x00\n#define PRE_NONE 0x01\n#define PRE_F2   0x02\n#define PRE_F3   0x04\n#define PRE_66   0x08\n#define PRE_67   0x10\n#define PRE_LOCK 0x20\n#define PRE_SEG  0x40\n#define PRE_ALL  0xff\n\n#define DELTA_OPCODES      0x4a\n#define DELTA_FPU_REG      0xfd\n#define DELTA_FPU_MODRM    0x104\n#define DELTA_PREFIXES     0x13c\n#define DELTA_OP_LOCK_OK   0x1ae\n#define DELTA_OP2_LOCK_OK  0x1c6\n#define DELTA_OP_ONLY_MEM  0x1d8\n#define DELTA_OP2_ONLY_MEM 0x1e7\n\n/* stdint.h - C99 standard header\n * http://en.wikipedia.org/wiki/stdint.h\n *\n * if your compiler doesn't contain \"stdint.h\" header (for\n * example, Microsoft Visual C++), you can download file:\n *   http://www.azillionmonkeys.com/qed/pstdint.h\n * and change next line to:\n *   #include \"pstdint.h\"\n */\n#include <stdint.h>\n\n#define F_MODRM         0x00000001\n#define F_SIB           0x00000002\n#define F_IMM8          0x00000004\n#define F_IMM16         0x00000008\n#define F_IMM32         0x00000010\n#define F_IMM64         0x00000020\n#define F_DISP8         0x00000040\n#define F_DISP16        0x00000080\n#define F_DISP32        0x00000100\n#define F_RELATIVE      0x00000200\n#define F_ERROR         0x00001000\n#define F_ERROR_OPCODE  0x00002000\n#define F_ERROR_LENGTH  0x00004000\n#define F_ERROR_LOCK    0x00008000\n#define F_ERROR_OPERAND 0x00010000\n#define F_PREFIX_REPNZ  0x01000000\n#define F_PREFIX_REPX   0x02000000\n#define F_PREFIX_REP    0x03000000\n#define F_PREFIX_66     0x04000000\n#define F_PREFIX_67     0x08000000\n#define F_PREFIX_LOCK   0x10000000\n#define F_PREFIX_SEG    0x20000000\n#define F_PREFIX_REX    0x40000000\n#define F_PREFIX_ANY    0x7f000000\n\n#define PREFIX_SEGMENT_CS   0x2e\n#define PREFIX_SEGMENT_SS   0x36\n#define PREFIX_SEGMENT_DS   0x3e\n#define PREFIX_SEGMENT_ES   0x26\n#define PREFIX_SEGMENT_FS   0x64\n#define PREFIX_SEGMENT_GS   0x65\n#define PREFIX_LOCK         0xf0\n#define PREFIX_REPNZ        0xf2\n#define PREFIX_REPX         0xf3\n#define PREFIX_OPERAND_SIZE 0x66\n#define PREFIX_ADDRESS_SIZE 0x67\n\n#pragma pack(push,1)\n\ntypedef struct {\n    uint8_t len;\n    uint8_t p_rep;\n    uint8_t p_lock;\n    uint8_t p_seg;\n    uint8_t p_66;\n    uint8_t p_67;\n    uint8_t rex;\n    uint8_t rex_w;\n    uint8_t rex_r;\n    uint8_t rex_x;\n    uint8_t rex_b;\n    uint8_t opcode;\n    uint8_t opcode2;\n    uint8_t modrm;\n    uint8_t modrm_mod;\n    uint8_t modrm_reg;\n    uint8_t modrm_rm;\n    uint8_t sib;\n    uint8_t sib_scale;\n    uint8_t sib_index;\n    uint8_t sib_base;\n    union {\n        uint8_t imm8;\n        uint16_t imm16;\n        uint32_t imm32;\n        uint64_t imm64;\n    } imm;\n    union {\n        uint8_t disp8;\n        uint16_t disp16;\n        uint32_t disp32;\n    } disp;\n    uint32_t flags;\n} hde64s;\n\n#pragma pack(pop)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* __cdecl */\nunsigned int hde64_disasm(const void *code, hde64s *hs);\n\n#ifdef __cplusplus\n}\n#endif\n\n\n#endif /* X86_64_H_ */\n"
  },
  {
    "path": "lib/src/main/res/layout/app_not_authorized.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/*\n**\n** Copyright 2013, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <!-- Customizable description text -->\n    <TextView android:id=\"@+id/description\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\"\n        android:layout_gravity=\"start|center_vertical\"\n        android:paddingTop=\"16dip\"\n        android:paddingBottom=\"16dip\"\n        android:paddingStart=\"16dip\"\n        android:paddingEnd=\"16dip\"\n        android:text=\"Change not allowed\"\n    />\n\n    <!-- Horizontal divider line -->\n    <View android:layout_height=\"1dip\"\n        android:layout_width=\"match_parent\"\n        android:background=\"?android:attr/dividerHorizontal\" />\n\n    <!-- Alert dialog style buttons along the bottom. -->\n    <LinearLayout android:id=\"@+id/button_bar\"\n        style=\"?android:attr/buttonBarStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:measureWithLargestChild=\"true\">\n        <Button android:id=\"@android:id/button1\"\n            style=\"?android:attr/buttonBarButtonStyle\"\n            android:layout_width=\"0dp\" android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"@android:string/yes\"\n            android:onClick=\"onCancelButtonClicked\" />\n    </LinearLayout>\n</LinearLayout>\n"
  },
  {
    "path": "lib/src/main/res/layout/choose_account_row.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"16dp\"\n    android:paddingStart=\"16dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingEnd=\"16dp\"\n    android:orientation=\"horizontal\" >\n\n   <ImageView android:id=\"@+id/account_row_icon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"fill_parent\"\n        android:paddingRight=\"8dip\"\n        android:paddingEnd=\"8dip\" />\n\n    <TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/account_row_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"?android:attr/textAppearanceListItem\"\n        android:gravity=\"center_vertical\"\n        android:minHeight=\"?android:listPreferredItemHeight\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "lib/src/main/res/layout/choose_account_type.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <View android:layout_height=\"3dip\"\n          android:layout_width=\"match_parent\"\n          android:background=\"#323232\"/>\n\n    <ListView android:id=\"@android:id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dip\"\n        android:layout_weight=\"1\"\n        android:drawSelectorOnTop=\"false\"\n        android:scrollbarAlwaysDrawVerticalTrack=\"true\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "lib/src/main/res/layout/choose_type_and_account.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <!-- Customizable description text -->\n    <TextView\n        android:id=\"@+id/description\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"start|center_vertical\"\n        android:paddingBottom=\"16dip\"\n        android:paddingEnd=\"16dip\"\n        android:paddingStart=\"16dip\"\n        android:paddingTop=\"16dip\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <!-- List of accounts, with \"Add new account\" as the last item -->\n    <ListView\n        android:id=\"@android:id/list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:choiceMode=\"singleChoice\"\n        android:drawSelectorOnTop=\"false\"\n        android:scrollbarAlwaysDrawVerticalTrack=\"true\" />\n\n    <!-- Horizontal divider line -->\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dip\"\n        android:background=\"?android:attr/dividerHorizontal\" />\n\n    <!-- Alert dialog style buttons along the bottom. -->\n    <LinearLayout\n        android:id=\"@+id/button_bar\"\n        style=\"?android:attr/buttonBarStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:measureWithLargestChild=\"true\">\n\n        <Button\n            android:id=\"@android:id/button1\"\n            style=\"?android:attr/buttonBarButtonStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:onClick=\"onCancelButtonClicked\"\n            android:text=\"@android:string/no\" />\n\n        <Button\n            android:id=\"@android:id/button2\"\n            style=\"?android:attr/buttonBarButtonStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:onClick=\"onOkButtonClicked\"\n            android:text=\"@android:string/yes\" />\n    </LinearLayout>\n</LinearLayout>\n"
  },
  {
    "path": "lib/src/main/res/layout/custom_notification.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/im_main\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scaleType=\"fitXY\"/>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n        <LinearLayout\n            style=\"@style/notification_layout\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/btn_1\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_2\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_3\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_4\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_5\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_6\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_7\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_8\"\n                style=\"@style/notification_button\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            style=\"@style/notification_layout\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/btn_9\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_10\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_11\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_12\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_13\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_14\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_15\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_16\"\n                style=\"@style/notification_button\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            style=\"@style/notification_layout\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/btn_17\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_18\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_19\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_20\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_21\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_22\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_23\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_24\"\n                style=\"@style/notification_button\"/>\n\n        </LinearLayout>\n\n        <LinearLayout\n            style=\"@style/notification_layout\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:id=\"@+id/btn_25\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_26\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_27\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_28\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_29\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_30\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_31\"\n                style=\"@style/notification_button\"/>\n\n            <ImageView\n                android:id=\"@+id/btn_32\"\n                style=\"@style/notification_button\"/>\n\n        </LinearLayout>\n    </LinearLayout>\n</FrameLayout>"
  },
  {
    "path": "lib/src/main/res/layout/custom_notification_lite.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\" >\n\n    <ImageView\n        android:id=\"@+id/im_main\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scaleType=\"fitXY\" />\n</FrameLayout>"
  },
  {
    "path": "lib/src/main/res/layout/resolve_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/* //device/apps/common/res/any/layout/resolve_list_item.xml\n**\n** Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n-->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"wrap_content\"\n              android:orientation=\"horizontal\"\n              android:paddingBottom=\"4dp\"\n              android:paddingTop=\"4dp\">\n\n    <!-- Activity icon when presenting dialog\n         Size will be filled in by ResolverActivity -->\n    <ImageView\n        android:id=\"@+id/icon\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_gravity=\"start|center_vertical\"\n        android:layout_marginBottom=\"12dp\"\n        android:layout_marginTop=\"12dp\"\n        android:scaleType=\"fitCenter\"/>\n\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                  android:layout_width=\"wrap_content\"\n                  android:layout_height=\"wrap_content\"\n                  android:layout_marginLeft=\"8dp\"\n                  android:layout_gravity=\"start|center_vertical\"\n                  android:gravity=\"start|center_vertical\"\n                  android:orientation=\"vertical\">\n        <!-- Activity name -->\n        <TextView\n            android:id=\"@+id/text1\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:maxLines=\"1\"\n            android:minLines=\"1\"/>\n        <!-- Extended activity info to distinguish between duplicate activity names -->\n        <TextView\n            android:id=\"@+id/text2\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"marquee\"\n            android:maxLines=\"1\"\n            android:minLines=\"1\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"/>\n    </LinearLayout>\n</LinearLayout>\n\n"
  },
  {
    "path": "lib/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- notification -->\n    <dimen name=\"match_parent\">-1px</dimen>\n    <dimen name=\"standard_notification_panel_width\">416dp\n    </dimen><!-- includes notification_side_padding on each side -->\n    <!-- Height of a small notification in the status bar -->\n    <dimen name=\"notification_min_height\">64dp</dimen>\n    <dimen name=\"notification_side_padding\">8dp</dimen>\n    <dimen name=\"notification_panel_width\">-1dp</dimen>\n    <!-- Height of a large notification in the status bar -->\n    <dimen name=\"notification_max_height\">256dp</dimen>\n    <dimen name=\"notification_padding\">4dp</dimen>\n    <!-- Height of a medium notification in the status bar -->\n    <dimen name=\"notification_mid_height\">128dp</dimen>\n</resources>\n"
  },
  {
    "path": "lib/src/main/res/values/integer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"config_maxResolverActivityColumns\">8</integer>\n</resources>"
  },
  {
    "path": "lib/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"engine_process_name\">:x</string>\n    <string name=\"virtual_installer\">VirtualPackage Installer</string>\n    <string name=\"owner_name\">Admin</string>\n    <string name=\"choose\">Choose</string>\n    <string name=\"choose_empty\">Chooser is Empty</string>\n    <string name=\"noApplications\">No find applications</string>\n    <string name=\"add_account_button_label\">Add account</string>\n</resources>\n"
  },
  {
    "path": "lib/src/main/res/values/styles.xml",
    "content": "<resources>\n    <style name=\"notification_layout\">\n        <item name=\"android:layout_width\">match_parent</item>\n        <item name=\"android:layout_height\">0dp</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n    <style name=\"notification_button\">\n        <item name=\"android:layout_width\">0dp</item>\n        <item name=\"android:layout_height\">match_parent</item>\n        <item name=\"android:layout_weight\">1</item>\n    </style>\n\n    <style name=\"VATheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowDisablePreview\">true</item>\n    </style>\n    <style name=\"VAAlertTheme\" parent=\"android:Theme.DeviceDefault.Dialog\">\n    </style>\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':lib', ':app'\n"
  }
]